use forge::signal::compactor;

pub fn compress_rustfmt(subcmd: &str, raw: &str) -> String {
    let sub = subcmd.trim();
    // `cargo fmt --check` or `rustfmt --check` produces diff output
    if sub.contains("check") || raw.contains("Diff in ") {
        return compress_check(raw);
    }
    compress_normal(raw)
}

fn compress_check(raw: &str) -> String {
    let cleaned = compactor::normalise(raw);
    if cleaned.trim().is_empty() {
        return "rustfmt: all files formatted".to_string();
    }

    // Collect file names that need formatting from "Diff in path/to/file.rs:" headers
    let mut files: Vec<&str> = Vec::new();
    let mut error_lines: Vec<&str> = Vec::new();

    for line in cleaned.lines() {
        let t = line.trim();
        if t.starts_with("Diff in ") {
            // "Diff in src/foo.rs at line 10:"
            if let Some(path) = t.strip_prefix("Diff in ") {
                let path = path.split(" at line").next().unwrap_or(path);
                files.push(path);
            }
        } else if t.starts_with("error") {
            error_lines.push(line);
        }
        // Skip diff content lines (+, -, unchanged context)
    }

    if !error_lines.is_empty() {
        return error_lines.join("\n");
    }

    if files.is_empty() {
        // No "Diff in" headers — some rustfmt versions just list file paths
        let formatted_files: Vec<&str> = cleaned
            .lines()
            .filter(|l| {
                let t = l.trim();
                (t.ends_with(".rs") || t.contains(".rs:"))
                    && !t.starts_with('+')
                    && !t.starts_with('-')
            })
            .collect();
        if !formatted_files.is_empty() {
            return format!(
                "rustfmt --check: {} file(s) need formatting\n{}",
                formatted_files.len(),
                formatted_files.join("\n")
            );
        }
        return "rustfmt --check: needs formatting".to_string();
    }

    format!(
        "rustfmt --check: {} file(s) need formatting\n{}",
        files.len(),
        files.join("\n")
    )
}

fn compress_normal(raw: &str) -> String {
    let cleaned = compactor::normalise(raw);
    // On success, rustfmt produces no output — return quiet confirmation
    if cleaned.trim().is_empty() {
        return String::new(); // silent success is correct — don't add noise
    }
    // Parse errors (invalid syntax) — keep verbatim
    if cleaned.contains("error[") || cleaned.contains("error:") {
        return cleaned;
    }
    compactor::collapse_blanks(&cleaned)
}

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

    #[test]
    fn check_lists_files_needing_formatting() {
        let raw = "Diff in src/foo.rs at line 10:\n-    let x=1;\n+    let x = 1;\nDiff in src/bar.rs at line 5:\n-fn bad(){}\n+fn bad() {}\n";
        let out = compress_rustfmt("check", raw);
        assert!(out.contains("src/foo.rs"), "{out}");
        assert!(out.contains("src/bar.rs"), "{out}");
        assert!(out.contains("2"), "{out}");
        assert!(!out.contains("-    let x=1"), "{out}");
    }

    #[test]
    fn check_all_formatted_clean() {
        let out = compress_rustfmt("check", "");
        assert!(out.contains("formatted") || out.is_empty(), "{out}");
    }

    #[test]
    fn normal_success_is_silent() {
        let out = compress_rustfmt("", "");
        assert!(
            out.is_empty(),
            "silent success should produce no output: {out}"
        );
    }

    #[test]
    fn normal_parse_error_kept() {
        let raw = "error[E0001]: expected `;`\n --> src/main.rs:10:1\n";
        let out = compress_rustfmt("", raw);
        assert!(out.contains("error"), "{out}");
    }
}
