use notify::{RecursiveMode, Watcher};
use std::collections::HashSet;
use std::path::{Path, PathBuf};
use std::sync::mpsc;
use std::sync::Mutex;
use std::time::Duration;
use tauri::{AppHandle, Emitter};

/// 当前项目的文件系统监听器；切换项目时替换（旧的随 drop 停止）
#[derive(Default)]
pub struct WatchState(Mutex<Option<notify::RecommendedWatcher>>);

/// 高频变动的大目录不触发刷新，避免 npm install / 编译期间刷屏
fn is_noise(path: &Path) -> bool {
    path.components().any(|c| {
        matches!(
            c.as_os_str().to_str(),
            Some("node_modules") | Some(".git") | Some("target") | Some("dist") | Some("out")
        )
    })
}

/// 监听项目根目录（递归）：事件去抖 500ms 后向前端发 "fs-changed"，
/// 同时把本轮变动路径集合交给代码索引做增量更新。
pub fn watch_project(app: AppHandle, state: &WatchState, project_path: String) -> Result<(), String> {
    let (tx, rx) = mpsc::channel::<PathBuf>();
    let mut watcher = notify::recommended_watcher(move |res: notify::Result<notify::Event>| {
        if let Ok(event) = res {
            for p in event.paths {
                if !is_noise(&p) {
                    let _ = tx.send(p);
                }
            }
        }
    })
    .map_err(|e| e.to_string())?;
    watcher
        .watch(Path::new(&project_path), RecursiveMode::Recursive)
        .map_err(|e| e.to_string())?;

    // 去抖线程：监听器被替换（channel 断开）后自动退出
    let root = project_path.clone();
    std::thread::spawn(move || {
        while let Ok(first) = rx.recv() {
            let mut changed: HashSet<PathBuf> = HashSet::new();
            changed.insert(first);
            // 吸收 500ms 内的连续事件，去重路径
            while let Ok(p) = rx.recv_timeout(Duration::from_millis(500)) {
                changed.insert(p);
            }
            let _ = app.emit("fs-changed", ());
            crate::index::on_files_changed(&app, &root, changed.into_iter().collect());
        }
    });

    *state.0.lock().unwrap() = Some(watcher);
    Ok(())
}
