use anyhow::{Context, Result};
use notify::{Event, EventKind, RecommendedWatcher, RecursiveMode, Watcher};
use std::path::PathBuf;
use tokio::sync::broadcast;

#[derive(Clone, Debug)]
pub struct FileEvent {
    /// Path relative to the watched root, with `/` separators.
    pub relative_path: String,
}

/// Start a recursive watcher on `root`.  Any create/modify/remove of a `.puml`
/// file is broadcast on `tx` as a `FileEvent`.
///
/// The returned `RecommendedWatcher` must be kept alive for watching to continue.
pub fn start_watcher(root: PathBuf, tx: broadcast::Sender<FileEvent>) -> Result<RecommendedWatcher> {
    let root_for_closure = root.clone();

    let mut watcher = notify::recommended_watcher(move |res: notify::Result<Event>| {
        let Ok(event) = res else { return };

        match event.kind {
            EventKind::Create(_) | EventKind::Modify(_) | EventKind::Remove(_) => {}
            _ => return,
        }

        for path in &event.paths {
            if path.extension().map(|e| e == "puml").unwrap_or(false) {
                let rel = path
                    .strip_prefix(&root_for_closure)
                    .map(|r| r.to_string_lossy().replace('\\', "/"))
                    .unwrap_or_else(|_| path.to_string_lossy().into_owned());

                let _ = tx.send(FileEvent { relative_path: rel });
            }
        }
    })
    .context("creating filesystem watcher")?;

    watcher
        .watch(&root, RecursiveMode::Recursive)
        .with_context(|| format!("watching {}", root.display()))?;

    Ok(watcher)
}
