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

// "System check identified no issues (0 silenced)." boilerplate
static SYSTEM_CHECK_OK_RE: Lazy<Regex> =
    Lazy::new(|| Regex::new(r"(?m)^System check identified no issues[^\n]*\n?").unwrap());
// "Performing system checks..." interim line
static SYSTEM_CHECK_PERFORMING_RE: Lazy<Regex> =
    Lazy::new(|| Regex::new(r"(?m)^Performing system checks\.\.\.\s*\n?").unwrap());
// HTTP request log lines in dev server: "GET /path HTTP/1.1" 200 1234
static HTTP_LOG_RE: Lazy<Regex> =
    Lazy::new(|| Regex::new(r#"(?m)^"[A-Z]+ [^\n]+" \d{3} \d+\s*$"#).unwrap());

// ── manage.py migrate ─────────────────────────────────────────────────────────

pub fn compress_migrate(raw: &str) -> String {
    let cleaned = compactor::normalise(raw);
    let s = SYSTEM_CHECK_OK_RE.replace_all(&cleaned, "");
    let s = SYSTEM_CHECK_PERFORMING_RE.replace_all(&s, "");

    // Keep migration names, status markers, errors
    let useful: Vec<&str> = s
        .lines()
        .filter(|l| {
            let t = l.trim();
            !t.is_empty()
                && (t.starts_with("Running migrations:")
                    || t.starts_with("Applying")
                    || t.starts_with("Unapplying")
                    || t.starts_with("No migrations")
                    || t.contains("OK")
                    || t.contains("FAKED")
                    || t.contains("error")
                    || t.contains("Error")
                    || t.contains("CommandError"))
        })
        .collect();

    if useful.is_empty() {
        return compactor::collapse_blanks(&s);
    }
    useful.join("\n")
}

// ── manage.py makemigrations ──────────────────────────────────────────────────

pub fn compress_makemigrations(raw: &str) -> String {
    let cleaned = compactor::normalise(raw);
    let s = SYSTEM_CHECK_OK_RE.replace_all(&cleaned, "");
    let s = SYSTEM_CHECK_PERFORMING_RE.replace_all(&s, "");

    let useful: Vec<&str> = s
        .lines()
        .filter(|l| {
            let t = l.trim();
            !t.is_empty()
                && (t.starts_with("Migrations for")
                    || t.starts_with("No changes")
                    || t.starts_with("Create model")
                    || t.starts_with("Add field")
                    || t.starts_with("Alter field")
                    || t.starts_with("Delete model")
                    || t.starts_with("Rename")
                    || t.starts_with('-')
                    || t.contains("migration")
                    || t.contains("Migration")
                    || t.contains("error")
                    || t.contains("Error"))
        })
        .collect();

    if useful.is_empty() {
        return compactor::collapse_blanks(&s);
    }
    useful.join("\n")
}

// ── manage.py runserver ───────────────────────────────────────────────────────

pub fn compress_runserver(raw: &str) -> String {
    let cleaned = compactor::normalise(raw);
    let s = HTTP_LOG_RE.replace_all(&cleaned, "");
    let s = SYSTEM_CHECK_OK_RE.replace_all(&s, "");
    let s = SYSTEM_CHECK_PERFORMING_RE.replace_all(&s, "");

    // Keep: startup address, settings, errors/warnings
    let useful: Vec<&str> = s
        .lines()
        .filter(|l| {
            let t = l.trim();
            !t.is_empty()
                && (t.contains("Starting development server")
                    || t.contains("Quit the server")
                    || t.contains("WARNINGS")
                    || t.contains("Error")
                    || t.contains("error")
                    || t.contains("Django version")
                    || t.contains("Using settings")
                    || t.contains("127.0.0.1")
                    || t.contains("0.0.0.0"))
        })
        .collect();

    if useful.is_empty() {
        return compactor::collapse_blanks(&s);
    }
    useful.join("\n")
}

// ── manage.py test ────────────────────────────────────────────────────────────

pub fn compress_test(raw: &str) -> String {
    let cleaned = compactor::normalise(raw);
    let s = SYSTEM_CHECK_OK_RE.replace_all(&cleaned, "");
    let s = SYSTEM_CHECK_PERFORMING_RE.replace_all(&s, "");

    // Keep test results; strip verbose dot-per-test output
    let s_no_dots = s.to_string();
    let useful: Vec<&str> = s_no_dots
        .lines()
        .filter(|l| {
            let t = l.trim();
            !t.is_empty()
                && !t.chars().all(|c| c == '.')
                && (t.starts_with("FAIL")
                    || t.starts_with("ERROR")
                    || t.starts_with("Ran ")
                    || t.starts_with("OK")
                    || t.starts_with("FAILED")
                    || t.contains("AssertionError")
                    || t.contains("Error:")
                    || t.starts_with("=====")
                    || t.starts_with("-----"))
        })
        .collect();

    if useful.is_empty() {
        return compactor::collapse_blanks(&s_no_dots);
    }
    useful.join("\n")
}

// ── manage.py collectstatic ───────────────────────────────────────────────────

pub fn compress_collectstatic(raw: &str) -> String {
    let cleaned = compactor::normalise(raw);
    let s = SYSTEM_CHECK_OK_RE.replace_all(&cleaned, "");
    // Keep summary line only
    let useful: Vec<&str> = s
        .lines()
        .filter(|l| {
            let t = l.trim();
            !t.is_empty()
                && (t.contains("static files")
                    || t.contains("copied")
                    || t.contains("post-processed")
                    || t.contains("unmodified")
                    || t.contains("error")
                    || t.contains("Error"))
        })
        .collect();
    if useful.is_empty() {
        return compactor::collapse_blanks(&s);
    }
    useful.join("\n")
}

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

pub fn compress_django(subcmd: &str, raw: &str) -> String {
    let sub = subcmd.trim();
    if sub.starts_with("migrate") {
        return compress_migrate(raw);
    }
    if sub.starts_with("makemigrations") {
        return compress_makemigrations(raw);
    }
    if sub.starts_with("runserver") {
        return compress_runserver(raw);
    }
    if sub.starts_with("test") {
        return compress_test(raw);
    }
    if sub.starts_with("collectstatic") {
        return compress_collectstatic(raw);
    }
    // shell, createsuperuser, dbshell, etc.
    let cleaned = compactor::normalise(raw);
    let s = SYSTEM_CHECK_OK_RE.replace_all(&cleaned, "");
    let s = SYSTEM_CHECK_PERFORMING_RE.replace_all(&s, "");
    compactor::collapse_blanks(&s)
}

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

    #[test]
    fn migrate_strips_system_check_boilerplate() {
        let raw = "Performing system checks...\n\nSystem check identified no issues (0 silenced).\n\nOperations to perform:\n  Apply all migrations: auth, contenttypes, myapp\nRunning migrations:\n  Applying myapp.0001_initial... OK\n  Applying myapp.0002_add_email... OK\n";
        let out = compress_migrate(raw);
        assert!(!out.contains("System check identified"), "{out}");
        assert!(!out.contains("Performing system checks"), "{out}");
        assert!(out.contains("0001_initial"), "{out}");
        assert!(out.contains("OK"), "{out}");
    }

    #[test]
    fn makemigrations_keeps_migration_details() {
        let raw = "Performing system checks...\n\nSystem check identified no issues (0 silenced).\n\nMigrations for 'myapp':\n  myapp/migrations/0003_add_phone.py\n    - Add field phone to userprofile\n";
        let out = compress_makemigrations(raw);
        assert!(out.contains("Migrations for"), "{out}");
        assert!(out.contains("0003_add_phone"), "{out}");
        assert!(out.contains("Add field phone"), "{out}");
        assert!(!out.contains("System check identified"), "{out}");
    }

    #[test]
    fn runserver_strips_http_request_logs() {
        let raw = "Django version 5.0, using settings 'myproject.settings'\nStarting development server at http://127.0.0.1:8000/\nQuit the server with CONTROL-C.\n\"GET / HTTP/1.1\" 200 1234\n\"GET /admin/ HTTP/1.1\" 302 0\n\"POST /api/login/ HTTP/1.1\" 200 456\n";
        let out = compress_runserver(raw);
        assert!(!out.contains("\"GET /"), "{out}");
        assert!(!out.contains("\"POST /"), "{out}");
        assert!(out.contains("127.0.0.1:8000"), "{out}");
        assert!(out.contains("Django version"), "{out}");
    }

    #[test]
    fn test_keeps_failure_output() {
        let raw = "Performing system checks...\n\nSystem check identified no issues (0 silenced).\n\n..F\n======================================================================\nFAIL: test_add (myapp.tests.MathTests)\n----------------------------------------------------------------------\nAssertionError: 3 != 4\n----------------------------------------------------------------------\nRan 3 tests in 0.001s\n\nFAILED (failures=1)\n";
        let out = compress_test(raw);
        assert!(out.contains("FAIL"), "{out}");
        assert!(out.contains("AssertionError"), "{out}");
        assert!(out.contains("Ran 3 tests"), "{out}");
        assert!(!out.contains("System check identified"), "{out}");
    }

    #[test]
    fn collectstatic_keeps_summary() {
        let raw = "Performing system checks...\n\nSystem check identified no issues (0 silenced).\n\n125 static files copied to '/app/staticfiles', 42 unmodified.\n";
        let out = compress_collectstatic(raw);
        assert!(out.contains("static files"), "{out}");
        assert!(!out.contains("System check identified"), "{out}");
    }
}
