use forge::signal::compactor;
use once_cell::sync::Lazy;
use regex::Regex;

static INSTALL_PROGRESS_RE: Lazy<Regex> = Lazy::new(||
    // "[1/5] Resolving...", "[2/5] Fetching..." style lines
    Regex::new(r"(?m)^\[[\d/]+\] (Resolving|Fetching|Linking|Building)[^\n]*\n?").unwrap());
static WARN_RE: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?m)^warn: [^\n]+\n?").unwrap());

pub fn compress_install(raw: &str) -> String {
    let cleaned = compactor::normalise(raw);
    let s = INSTALL_PROGRESS_RE.replace_all(&cleaned, "");
    let s = WARN_RE.replace_all(&s, "");
    // Keep: summary line "X packages installed", errors, timing
    let kept: Vec<&str> = s
        .lines()
        .filter(|l| {
            let t = l.trim();
            t.is_empty()
                || t.contains("packages installed")
                || t.contains("Done")
                || t.contains("error")
                || t.starts_with("error")
                || t.contains("blocked")
        })
        .collect();
    let result = compactor::collapse_blanks(&kept.join("\n"));
    if result.trim().is_empty() {
        compactor::collapse_blanks(&s)
    } else {
        result
    }
}

pub fn compress_test(raw: &str, exit_code: i32) -> String {
    let cleaned = compactor::normalise(raw);
    if exit_code == 0 {
        // Keep "X pass", "X fail", timing summary
        let kept: Vec<&str> = cleaned
            .lines()
            .filter(|l| {
                l.contains(" pass")
                    || l.contains(" fail")
                    || l.contains("Tests:")
                    || l.contains("Duration:")
            })
            .collect();
        if !kept.is_empty() {
            return kept.join("\n");
        }
    }
    // On failure: strip passing test lines (✓), keep failing (✗) + errors
    cleaned
        .lines()
        .filter(|l| !l.trim_start().starts_with('✓'))
        .collect::<Vec<_>>()
        .join("\n")
}

pub fn compress_bun(subcmd: &str, raw: &str, exit_code: i32) -> String {
    let sub = subcmd.trim();
    match sub {
        "install" | "add" | "remove" | "update" => compress_install(raw),
        "test" => compress_test(raw, exit_code),
        "run" | "x" => compactor::normalise(raw),
        _ => compactor::normalise(raw),
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn install_strips_step_lines() {
        let raw = "[1/5] Resolving packages...\n[2/5] Fetching packages...\n[3/5] Linking packages...\n47 packages installed [1.23s]\n";
        let out = compress_install(raw);
        assert!(!out.contains("Resolving packages"), "{out}");
        assert!(out.contains("packages installed"), "{out}");
    }

    #[test]
    fn test_success_keeps_summary() {
        let raw = " ✓ foo test (12ms)\n ✓ bar test (8ms)\n Tests: 2 pass\n Duration: 0.02s\n";
        let out = compress_test(raw, 0);
        assert!(out.contains("pass"), "{out}");
        assert!(!out.contains("✓ foo"), "{out}");
    }

    #[test]
    fn test_failure_keeps_failing() {
        let raw =
            " ✓ foo test\n ✗ bar test\n  AssertionError: expected 1 to be 2\n Tests: 1 fail\n";
        let out = compress_test(raw, 1);
        assert!(out.contains("✗ bar"), "{out}");
        assert!(!out.contains("✓ foo"), "{out}");
    }
}
