use forge::signal::compactor;

const MAX_PS_LINES: usize = 30;
const MAX_LSOF_LINES: usize = 40;
const MAX_NETSTAT_LINES: usize = 40;

// ── ps ────────────────────────────────────────────────────────────────────────

pub fn compress_ps(raw: &str) -> String {
    let cleaned = compactor::normalise(raw);
    let lines: Vec<&str> = cleaned.lines().filter(|l| !l.trim().is_empty()).collect();
    if lines.len() <= MAX_PS_LINES + 1 {
        return cleaned;
    }
    // Keep header + top N rows (processes sorted by CPU/MEM as delivered)
    let header = lines.first().copied().unwrap_or("");
    let body = &lines[1..MAX_PS_LINES + 1];
    let omitted = lines.len() - 1 - MAX_PS_LINES;
    format!(
        "{}\n{}\n... [{} more processes] ...",
        header,
        body.join("\n"),
        omitted
    )
}

// ── df ────────────────────────────────────────────────────────────────────────

pub fn compress_df(raw: &str) -> String {
    let cleaned = compactor::normalise(raw);
    // df output is typically short — keep as-is, just normalise
    // Filter: skip 0% entries and tmpfs by default
    let kept: Vec<&str> = cleaned
        .lines()
        .filter(|l| {
            let t = l.trim();
            t.is_empty() ||
            // Keep header + non-zero-use, non-tmpfs entries + high usage
            t.starts_with("Filesystem") || t.starts_with("udev") ||
            (!t.contains("tmpfs") && !t.contains("devtmpfs")) ||
            // Always keep high-usage entries
            t.contains("9") && t.contains('%')
        })
        .collect();
    if kept.is_empty() {
        return cleaned;
    }
    kept.join("\n")
}

// ── du ────────────────────────────────────────────────────────────────────────

pub fn compress_du(raw: &str) -> String {
    let cleaned = compactor::normalise(raw);
    let lines: Vec<&str> = cleaned.lines().filter(|l| !l.trim().is_empty()).collect();
    // du can be enormous — keep first 50 lines + total (last line usually has total)
    if lines.len() <= 50 {
        return cleaned;
    }
    let last = lines.last().copied().unwrap_or("");
    let omitted = lines.len() - 50;
    format!(
        "{}\n... [{} more entries] ...\n{}",
        lines[..50].join("\n"),
        omitted,
        last
    )
}

// ── lsof ─────────────────────────────────────────────────────────────────────

pub fn compress_lsof(raw: &str) -> String {
    let cleaned = compactor::normalise(raw);
    let lines: Vec<&str> = cleaned.lines().filter(|l| !l.trim().is_empty()).collect();
    if lines.len() <= MAX_LSOF_LINES + 1 {
        return cleaned;
    }
    let header = lines.first().copied().unwrap_or("");
    let body = &lines[1..MAX_LSOF_LINES + 1];
    let omitted = lines.len() - 1 - MAX_LSOF_LINES;
    format!(
        "{}\n{}\n... [{} more open files] ...",
        header,
        body.join("\n"),
        omitted
    )
}

// ── netstat / ss ──────────────────────────────────────────────────────────────

pub fn compress_netstat(raw: &str) -> String {
    let cleaned = compactor::normalise(raw);
    let lines: Vec<&str> = cleaned.lines().filter(|l| !l.trim().is_empty()).collect();
    if lines.len() <= MAX_NETSTAT_LINES + 1 {
        return cleaned;
    }
    let header = lines.first().copied().unwrap_or("");
    let body = &lines[1..MAX_NETSTAT_LINES + 1];
    let omitted = lines.len() - 1 - MAX_NETSTAT_LINES;
    format!(
        "{}\n{}\n... [{} more connections] ...",
        header,
        body.join("\n"),
        omitted
    )
}

// ── free / vmstat ─────────────────────────────────────────────────────────────

pub fn compress_free(raw: &str) -> String {
    // free is typically 3 lines — passthrough
    compactor::normalise(raw)
}

// ── uname ─────────────────────────────────────────────────────────────────────

pub fn compress_uname(raw: &str) -> String {
    compactor::normalise(raw)
}

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

    #[test]
    fn ps_truncates_many_processes() {
        let header = "PID  TTY  TIME     CMD\n";
        let rows: String = (0..50)
            .map(|i| format!("{i:5}  pts/0  00:00:0{i}  prog{i}\n"))
            .collect();
        let out = compress_ps(&format!("{header}{rows}"));
        assert!(out.contains("more processes"), "{out}");
    }

    #[test]
    fn du_truncates_large_output() {
        let raw: String = (0..80).map(|i| format!("4.0K\t./dir_{i:02}\n")).collect();
        let out = compress_du(&raw);
        assert!(out.contains("more entries"), "{out}");
    }

    #[test]
    fn lsof_truncates_many_files() {
        let header = "COMMAND  PID  USER  FD  TYPE  DEVICE  SIZE  NODE  NAME\n";
        let rows: String = (0..60)
            .map(|i| format!("cmd  {i}  user  {i}  REG  8,1  1024  {i}  /path/file{i}\n"))
            .collect();
        let out = compress_lsof(&format!("{header}{rows}"));
        assert!(out.contains("more open files"), "{out}");
    }
}
