use std::path::{Path, PathBuf};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::mpsc;
use std::sync::Arc;
use std::time::Duration;

use notify::{Event, EventKind, RecursiveMode, Watcher};
use serde::{Deserialize, Serialize};

use crate::error::{PixstripError, Result};

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WatchFolder {
    pub path: PathBuf,
    pub preset_name: String,
    pub recursive: bool,
    pub active: bool,
}

pub enum WatchEvent {
    NewImage(PathBuf),
    Error(String),
}

pub struct FolderWatcher {
    running: Arc<AtomicBool>,
}

impl FolderWatcher {
    pub fn new() -> Self {
        Self {
            running: Arc::new(AtomicBool::new(false)),
        }
    }

    pub fn start(
        &self,
        folder: &WatchFolder,
        event_tx: mpsc::Sender<WatchEvent>,
    ) -> Result<()> {
        if !folder.path.exists() {
            return Err(PixstripError::Config(format!(
                "Watch folder does not exist: {}",
                folder.path.display()
            )));
        }

        self.running.store(true, Ordering::Relaxed);
        let running = self.running.clone();
        let watch_path = folder.path.clone();
        let recursive = folder.recursive;

        std::thread::spawn(move || {
            let (tx, rx) = mpsc::channel();

            let mut watcher = match notify::recommended_watcher(move |res: std::result::Result<Event, notify::Error>| {
                if let Ok(event) = res {
                    let _ = tx.send(event);
                }
            }) {
                Ok(w) => w,
                Err(e) => {
                    let _ = event_tx.send(WatchEvent::Error(e.to_string()));
                    return;
                }
            };

            let mode = if recursive {
                RecursiveMode::Recursive
            } else {
                RecursiveMode::NonRecursive
            };

            if let Err(e) = watcher.watch(&watch_path, mode) {
                let _ = event_tx.send(WatchEvent::Error(e.to_string()));
                return;
            }

            while running.load(Ordering::Relaxed) {
                match rx.recv_timeout(Duration::from_millis(500)) {
                    Ok(event) => {
                        if matches!(event.kind, EventKind::Create(_)) {
                            for path in event.paths {
                                if is_image_file(&path) {
                                    let _ = event_tx.send(WatchEvent::NewImage(path));
                                }
                            }
                        }
                    }
                    Err(mpsc::RecvTimeoutError::Timeout) => continue,
                    Err(mpsc::RecvTimeoutError::Disconnected) => break,
                }
            }
        });

        Ok(())
    }

    pub fn stop(&self) {
        self.running.store(false, Ordering::Relaxed);
    }

    pub fn is_running(&self) -> bool {
        self.running.load(Ordering::Relaxed)
    }
}

impl Default for FolderWatcher {
    fn default() -> Self {
        Self::new()
    }
}

fn is_image_file(path: &Path) -> bool {
    path.extension()
        .and_then(|e| e.to_str())
        .is_some_and(crate::discovery::is_image_extension)
}
