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

// Noise emitted by apt/apt-get on nearly every run.
static HOUSEKEEPING_RE: Lazy<Regex> = Lazy::new(|| {
    Regex::new(
        r"(?m)^\s*(Reading package lists|Building dependency tree|Reading state information|Reading database)[^\n]*\n?",
    )
    .unwrap()
});
// Per-archive download + unpack chatter.
static FETCH_RE: Lazy<Regex> = Lazy::new(|| {
    Regex::new(
        r"(?m)^\s*(Get:\d+|Hit:\d+|Ign:\d+|Fetched|Selecting previously|Preparing to unpack|Unpacking|Processing triggers)[^\n]*\n?",
    )
    .unwrap()
});

/// apt-get install/upgrade: drop housekeeping + download/unpack noise, keep the
/// package plan, the "N upgraded, M newly installed" summary, and any E:/W: lines.
pub fn compress_install(raw: &str) -> String {
    let cleaned = compactor::normalise(raw);
    let s = HOUSEKEEPING_RE.replace_all(&cleaned, "");
    let s = FETCH_RE.replace_all(&s, "");
    compactor::collapse_blanks(&s)
}

/// apt list / apt-cache search: keep the head of long package listings.
pub fn compress_list(raw: &str) -> String {
    let cleaned = compactor::normalise(raw);
    let lines: Vec<&str> = cleaned.lines().collect();
    if lines.len() <= 30 {
        return cleaned;
    }
    format!(
        "{}\n... [{} more packages] ...",
        lines[..30].join("\n"),
        lines.len() - 30
    )
}

pub fn compress_apt(subcmd: &str, raw: &str) -> String {
    let sub = subcmd.trim();
    if sub.starts_with("install")
        || sub.starts_with("upgrade")
        || sub.starts_with("dist-upgrade")
        || sub.starts_with("full-upgrade")
        || sub.starts_with("remove")
        || sub.starts_with("purge")
        || sub.starts_with("update")
        || sub.starts_with("build-dep")
    {
        return compress_install(raw);
    }
    if sub.starts_with("list") || sub.starts_with("search") || sub.starts_with("show") {
        return compress_list(raw);
    }
    compactor::normalise(raw)
}

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

    #[test]
    fn install_strips_housekeeping_and_fetch() {
        let raw = "Reading package lists... Done\nBuilding dependency tree... Done\nReading state information... Done\nThe following NEW packages will be installed:\n  curl git\n0 upgraded, 2 newly installed, 0 to remove and 5 not upgraded.\nGet:1 http://deb.debian.org/debian bookworm/main amd64 curl amd64 7.88.1 [315 kB]\nFetched 2345 kB in 1s\nSelecting previously unselected package curl.\nPreparing to unpack .../curl_7.88.1.deb ...\nUnpacking curl (7.88.1) ...\nSetting up curl (7.88.1) ...\n";
        let out = compress_install(raw);
        assert!(!out.contains("Reading package lists"), "{out}");
        assert!(!out.contains("Get:1"), "{out}");
        assert!(!out.contains("Unpacking curl"), "{out}");
        assert!(out.contains("newly installed"), "{out}");
        assert!(out.contains("Setting up curl"), "{out}");
    }

    #[test]
    fn list_truncates_long_output() {
        let raw = (0..50)
            .map(|i| format!("pkg{i}/stable 1.{i}.0 amd64"))
            .collect::<Vec<_>>()
            .join("\n");
        let out = compress_list(&raw);
        assert!(out.contains("more packages"), "{out}");
    }
}
