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

// "Resolved N packages in Xms" — keep abbreviated
static RESOLVED_RE: Lazy<Regex> =
    Lazy::new(|| Regex::new(r"(?m)^Resolved (\d+) packages? in [^\n]+\n?").unwrap());
// Per-package download lines
static DOWNLOAD_RE: Lazy<Regex> =
    Lazy::new(|| Regex::new(r"(?m)^ +Downloading [^\n]+\n?").unwrap());
// Hash / integrity lines in lockfile output
static HASH_RE: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?m)^\s+hash = [^\n]+\n?").unwrap());

pub fn compress_uv(subcmd: &str, raw: &str) -> String {
    let sub = subcmd.trim();
    match sub {
        "pip" => compress_pip(raw),
        "sync" | "install" => compress_install(raw),
        "add" | "remove" => compress_add(raw),
        "lock" => compress_lock(raw),
        "run" | "tool" => compactor::normalise(raw), // passthrough — program output
        _ => compress_install(raw),
    }
}

fn compress_install(raw: &str) -> String {
    let cleaned = compactor::normalise(raw);
    let s = DOWNLOAD_RE.replace_all(&cleaned, "");

    // Collapse per-package install lines; keep summary
    let mut installed = 0usize;
    let mut lines_out: Vec<&str> = Vec::new();
    for line in s.lines() {
        let t = line.trim();
        if t.starts_with("Installed ") && t.ends_with("packages") {
            lines_out.push(line);
            continue;
        }
        if t.starts_with("+ ") || t.starts_with("- ") || t.starts_with("~ ") {
            installed += 1;
            continue;
        }
        if !t.is_empty() {
            lines_out.push(line);
        }
    }
    if installed > 0 {
        lines_out.push(""); // spacing
    }
    let mut result = lines_out.join("\n");
    if installed > 0 {
        // Already have an Installed summary? Skip double-counting
        if !result.contains("Installed") {
            result.push_str(&format!("\nInstalled {installed} packages"));
        }
    }
    compactor::collapse_blanks(&result)
}

fn compress_pip(raw: &str) -> String {
    // `uv pip install` / `uv pip list` / `uv pip show`
    let cleaned = compactor::normalise(raw);
    let s = DOWNLOAD_RE.replace_all(&cleaned, "");
    let s = RESOLVED_RE.replace_all(&s, |caps: &regex::Captures| {
        let n: usize = caps[1].parse().unwrap_or(0);
        if n > 20 {
            format!("Resolved {n} packages\n")
        } else {
            caps[0].to_string()
        }
    });
    compactor::collapse_blanks(&s)
}

fn compress_add(raw: &str) -> String {
    let cleaned = compactor::normalise(raw);
    let s = DOWNLOAD_RE.replace_all(&cleaned, "");
    compactor::collapse_blanks(&s)
}

fn compress_lock(raw: &str) -> String {
    let cleaned = compactor::normalise(raw);
    let s = HASH_RE.replace_all(&cleaned, "");
    let s = DOWNLOAD_RE.replace_all(&s, "");
    // Keep: "Updated uv.lock", "No changes", error lines
    let kept: Vec<&str> = s
        .lines()
        .filter(|l| {
            let t = l.trim();
            t.is_empty()
                || t.starts_with("Updated")
                || t.starts_with("No changes")
                || t.starts_with("Resolved")
                || t.contains("error")
                || t.contains("conflict")
        })
        .collect();
    compactor::collapse_blanks(&kept.join("\n"))
}

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

    #[test]
    fn install_collapses_package_lines() {
        let raw = "Resolved 42 packages in 300ms\n+ requests==2.31.0\n+ urllib3==2.0.0\n+ certifi==2024.1.0\n";
        let out = compress_uv("sync", raw);
        assert!(!out.contains("+ requests"), "{out}");
        assert!(
            out.contains("Resolved") || out.contains("Installed"),
            "{out}"
        );
    }

    #[test]
    fn lock_strips_hashes() {
        let raw = "name = \"requests\"\nversion = \"2.31.0\"\nhash = \"sha256:abcdef1234567890abcdef1234567890\"\nUpdated uv.lock\n";
        let out = compress_uv("lock", raw);
        assert!(!out.contains("sha256"), "{out}");
        assert!(out.contains("Updated"), "{out}");
    }

    #[test]
    fn lock_no_changes_kept() {
        let raw = "No changes to uv.lock\n";
        let out = compress_uv("lock", raw);
        assert!(out.contains("No changes"), "{out}");
    }

    #[test]
    fn run_is_passthrough() {
        let raw = "Hello, world!\nDone.\n";
        let out = compress_uv("run", raw);
        assert!(out.contains("Hello"), "{out}");
    }
}
