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

// "INFO: Analyzed target //foo:bar (N packages loaded, M targets configured)"
static ANALYZED_RE: Lazy<Regex> =
    Lazy::new(|| Regex::new(r"(?m)^INFO: Analyzed target[^\n]+\n?").unwrap());
// "INFO: Found N targets..."
static FOUND_RE: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?m)^INFO: Found \d+[^\n]+\n?").unwrap());
// Build action progress lines: "[N / M] Compiling foo.cc"
static ACTION_RE: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?m)^\[\d+ / \d+\] [^\n]+\n?").unwrap());

pub fn compress_build(raw: &str, exit_code: i32) -> String {
    let cleaned = compactor::normalise(raw);
    let s = ANALYZED_RE.replace_all(&cleaned, "");
    let s = FOUND_RE.replace_all(&s, "");
    if exit_code == 0 {
        // Strip action lines, keep "Target //... up-to-date" + "Build completed"
        let s = ACTION_RE.replace_all(&s, "");
        return compactor::collapse_blanks(&s);
    }
    // On failure: strip action progress, keep errors
    let s = ACTION_RE.replace_all(&s, "");
    s.lines()
        .filter(|l| {
            let t = l.trim();
            !t.is_empty()
                && (t.contains("ERROR")
                    || t.contains("error")
                    || t.contains("FAILED")
                    || t.starts_with("Target")
                    || t.starts_with("Build did NOT complete"))
        })
        .collect::<Vec<_>>()
        .join("\n")
}

pub fn compress_test(raw: &str, exit_code: i32) -> String {
    let cleaned = compactor::normalise(raw);
    let s = ANALYZED_RE.replace_all(&cleaned, "");
    let s = ACTION_RE.replace_all(&s, "");
    if exit_code == 0 {
        // Keep "//target:name PASSED" + summary
        let kept: Vec<&str> = s
            .lines()
            .filter(|l| {
                l.contains("PASSED") || l.contains("FAILED") || l.contains("Build completed")
            })
            .collect();
        if !kept.is_empty() {
            return kept.join("\n");
        }
    }
    compactor::collapse_blanks(&s)
}

pub fn compress_bazel(subcmd: &str, raw: &str, exit_code: i32) -> String {
    match subcmd.trim() {
        "build" | "run" => compress_build(raw, exit_code),
        "test" => compress_test(raw, exit_code),
        "query" => {
            // bazel query can be very long
            let cleaned = compactor::normalise(raw);
            let lines: Vec<&str> = cleaned.lines().collect();
            if lines.len() > 50 {
                return format!(
                    "{}\n... [{} more targets] ...",
                    lines[..50].join("\n"),
                    lines.len() - 50
                );
            }
            cleaned
        }
        _ => compactor::normalise(raw),
    }
}

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

    #[test]
    fn build_success_strips_progress() {
        let raw = "INFO: Analyzed target //foo:bar (12 packages loaded)\n[1 / 42] Compiling src/foo.cc\n[42 / 42] Linking foo\nTarget //foo:bar up-to-date: bazel-bin/foo/bar\nINFO: Build completed successfully.\n";
        let out = compress_build(raw, 0);
        assert!(!out.contains("Analyzed target"), "{out}");
        assert!(!out.contains("[1 / 42]"), "{out}");
        assert!(out.contains("Build completed"), "{out}");
    }

    #[test]
    fn test_keeps_pass_fail() {
        let raw = "INFO: Analyzed target //test:mytest\n[1 / 2] Compiling test/mytest.cc\n//test:mytest PASSED in 0.3s\nINFO: Build completed successfully.\n";
        let out = compress_test(raw, 0);
        assert!(out.contains("PASSED"), "{out}");
    }
}
