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

// "  crate-name v1.2.3 (license)" allowed lines — strip by default
static ALLOWED_LICENSE_RE: Lazy<Regex> =
    Lazy::new(|| Regex::new(r"(?m)^\s+\S+ v\d+\.\d+[\.\d]* \([A-Za-z0-9\-\. ]+\)\s*$").unwrap());
// "Gathered 123 crates" / "Checked 45 bans" progress lines
static PROGRESS_RE: Lazy<Regex> =
    Lazy::new(|| Regex::new(r"(?m)^(?:Gathered|Checked|Fetched)[^\n]*\n?").unwrap());

// ── cargo deny check (all or specific) ───────────────────────────────────────

pub fn compress_deny(raw: &str) -> String {
    let cleaned = compactor::normalise(raw);
    let s = PROGRESS_RE.replace_all(&cleaned, "");
    let s = ALLOWED_LICENSE_RE.replace_all(&s, "");

    let mut violations: Vec<&str> = Vec::new();
    let mut summary_lines: Vec<&str> = Vec::new();
    let mut advisory_lines: Vec<&str> = Vec::new();

    for line in s.lines() {
        let t = line.trim();
        if t.is_empty() {
            continue;
        }
        // Summary / error / warning lines
        if t.starts_with("error[")
            || t.starts_with("warning[")
            || t.starts_with("  = ")
            || t.starts_with("note:")
            || t.contains("RUSTSEC-")
            || t.contains("advisory")
            || t.contains("license")
                && (t.contains("denied") || t.contains("not explicitly allowed"))
        {
            violations.push(line);
        } else if t.contains("advisories") || t.contains("licenses") || t.contains("bans") {
            summary_lines.push(line);
        } else if t.starts_with("RUSTSEC") || t.contains("RUSTSEC-") {
            advisory_lines.push(line);
        }
    }

    if violations.is_empty() && summary_lines.is_empty() && advisory_lines.is_empty() {
        // All checks passed — show a compact message
        let pass_lines: Vec<&str> = s
            .lines()
            .filter(|l| {
                let t = l.trim();
                !t.is_empty()
                    && (t.contains("advisories ok")
                        || t.contains("licenses ok")
                        || t.contains("bans ok")
                        || t.contains("sources ok")
                        || t.contains("0 errors")
                        || t.starts_with("Finished"))
            })
            .collect();
        if pass_lines.is_empty() {
            return "cargo-deny: all checks passed".to_string();
        }
        return pass_lines.join("\n");
    }

    let mut out = Vec::new();
    out.extend(violations);
    out.extend(advisory_lines);
    out.extend(summary_lines);
    out.join("\n")
}

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

    #[test]
    fn strips_allowed_crate_lines() {
        let raw = "  serde v1.0.195 (MIT OR Apache-2.0)\n  tokio v1.35.1 (MIT)\nerror[A001]: crate 'old-crate' is banned\n  = use `new-crate` instead\n";
        let out = compress_deny(raw);
        assert!(!out.contains("serde v1"), "{out}");
        assert!(!out.contains("tokio v1"), "{out}");
        assert!(out.contains("old-crate"), "{out}");
        assert!(out.contains("banned"), "{out}");
    }

    #[test]
    fn keeps_rustsec_advisories() {
        let raw = "error[A001]: Unmaintained advisory for `ansi_term`\n  = RUSTSEC-2021-0139: ansi_term is Unmaintained\n  = Advisory: https://rustsec.org/advisories/RUSTSEC-2021-0139\n";
        let out = compress_deny(raw);
        assert!(out.contains("RUSTSEC-2021-0139"), "{out}");
        assert!(out.contains("Unmaintained"), "{out}");
    }

    #[test]
    fn all_pass_returns_compact_message() {
        let raw = "Gathered 142 crates\n  tokio v1.35.1 (MIT)\n  serde v1.0.195 (MIT OR Apache-2.0)\nadvisories ok\nlicenses ok\nbans ok\nsources ok\n";
        let out = compress_deny(raw);
        // Should not list all crates, should show pass status
        assert!(!out.contains("tokio v1"), "{out}");
        assert!(out.contains("ok") || out.contains("passed"), "{out}");
    }

    #[test]
    fn keeps_license_violations() {
        let raw = "error[L001]: crate 'gpl-crate' is licensed under GPL-3.0 which is not allowed\n  note: Use an MIT-licensed alternative\n";
        let out = compress_deny(raw);
        assert!(out.contains("gpl-crate"), "{out}");
        assert!(out.contains("GPL-3.0"), "{out}");
    }
}
