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

use anyhow::Result;
use notify::{RecursiveMode, Watcher};
use tokio::process::Command;
use tower_http::services::ServeDir;

use crate::cli::Site;
use super::build;

pub async fn run(site: Site) -> Result<()> {
    println!("Build initial...");
    if let Err(e) = build::run(site) {
        eprintln!("Erreur de build: {e}");
    }

    let watcher_handle = tokio::task::spawn_blocking(move || watch_files(site));

    let tailwind_handle = tokio::spawn(tailwind_watch(site));

    let serve_handle = tokio::spawn(serve(site));

    tokio::select! {
        result = watcher_handle => result?,
        result = tailwind_handle => result?,
        result = serve_handle => result?,
    }
}

fn watch_files(site: Site) -> Result<()> {
    let (tx, rx) = mpsc::channel();

    let mut watcher = notify::recommended_watcher(move |event: notify::Result<notify::Event>| {
        if let Ok(event) = event {
            if event.kind.is_modify() || event.kind.is_create() || event.kind.is_remove() {
                let _ = tx.send(());
            }
        }
    })?;

    watcher.watch(Path::new("content"), RecursiveMode::Recursive)?;

    println!("Watching content/ pour les changements...");

    loop {
        rx.recv()?;
        // Debounce : vider les events accumulés pendant 100ms
        std::thread::sleep(Duration::from_millis(100));
        while rx.try_recv().is_ok() {}

        // Ignorer les changements sur syntax.css (généré par le build)
        println!("Changement détecté, rebuild...");
        if let Err(e) = build::run(site) {
            eprintln!("Erreur de build: {e}");
        }
    }
}

async fn tailwind_watch(site: Site) -> Result<()> {
    let output_path = format!("{}/css/app.css", site.build_dir());

    let mut child = Command::new("npx")
        .args([
            "@tailwindcss/cli",
            "-i",
            "content/css/app.css",
            "-o",
            &output_path,
            "--watch",
        ])
        .spawn()?;

    child.wait().await?;

    Ok(())
}

async fn serve(site: Site) -> Result<()> {
    let service = ServeDir::new(site.build_dir());
    let app = axum::Router::new().fallback_service(service);

    let listener = tokio::net::TcpListener::bind("127.0.0.1:8000").await?;
    println!("Serving on http://localhost:8000");

    axum::serve(listener, app).await?;

    Ok(())
}
