use super::{AwarenessMap, EcosystemAwareness};
use once_cell::sync::Lazy;
use regex::Regex;

static RUST_RESULT_RE: Lazy<Regex> = Lazy::new(|| {
    Regex::new(r"test result: (\w+)\. (\d+) passed; (\d+) failed; (\d+) ignored").unwrap()
});
static RUST_FAIL_RE: Lazy<Regex> = Lazy::new(|| Regex::new(r"^FAILED\s+(.+)$").unwrap());
static PYTEST_RE: Lazy<Regex> =
    Lazy::new(|| Regex::new(r"(\d+) passed(?:, (\d+) failed)?(?:, (\d+) error)?").unwrap());

pub struct TestAwareness;

impl EcosystemAwareness for TestAwareness {
    fn matches(&self, program: &str, args: &[String]) -> bool {
        let prog = program.split('/').next_back().unwrap_or(program);
        let sub = args.first().map(|s| s.as_str()).unwrap_or("");
        matches!(prog, "pytest" | "jest" | "vitest" | "rspec" | "mocha")
            || (prog == "cargo" && sub == "test")
            || (prog == "go" && sub == "test")
            || (prog == "npm" && args.get(1).map(|s| s.as_str()) == Some("test"))
    }

    fn extract(&self, stdout: &str, stderr: &str, exit_code: i32) -> AwarenessMap {
        let mut map = AwarenessMap::new("test", "run");
        map.insert("exit_code", exit_code.to_string());

        let combined = format!("{stdout}\n{stderr}");

        if let Some(cap) = RUST_RESULT_RE.captures(&combined) {
            map.insert("status", cap[1].to_string());
            map.insert("passed", cap[2].to_string());
            map.insert("failed", cap[3].to_string());
            map.insert("ignored", cap[4].to_string());
        } else if let Some(cap) = PYTEST_RE.captures(&combined) {
            map.insert("passed", cap[1].to_string());
            if let Some(f) = cap.get(2) {
                map.insert("failed", f.as_str().to_string());
            }
        }

        let failures: Vec<&str> = RUST_FAIL_RE
            .captures_iter(&combined)
            .filter_map(|c| c.get(1).map(|m| m.as_str()))
            .collect();
        if !failures.is_empty() {
            map.insert("failed_tests", failures.join(", "));
        }

        map
    }
}
