mod cargo;
mod deno;
mod go;
mod nextjs;
mod nuxt;
mod remix;
mod sveltekit;
mod vite;

use std::path::Path;

use anyhow::Result;
use tracing::warn;

pub struct DetectResult {
    pub matches: bool,
    pub alert_msg: Option<String>,
}

impl DetectResult {
    pub fn matched() -> Self {
        Self { matches: true, alert_msg: None }
    }

    pub fn no_match() -> Self {
        Self { matches: false, alert_msg: None }
    }

    pub fn with_alert(mut self, msg: impl Into<String>) -> Self {
        self.alert_msg = Some(msg.into());
        self
    }
}

pub struct ProjectDef {
    pub name: &'static str,
    pub detect: fn(&Path) -> Result<DetectResult>,
    pub build_cmd: &'static [&'static str],
    pub run_cmd: &'static [&'static str],
}

static REGISTRY: &[ProjectDef] = &[
    sveltekit::DEF,
    nuxt::DEF,
    remix::DEF,
    nextjs::DEF,
    vite::DEF,
    deno::DEF,
    go::DEF,
    cargo::DEF,
];

pub fn detect(dir: &Path) -> Option<&'static ProjectDef> {
    for def in REGISTRY {
        match (def.detect)(dir) {
            Ok(result) => {
                if let Some(ref msg) = result.alert_msg {
                    warn!("{}: {}", def.name, msg);
                }
                if result.matches {
                    return Some(def);
                }
            }
            Err(e) => {
                warn!(detector = def.name, error = %e, "Detection failed");
            }
        }
    }
    None
}
