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

// "⛅️ wrangler 3.40.0" version header
static VERSION_HEADER_RE: Lazy<Regex> =
    Lazy::new(|| Regex::new(r"(?m)^[^\n]*wrangler \d+\.\d+\.\d+[^\n]*\n?").unwrap());
// "Bundling with esbuild..." and similar progress lines
static BUNDLING_RE: Lazy<Regex> =
    Lazy::new(|| Regex::new(r"(?m)^[^\n]*(Bundling|bundling)[^\n]*\n?").unwrap());
// "Your worker has access to the following bindings:" section header (keep body)
static BINDINGS_HEADER_RE: Lazy<Regex> = Lazy::new(|| {
    Regex::new(r"(?m)^Your worker has access to the following bindings:[^\n]*\n?").unwrap()
});
// Upload progress: "✨ Success! Uploaded X files (Ys)"
static UPLOAD_FILES_RE: Lazy<Regex> =
    Lazy::new(|| Regex::new(r"(?m)^[^\n]*Uploaded \d+ files?[^\n]*\n?").unwrap());
// Watching for file changes lines
static WATCHING_RE: Lazy<Regex> =
    Lazy::new(|| Regex::new(r"(?m)^[^\n]*(Watching|watching) for[^\n]*\n?").unwrap());

// ── wrangler deploy ───────────────────────────────────────────────────────────

pub fn compress_deploy(raw: &str) -> String {
    let cleaned = compactor::normalise(raw);
    let s = VERSION_HEADER_RE.replace_all(&cleaned, "");
    let s = BUNDLING_RE.replace_all(&s, "");
    let s = UPLOAD_FILES_RE.replace_all(&s, "");
    let s = WATCHING_RE.replace_all(&s, "");
    // Keep lines with the deployed URL, worker name, size stats, errors
    let useful: Vec<&str> = s
        .lines()
        .filter(|l| {
            let t = l.trim();
            !t.is_empty()
                && (t.contains("workers.dev")
                    || t.contains("Deployed")
                    || t.contains("deployed")
                    || t.contains("Total Upload")
                    || t.contains("Gzip")
                    || t.contains("Script size")
                    || t.contains("error")
                    || t.contains("Error")
                    || t.contains("warn")
                    || t.contains("Warn")
                    || t.contains("✅")
                    || t.contains("✨")
                    || t.starts_with("Published")
                    || t.starts_with("Binding:"))
        })
        .collect();
    if useful.is_empty() {
        return compactor::collapse_blanks(&s);
    }
    useful.join("\n")
}

// ── wrangler build ────────────────────────────────────────────────────────────

pub fn compress_build(raw: &str) -> String {
    let cleaned = compactor::normalise(raw);
    let s = VERSION_HEADER_RE.replace_all(&cleaned, "");
    let s = BUNDLING_RE.replace_all(&s, "");
    let s = BINDINGS_HEADER_RE.replace_all(&s, "");
    let useful: Vec<&str> = s
        .lines()
        .filter(|l| {
            let t = l.trim();
            !t.is_empty()
                && (t.contains("Built")
                    || t.contains("Total Upload")
                    || t.contains("Gzip")
                    || t.contains("output")
                    || t.contains("error")
                    || t.contains("Error")
                    || t.contains("warn"))
        })
        .collect();
    if useful.is_empty() {
        return compactor::collapse_blanks(&s);
    }
    useful.join("\n")
}

// ── wrangler dev (passthrough — streaming, but trim banner) ──────────────────

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

// ── wrangler tail (log streaming) ─────────────────────────────────────────────

pub fn compress_tail(raw: &str) -> String {
    // Keep log events, strip heartbeat/connection lines
    let cleaned = compactor::normalise(raw);
    let lines: Vec<&str> = cleaned
        .lines()
        .filter(|l| {
            let t = l.trim();
            !t.is_empty()
                && !t.starts_with("Connected to")
                && !t.starts_with("Waiting for")
                && !t.contains("Listening on")
        })
        .collect();
    if lines.len() > 50 {
        return format!(
            "{}\n… [{} more log lines] …",
            lines[..50].join("\n"),
            lines.len() - 50
        );
    }
    lines.join("\n")
}

// ── wrangler kv / r2 / d1 ────────────────────────────────────────────────────

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

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

pub fn compress_wrangler(subcmd: &str, raw: &str) -> String {
    let sub = subcmd.trim();
    if sub.starts_with("deploy") || sub.starts_with("publish") {
        return compress_deploy(raw);
    }
    if sub.starts_with("build") {
        return compress_build(raw);
    }
    if sub.starts_with("dev") {
        return compress_dev(raw);
    }
    if sub.starts_with("tail") {
        return compress_tail(raw);
    }
    if sub.starts_with("kv") || sub.starts_with("r2") || sub.starts_with("d1") {
        return compress_storage(raw);
    }
    // pages, secret, etc.
    let cleaned = compactor::normalise(raw);
    let s = VERSION_HEADER_RE.replace_all(&cleaned, "");
    compactor::collapse_blanks(&s)
}

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

    #[test]
    fn deploy_keeps_url_and_size() {
        let raw = "⛅️ wrangler 3.40.0\n--------------------\nTotal Upload: 42.3 KiB / gzip: 12.1 KiB\nUploaded my-worker (1.23 sec)\nPublished my-worker (0.45 sec)\n  https://my-worker.my-account.workers.dev\n";
        let out = compress_deploy(raw);
        assert!(out.contains("workers.dev"), "{out}");
        assert!(out.contains("Total Upload"), "{out}");
        assert!(out.contains("Published"), "{out}");
    }

    #[test]
    fn deploy_strips_bundling_noise() {
        let raw = "⛅️ wrangler 3.40.0\nBundling with esbuild...\nTotal Upload: 10 KiB / gzip: 4 KiB\nPublished worker (0.5 sec)\n  https://worker.example.workers.dev\n";
        let out = compress_deploy(raw);
        assert!(!out.contains("Bundling"), "{out}");
        assert!(!out.contains("wrangler 3"), "{out}");
        assert!(out.contains("Total Upload"), "{out}");
    }

    #[test]
    fn tail_truncates_long_log_stream() {
        let lines: Vec<String> = (0..60)
            .map(|i| format!("{{\"level\":\"log\",\"message\":\"event-{i}\"}}"))
            .collect();
        let out = compress_tail(&lines.join("\n"));
        assert!(out.contains("more log lines"), "{out}");
    }

    #[test]
    fn tail_strips_connection_lines() {
        let raw = "Connected to my-worker at https://...\nWaiting for logs...\n{\"level\":\"log\",\"message\":\"hello\"}\n";
        let out = compress_tail(raw);
        assert!(!out.contains("Connected to"), "{out}");
        assert!(!out.contains("Waiting for"), "{out}");
        assert!(out.contains("hello"), "{out}");
    }
}
