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

static COMPILE_RE: Lazy<Regex> = Lazy::new(||
    // "Compiling lib/..." verbose dart AOT compile lines
    Regex::new(r"(?m)^(Compiling lib/|Running Gradle|Running pod install)[^\n]+\n?").unwrap());
static PUB_FETCH_RE: Lazy<Regex> = Lazy::new(|| {
    Regex::new(r"(?m)^(Downloading|Fetching|Resolving|Precompiling) [^\n]+\.\.\.\n?").unwrap()
});
static TOOL_DOWNLOAD_RE: Lazy<Regex> =
    Lazy::new(|| Regex::new(r"(?m)^(Downloading|Installing) Flutter (tool|SDK)[^\n]*\n?").unwrap());

// ── flutter pub get / pub upgrade ─────────────────────────────────────────────

pub fn compress_pub(raw: &str) -> String {
    let cleaned = compactor::normalise(raw);
    let s = PUB_FETCH_RE.replace_all(&cleaned, "");
    // Keep "Got dependencies!", "Changed X dependencies", errors
    let kept: Vec<&str> = s
        .lines()
        .filter(|l| {
            let t = l.trim();
            t.is_empty()
                || t.starts_with("Got dependencies")
                || t.contains("Changed")
                || t.contains("error:")
                || t.starts_with("Because")
                || t.starts_with("Note:")
                || t.starts_with("Dart SDK")
        })
        .collect();
    if kept.is_empty() {
        return compactor::collapse_blanks(&s);
    }
    kept.join("\n")
}

// ── flutter build ─────────────────────────────────────────────────────────────

pub fn compress_build(raw: &str, exit_code: i32) -> String {
    let cleaned = compactor::normalise(raw);
    let s = COMPILE_RE.replace_all(&cleaned, "");
    let s = TOOL_DOWNLOAD_RE.replace_all(&s, "");
    if exit_code == 0 {
        // Keep "✓ Built build/..." and timing lines
        let kept: Vec<&str> = s
            .lines()
            .filter(|l| {
                let t = l.trim();
                t.is_empty()
                    || t.starts_with("✓")
                    || t.starts_with("Building")
                    || t.contains("Built ")
                    || t.contains("seconds")
                    || t.contains("error")
                    || t.contains("warning")
            })
            .collect();
        if !kept.is_empty() {
            return kept.join("\n");
        }
    }
    // On failure: keep error lines
    s.lines()
        .filter(|l| l.contains("error") || l.contains("Error") || !l.trim().is_empty())
        .collect::<Vec<_>>()
        .join("\n")
}

// ── flutter test ──────────────────────────────────────────────────────────────

pub fn compress_test(raw: &str, exit_code: i32) -> String {
    let cleaned = compactor::normalise(raw);
    if exit_code == 0 {
        let kept: Vec<&str> = cleaned
            .lines()
            .filter(|l| {
                l.contains("All tests passed")
                    || l.contains("passed")
                    || l.contains("+") && l.contains(":") // "+N: ..." summary lines
            })
            .collect();
        if !kept.is_empty() {
            return kept.join("\n");
        }
    }
    // Failure: strip passing lines
    cleaned
        .lines()
        .filter(|l| {
            let t = l.trim();
            !t.starts_with('+') || t.contains("FAILED") || t.contains("ERROR")
        })
        .collect::<Vec<_>>()
        .join("\n")
}

pub fn compress_flutter(subcmd: &str, raw: &str, exit_code: i32) -> String {
    let sub = subcmd.trim();
    match sub {
        "pub" => compress_pub(raw),
        "build" | "run" | "install" => compress_build(raw, exit_code),
        "test" => compress_test(raw, exit_code),
        "analyze" => {
            // flutter analyze: keep findings only
            let cleaned = compactor::normalise(raw);
            cleaned
                .lines()
                .filter(|l| {
                    l.contains("error")
                        || l.contains("warning")
                        || l.contains("info")
                        || l.contains("issues found")
                })
                .collect::<Vec<_>>()
                .join("\n")
        }
        _ => compactor::normalise(raw),
    }
}

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

    #[test]
    fn pub_strips_fetch_lines() {
        let raw = "Resolving dependencies...\nFetching foo 1.0.0...\nGot dependencies!\n";
        let out = compress_pub(raw);
        assert!(!out.contains("Fetching foo"), "{out}");
        assert!(out.contains("Got dependencies"), "{out}");
    }

    #[test]
    fn build_success_keeps_built_line() {
        let raw = "Compiling lib/main.dart\n✓ Built build/app/outputs/flutter-apk/app-release.apk (12.4MB)\n";
        let out = compress_build(raw, 0);
        assert!(out.contains("Built build/"), "{out}");
        assert!(!out.contains("Compiling lib/"), "{out}");
    }
}
