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

static PROGRESS_RE: Lazy<Regex> = Lazy::new(||
    // npm/yarn progress spinners and bars
    Regex::new(r"(?m)^[⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏⸗ ]+ [^\n]+\n?").unwrap());
static VERB_RE: Lazy<Regex> =
    Lazy::new(|| Regex::new(r"(?m)^npm (verb|http|info|timing|sill) [^\n]+\n?").unwrap());
static WARN_DEPRECATED_RE: Lazy<Regex> =
    Lazy::new(|| Regex::new(r"(?m)^npm warn deprecated [^\n]+\n?").unwrap());
static GYPI_RE: Lazy<Regex> = Lazy::new(||
    // node-gyp build noise
    Regex::new(r"(?m)^(gyp info|gyp ERR!|make\[)[^\n]+\n?").unwrap());

// ── npm install / yarn add ────────────────────────────────────────────────────

pub fn compress_install(raw: &str) -> String {
    let cleaned = compactor::normalise(raw);
    let s = VERB_RE.replace_all(&cleaned, "");
    let s = PROGRESS_RE.replace_all(&s, "");
    let s = WARN_DEPRECATED_RE.replace_all(&s, "");
    let s = GYPI_RE.replace_all(&s, "");
    compactor::collapse_blanks(&s)
}

// ── npm audit ─────────────────────────────────────────────────────────────────

pub fn compress_audit(raw: &str) -> String {
    let cleaned = compactor::normalise(raw);
    // Keep summary line and "X vulnerabilities" lines, strip individual package noise
    let out: Vec<&str> = cleaned
        .lines()
        .filter(|l| {
            l.contains("vulnerabilit")
                || l.contains("found")
                || l.contains("severity")
                || l.contains("fix available")
                || l.starts_with("  ") && l.contains("https://")
        })
        .collect();
    if out.is_empty() {
        cleaned
    } else {
        out.join("\n")
    }
}

// ── npm outdated ──────────────────────────────────────────────────────────────

pub fn compress_outdated(raw: &str) -> String {
    // Outdated output is already a compact table — just normalise
    compactor::normalise(raw)
}

// ── npm run <script> ──────────────────────────────────────────────────────────

pub fn compress_run(raw: &str) -> String {
    let cleaned = compactor::normalise(raw);
    // Strip "npm run <name>" echo header
    let out: Vec<&str> = cleaned
        .lines()
        .filter(|l| !l.starts_with("> ") || l.contains("error") || l.contains("Error"))
        .collect();
    if out.is_empty() {
        cleaned
    } else {
        out.join("\n")
    }
}

// ── npx / pnpm dlx ───────────────────────────────────────────────────────────

static NPX_INSTALL_RE: Lazy<Regex> = Lazy::new(||
    // "Need to install the following packages:\n  create-react-app\nOk to proceed? (y)"
    Regex::new(r"(?ms)^Need to install the following packages:.*?Ok to proceed\? \(y\)[^\n]*\n?").unwrap());

pub fn compress_npx(raw: &str) -> String {
    let cleaned = compactor::normalise(raw);
    let s = NPX_INSTALL_RE.replace_all(&cleaned, "");
    // Also strip npm verb/info lines that leak through
    let s = VERB_RE.replace_all(&s, "");
    compactor::collapse_blanks(&s)
}

// ── top-level dispatcher ──────────────────────────────────────────────────────

pub fn compress_npm(subcmd: &str, raw: &str) -> String {
    let sub = subcmd.trim();
    if sub.starts_with("install")
        || sub.starts_with("i ")
        || sub == "i"
        || sub.starts_with("add")
        || sub.starts_with("ci")
    {
        return compress_install(raw);
    }
    if sub.starts_with("audit") {
        return compress_audit(raw);
    }
    if sub.starts_with("outdated") {
        return compress_outdated(raw);
    }
    if sub.starts_with("run") {
        return compress_run(raw);
    }
    compress_install(raw) // generic: strip progress noise
}

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

    #[test]
    fn install_strips_verb_lines() {
        let raw = "npm verb cli /usr/bin/node\nnpm info ok\nadded 42 packages in 3s\n";
        let out = compress_install(raw);
        assert!(!out.contains("npm verb"), "{out}");
        assert!(out.contains("added 42 packages"));
    }

    #[test]
    fn install_strips_deprecated_warnings() {
        let raw = "npm warn deprecated foo@1.0.0: use bar instead\nadded 1 package\n";
        let out = compress_install(raw);
        assert!(!out.contains("deprecated"), "{out}");
        assert!(out.contains("added 1 package"));
    }

    #[test]
    fn audit_keeps_vulnerability_summary() {
        let raw = "found 3 vulnerabilities (1 low, 2 high)\nrun `npm audit fix` to fix\n";
        let out = compress_audit(raw);
        assert!(out.contains("vulnerabilit"), "{out}");
    }
}
