use std::path::PathBuf;
use std::sync::mpsc;
use std::time::Duration;

use notify::{Config, Event, EventKind, RecommendedWatcher, RecursiveMode, Watcher};

/// Events sent from the file watcher to the UI thread.
#[derive(Debug, Clone)]
pub enum WatchEvent {
    /// One or more AppImage files were created, modified, or deleted.
    Changed(Vec<PathBuf>),
}

/// Start watching the given directories for AppImage file changes.
/// Returns the watcher handle (must be kept alive).
/// The callback `on_event` is invoked on the background debounce thread.
pub fn start_watcher<F: Fn(WatchEvent) + Send + 'static>(
    dirs: Vec<PathBuf>,
    on_event: F,
) -> Option<RecommendedWatcher> {
    let (notify_tx, notify_rx) = mpsc::channel::<Result<Event, notify::Error>>();

    let mut watcher = RecommendedWatcher::new(
        move |res| {
            notify_tx.send(res).ok();
        },
        Config::default().with_poll_interval(Duration::from_secs(2)),
    ).ok()?;

    for dir in &dirs {
        if dir.is_dir() {
            watcher.watch(dir, RecursiveMode::NonRecursive).ok();
        }
    }

    // Spawn a thread to debounce and forward events
    std::thread::spawn(move || {
        let mut pending: Vec<PathBuf> = Vec::new();
        let debounce = Duration::from_millis(500);

        loop {
            match notify_rx.recv_timeout(debounce) {
                Ok(Ok(event)) => {
                    if is_appimage_event(&event) {
                        for path in event.paths {
                            if !pending.contains(&path) {
                                pending.push(path);
                            }
                        }
                    }
                }
                Ok(Err(_)) => {}
                Err(mpsc::RecvTimeoutError::Timeout) => {
                    if !pending.is_empty() {
                        let paths = std::mem::take(&mut pending);
                        on_event(WatchEvent::Changed(paths));
                    }
                }
                Err(mpsc::RecvTimeoutError::Disconnected) => break,
            }
        }
    });

    Some(watcher)
}

fn is_appimage_event(event: &Event) -> bool {
    match event.kind {
        EventKind::Create(_) | EventKind::Remove(_) | EventKind::Modify(_) => {
            event.paths.iter().any(|p| {
                p.extension()
                    .and_then(|e| e.to_str())
                    .map(|e| e.eq_ignore_ascii_case("appimage"))
                    .unwrap_or(false)
            })
        }
        _ => false,
    }
}
