use lean_ctx::core::compressor::{aggressive_compress, lightweight_cleanup, safeguard_ratio};
use lean_ctx::core::entropy::entropy_compress;
use lean_ctx::core::protocol::instruction_decoder_block;
use lean_ctx::core::signatures::extract_signatures;
use lean_ctx::core::tokens::count_tokens;
use lean_ctx::tools::CrpMode;
use lean_ctx::tools::ctx_response;

// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------

fn compression_ratio(original: usize, compressed: usize) -> f64 {
    if original == 0 {
        return 0.0;
    }
    (original - compressed) as f64 / original as f64 * 100.0
}

fn measure_pattern(command: &str, output: &str) -> (usize, usize, f64) {
    let original = count_tokens(output);
    let compressed = lean_ctx::core::patterns::compress_output(command, output)
        .unwrap_or_else(|| output.to_string());
    let comp_tokens = count_tokens(&compressed);
    (
        original,
        comp_tokens,
        compression_ratio(original, comp_tokens),
    )
}

fn measure_text(original: &str, compressed: &str) -> (usize, usize, f64) {
    let orig_t = count_tokens(original);
    let comp_t = count_tokens(compressed);
    (orig_t, comp_t, compression_ratio(orig_t, comp_t))
}

// ═══════════════════════════════════════════════════════════════════════════
// SECTION 1: INPUT TOKEN BENCHMARKS — Tool Descriptions + System Instructions
// ═══════════════════════════════════════════════════════════════════════════

#[test]
fn bench_system_instructions_token_count() {
    let instructions_off = lean_ctx::server::build_instructions_for_test(CrpMode::Off);
    let instructions_compact = lean_ctx::server::build_instructions_for_test(CrpMode::Compact);
    let instructions_tdd = lean_ctx::server::build_instructions_for_test(CrpMode::Tdd);

    let tok_off = count_tokens(&instructions_off);
    let tok_compact = count_tokens(&instructions_compact);
    let tok_tdd = count_tokens(&instructions_tdd);

    eprintln!("\n{}", "=".repeat(70));
    eprintln!("  SYSTEM INSTRUCTIONS TOKEN COUNT");
    eprintln!("{}", "=".repeat(70));
    eprintln!(
        "  CRP Off:     {:>6} tokens ({:>5} chars)",
        tok_off,
        instructions_off.len()
    );
    eprintln!(
        "  CRP Compact: {:>6} tokens ({:>5} chars)",
        tok_compact,
        instructions_compact.len()
    );
    eprintln!(
        "  CRP TDD:     {:>6} tokens ({:>5} chars)",
        tok_tdd,
        instructions_tdd.len()
    );
    let compact_overhead = (tok_compact as i64) - (tok_off as i64);
    let tdd_overhead = (tok_tdd as i64) - (tok_off as i64);
    eprintln!("  Compact overhead: {compact_overhead:+} tokens vs Off");
    eprintln!("  TDD overhead:     {tdd_overhead:+} tokens vs Off");
    eprintln!("{}", "=".repeat(70));

    assert!(
        tok_off < 2300,
        "Base instructions should be <2300 tokens, got {tok_off}"
    );
    assert!(
        tok_compact < 2450,
        "Compact instructions should be <2450 tokens, got {tok_compact}"
    );
    assert!(
        tok_tdd < 2550,
        "TDD instructions should be <2550 tokens, got {tok_tdd}"
    );

    // The char budget governs the STATIC cold first-contact handshake
    // instructions. A live build also appends dynamic session/knowledge/gotcha
    // payload, but that is capped by INSTRUCTION_CAP_TOKENS (token budget), not
    // this char budget — and it varies with whatever session is persisted on the
    // runner. Measuring the static surface keeps the assertion deterministic
    // (#498) instead of order/state-dependent.
    //
    // Budget history: 2048 → 2400 (#609), then the v5 rules diet (#578) folded
    // the loop/paradox guidance into INTENT and shrank the COMPACT/Bare channel
    // the cold handshake renders. 2400 stays as the ceiling (upper bound only —
    // it catches a runaway regression, shrinking is always fine).
    let claude_code_instr = lean_ctx::server::build_claude_code_static_instructions_for_test();
    let claude_chars = claude_code_instr.len();
    let claude_tokens = count_tokens(&claude_code_instr);
    eprintln!("  Claude Code (static): {claude_tokens:>6} tokens ({claude_chars:>5} chars)");
    assert!(
        claude_chars <= 2400,
        "Claude Code static instructions MUST be <=2400 chars, got {claude_chars}"
    );
    assert!(
        compact_overhead.unsigned_abs() < 300,
        "Compact mode overhead should be <300 tokens, got {compact_overhead}"
    );
    assert!(
        tdd_overhead.unsigned_abs() < 650,
        "TDD mode overhead should be <650 tokens, got {tdd_overhead}"
    );
}

#[test]
fn bench_tool_descriptions_token_count() {
    let descriptions = lean_ctx::server::tool_descriptions_for_test();

    let mut total = 0usize;
    eprintln!("\n{}", "=".repeat(70));
    eprintln!("  TOOL DESCRIPTION TOKEN COUNTS");
    eprintln!("{}", "=".repeat(70));
    eprintln!("  {:<25} {:>8} {:>8}", "Tool", "Tokens", "Chars");
    eprintln!("  {}", "-".repeat(45));

    for (name, desc) in &descriptions {
        let t = count_tokens(desc);
        total += t;
        eprintln!("  {:<25} {:>8} {:>8}", name, t, desc.len());
    }

    eprintln!("  {}", "-".repeat(45));
    eprintln!("  {:<25} {:>8}", "TOTAL", total);
    eprintln!("{}", "=".repeat(70));

    // Budget = the real registry tool surface (single source of truth, #141):
    // every tool with its full `McpTool::tool_def()` description — exactly what
    // the live server advertises in full mode. Set with deliberate headroom over
    // the current actual so adding a tool or two never blocks CI; only raise it
    // again on a material jump in the surface, not on routine additions (#290).
    // Raised 3000 -> 4000 for the #496 tool-profile reorg: a material jump that
    // enriches per-tool profile metadata on the full opt-in surface. The default
    // surface stays small (see `bench_lazy_default_vs_full_overhead`).
    // Raised 4000 -> 5200 for #505 (@omar-mohamed-khallaf): the power tier now
    // carries the same first-line-dense, workflow-first treatment as the other
    // tiers (actual ~4872), with headroom for a tool or two per #290.
    // Raised 5200 -> 5600 for #510 (@omar-mohamed-khallaf): the optimized tools
    // schema adds explicit WORKFLOW:/ANTIPATTERN: intent lines across the surface
    // (actual ~5498). Validated by the output-quality gate (eval A/B: NO
    // REGRESSION, Δ +0.039) — the richer intents earn their tokens. The per-
    // request cost is unchanged (core surface stays at ~2272, see
    // `core_tool_surface_stays_within_budget`); this full-surface total only
    // applies in opt-in full mode. Cutting it further is #509 (reduce tool
    // COUNT), not trimming these eval-validated descriptions.
    // Raised 5600 -> 6000 for #982: registering ctx_compare (78 -> 79 tools)
    // pushed the actual to ~5630, over the previous 5600 ceiling whose headroom
    // had shrunk below one tool. Per #290 the ceiling must carry "a tool or two"
    // of slack over the actual, so 6000 (actual ~5630) restores that buffer.
    assert!(
        total < 6000,
        "Total tool description tokens should be <6000, got {total}"
    );

    for (name, desc) in &descriptions {
        // Every tool — including the ctx_call invoker — stays within a tight
        // per-tool description budget. ctx_call no longer embeds the full
        // non-core catalog; agents discover callable tools via ctx_discover_tools
        // (#680), so no tool is exempt from this ceiling.
        let t = count_tokens(desc);
        assert!(
            t < 160,
            "Tool '{name}' description should be <160 tokens, got {t}"
        );
    }
}

#[test]
fn bench_total_input_overhead() {
    let instructions = lean_ctx::server::build_instructions_for_test(CrpMode::Off);
    let descs = lean_ctx::server::tool_descriptions_for_test();
    let schemas = lean_ctx::server::tool_schemas_json_for_test();

    let instr_tokens = count_tokens(&instructions);
    let desc_tokens: usize = descs.iter().map(|(_, d)| count_tokens(d)).sum();
    let schema_tokens = count_tokens(&schemas);

    let total = instr_tokens + desc_tokens + schema_tokens;

    eprintln!("\n{}", "=".repeat(70));
    eprintln!("  TOTAL INPUT OVERHEAD (per session start)");
    eprintln!("{}", "=".repeat(70));
    eprintln!("  System instructions: {instr_tokens:>6} tokens");
    eprintln!("  Tool descriptions:   {desc_tokens:>6} tokens");
    eprintln!("  Tool schemas (JSON): {schema_tokens:>6} tokens");
    eprintln!("  {}", "-".repeat(40));
    eprintln!("  TOTAL overhead:      {total:>6} tokens");
    eprintln!(
        "  Estimated cost @$3/1M input: ${:.4}",
        total as f64 * 3.0 / 1_000_000.0
    );
    eprintln!("{}", "=".repeat(70));

    // Full tool surface (registry SSOT incl. full property schemas) — the
    // worst-case opt-in overhead. The default lazy surface is far smaller; see
    // `bench_lazy_default_vs_full_overhead` (#141). Set with deliberate headroom
    // over the current actual so routine tool additions do not trip CI (#290).
    // Raised 12000 -> 13000 for the #496 tool-profile reorg (material jump in
    // the full opt-in surface); the lazy default users actually pay is unaffected.
    // Raised 13000 -> 14000 for #510 (@omar-mohamed-khallaf): the optimized tools
    // schema enriches per-parameter descriptions + WORKFLOW/ANTIPATTERN intents
    // (actual ~13707: instr ~457 + desc ~5498 + schemas ~7752). Output-quality
    // gate confirms NO REGRESSION (eval A/B Δ +0.039). The lazy default surface
    // (bench_lazy_default_vs_full_overhead) is unaffected; cutting the full-
    // surface total is #509 (reduce tool COUNT).
    // Raised 14000 -> 15000 for #982: registering ctx_compare (78 -> 79 tools)
    // lifted the actual to ~14205 (instr ~440 + desc ~5630 + schemas ~8135), over
    // the previous 14000 ceiling whose headroom had shrunk below one tool. Per
    // #290 the ceiling must carry "a tool or two" of slack over the actual, so
    // 15000 (actual ~14205) restores that buffer.
    // Raised 15000 -> 16000 for #1082: registering ctx_quality (80 -> 81 tools)
    // lifted the actual to ~15116 (instr ~502 + desc ~5905 + schemas ~8709), over
    // the previous 15000 ceiling. Per #290 the ceiling must carry "a tool or two"
    // of slack over the actual, so 16000 (actual ~15116, ~2 tools headroom)
    // restores that buffer. The lazy default surface users actually pay is
    // unaffected (bench_lazy_default_vs_full_overhead).
    assert!(
        total < 16000,
        "Total input overhead should be <16000 tokens, got {total}"
    );
}

#[test]
fn bench_lazy_default_vs_full_overhead() {
    // This benchmark must be hermetic: instructions can inject session/memory blocks
    // unless minimal overhead is enforced and the data dir is isolated.
    let _lock = lean_ctx::core::data_dir::test_env_lock();
    let tmp = tempfile::tempdir().expect("tempdir");

    let prev_data_dir = std::env::var("LEAN_CTX_DATA_DIR").ok();
    let prev_minimal = std::env::var("LEAN_CTX_MINIMAL").ok();

    // TODO: Audit that the environment access only happens in single-threaded code.
    unsafe { std::env::set_var("LEAN_CTX_DATA_DIR", tmp.path()) };
    // TODO: Audit that the environment access only happens in single-threaded code.
    unsafe { std::env::set_var("LEAN_CTX_MINIMAL", "1") };

    let lazy_tools = lean_ctx::tool_defs::lazy_tool_defs();
    let full_tools = lean_ctx::tool_defs::granular_tool_defs();

    let tool_tokens = |tools: &[rmcp::model::Tool]| -> (usize, usize) {
        let desc: usize = tools
            .iter()
            .map(|t| {
                t.description
                    .as_ref()
                    .map_or(0, |d| count_tokens(d.as_ref()))
            })
            .sum();
        let schema: usize = tools
            .iter()
            .map(|t| count_tokens(&serde_json::to_string(&t.input_schema).unwrap_or_default()))
            .sum();
        (desc, schema)
    };

    let (lazy_desc_tokens, lazy_schema_tokens) = tool_tokens(&lazy_tools);
    let lazy_total = lazy_desc_tokens + lazy_schema_tokens;

    let (full_desc_tokens, full_schema_tokens) = tool_tokens(&full_tools);
    let full_total = full_desc_tokens + full_schema_tokens;
    let _ = (full_desc_tokens, full_schema_tokens);

    let instructions = lean_ctx::server::build_instructions_for_test(CrpMode::Off);
    let instr_tokens = count_tokens(&instructions);

    let lazy_user_overhead = instr_tokens + lazy_total;
    let full_user_overhead = instr_tokens + full_total;
    let reduction_pct = (full_total - lazy_total) as f64 / full_total as f64 * 100.0;

    eprintln!("\n{}", "=".repeat(70));
    eprintln!("  LAZY (DEFAULT) vs FULL TOOL OVERHEAD");
    eprintln!("{}", "=".repeat(70));
    eprintln!(
        "  Lazy tools:   {:>3} tools, {:>5} tokens (desc+schema)",
        lazy_tools.len(),
        lazy_total
    );
    eprintln!(
        "  Full tools:   {:>3} tools, {:>5} tokens (desc+schema)",
        full_tools.len(),
        full_total
    );
    eprintln!("  Instructions:          {instr_tokens:>5} tokens");
    eprintln!("  {}", "-".repeat(50));
    eprintln!("  User overhead (LAZY DEFAULT):  {lazy_user_overhead:>5} tokens");
    eprintln!("  User overhead (FULL opt-in):   {full_user_overhead:>5} tokens");
    eprintln!("  Tool token reduction:          {reduction_pct:>5.1}%");
    eprintln!("{}", "=".repeat(70));

    // Lazy mode exposes exactly the curated core set. Bound it to the canonical
    // CORE_TOOL_NAMES length so it tracks the SSOT instead of a magic number
    // (#141: the prior `<=12` reflected a granular list that had drifted to omit
    // one core tool).
    assert!(
        lazy_tools.len() <= lean_ctx::tool_defs::core_tool_names().len(),
        "Lazy mode should expose <= core_tool_names() ({}), got {}",
        lean_ctx::tool_defs::core_tool_names().len(),
        lazy_tools.len()
    );
    // Real default overhead: core tools carry their full registry schemas
    // (uncompressed) plus descriptions, matching what the live server sends
    // (#141).
    assert!(
        lazy_user_overhead < 3800,
        "Lazy default overhead should be <3800 tokens, got {lazy_user_overhead}"
    );
    assert!(
        reduction_pct > 60.0,
        "Tool token reduction should be >60%, got {reduction_pct:.1}%"
    );

    match prev_data_dir {
        // TODO: Audit that the environment access only happens in single-threaded code.
        Some(v) => unsafe { std::env::set_var("LEAN_CTX_DATA_DIR", v) },
        // TODO: Audit that the environment access only happens in single-threaded code.
        None => unsafe { std::env::remove_var("LEAN_CTX_DATA_DIR") },
    }
    match prev_minimal {
        // TODO: Audit that the environment access only happens in single-threaded code.
        Some(v) => unsafe { std::env::set_var("LEAN_CTX_MINIMAL", v) },
        // TODO: Audit that the environment access only happens in single-threaded code.
        None => unsafe { std::env::remove_var("LEAN_CTX_MINIMAL") },
    }
}

#[test]
fn bench_decoder_block_token_count() {
    let block = instruction_decoder_block(false);
    let tokens = count_tokens(&block);

    eprintln!(
        "\n  CEP Decoder Block: {} tokens ({} chars)",
        tokens,
        block.len()
    );
    assert!(
        tokens < 300,
        "Decoder block should be <300 tokens, got {tokens}"
    );
}

// ═══════════════════════════════════════════════════════════════════════════
// SECTION 2: PATTERN COMPRESSION BENCHMARKS — Shell Output Compression
// ═══════════════════════════════════════════════════════════════════════════

#[test]
fn bench_git_all_commands() {
    let scenarios = vec![
        ("git log -p -10", generate_git_log_patch(10), 70.0),
        ("git log -p -50", generate_git_log_patch(50), 80.0),
        ("git log --stat -20", generate_git_log_stat(20), 50.0),
        ("git log -30", generate_git_log_standard(30), 60.0),
        ("git status", generate_git_status(), 30.0),
        ("git diff", generate_git_diff(15), 5.0),
        (
            "git commit -m 'feat'",
            generate_git_commit_with_hooks(30),
            0.0, // verbatim: git write-commands are never compressed (daviddatu_ fix)
        ),
        ("git push origin main", generate_git_push(), 0.0), // verbatim
    ];

    print_compression_report("GIT COMMANDS", &scenarios);
}

#[test]
fn bench_cargo_all_commands() {
    let scenarios = vec![
        ("cargo build", generate_cargo_build_success(), 30.0),
        ("cargo build", generate_cargo_build_with_warnings(20), 40.0),
        ("cargo test", generate_cargo_test(50, 2), 40.0),
        ("cargo clippy", generate_cargo_clippy(10), 30.0),
    ];

    print_compression_report("CARGO COMMANDS", &scenarios);
}

#[test]
fn bench_docker_all_commands() {
    let scenarios = vec![
        ("docker ps", generate_docker_ps(10), 0.0), // Verbatim via OutputPolicy
        ("docker images", generate_docker_images(15), 0.0), // Verbatim via OutputPolicy
        ("docker build -t app .", generate_docker_build(20), 40.0),
    ];

    print_compression_report("DOCKER COMMANDS", &scenarios);
}

#[test]
fn bench_npm_all_commands() {
    let scenarios = vec![
        ("npm install", generate_npm_install(30), 0.0), // Verbatim via OutputPolicy (npm install -> Passthrough)
        ("npm test", generate_npm_test_jest(), 30.0),
        ("npm ls", generate_npm_ls(20), 0.0), // Verbatim via OutputPolicy (is_package_manager_info)
    ];

    print_compression_report("NPM COMMANDS", &scenarios);
}

#[test]
fn bench_pip_commands() {
    let scenarios = vec![
        (
            "pip install -r requirements.txt",
            generate_pip_install(15),
            0.0, // Verbatim via OutputPolicy (is_package_manager_info)
        ),
        ("pip list", generate_pip_list(30), 0.0), // Verbatim via OutputPolicy (is_package_manager_info)
    ];

    print_compression_report("PIP COMMANDS", &scenarios);
}

#[test]
fn bench_kubectl_commands() {
    let scenarios = vec![
        ("kubectl get pods", generate_kubectl_pods(20), 0.0),
        ("kubectl get pods -A", generate_kubectl_pods_all(30), 0.0),
    ];

    print_compression_report("KUBECTL COMMANDS", &scenarios);
}

#[test]
fn bench_pattern_coverage_comprehensive() {
    let all = vec![
        ("git log -p -10", generate_git_log_patch(10)),
        ("git log --stat -20", generate_git_log_stat(20)),
        ("git status", generate_git_status()),
        ("git diff", generate_git_diff(15)),
        ("git commit -m 'feat'", generate_git_commit_with_hooks(30)),
        ("cargo build", generate_cargo_build_with_warnings(20)),
        ("cargo test", generate_cargo_test(50, 2)),
        ("cargo clippy", generate_cargo_clippy(10)),
        ("docker ps", generate_docker_ps(10)),
        ("docker build -t app .", generate_docker_build(20)),
        ("npm install", generate_npm_install(30)),
        ("npm test", generate_npm_test_jest()),
        ("pip install -r requirements.txt", generate_pip_install(15)),
        ("kubectl get pods", generate_kubectl_pods(20)),
    ];

    let mut total_orig = 0usize;
    let mut total_comp = 0usize;

    eprintln!("\n{}", "=".repeat(80));
    eprintln!("  COMPREHENSIVE PATTERN COVERAGE — ALL COMMANDS");
    eprintln!("{}", "=".repeat(80));
    eprintln!(
        "  {:<40} {:>8} {:>8} {:>7}",
        "Command", "Original", "Compr.", "Saved%"
    );
    eprintln!("  {}", "-".repeat(65));

    for (cmd, output) in &all {
        let (orig, comp, pct) = measure_pattern(cmd, output);
        total_orig += orig;
        total_comp += comp;
        eprintln!("  {cmd:<40} {orig:>8} {comp:>8} {pct:>6.1}%");
    }

    let total_pct = compression_ratio(total_orig, total_comp);
    eprintln!("  {}", "-".repeat(65));
    eprintln!(
        "  {:<40} {:>8} {:>8} {:>6.1}%",
        "TOTAL", total_orig, total_comp, total_pct
    );
    eprintln!("{}", "=".repeat(80));

    assert!(
        total_pct > 40.0,
        "Overall pattern savings should be >40%, got {total_pct:.1}%"
    );
}

// ═══════════════════════════════════════════════════════════════════════════
// SECTION 3: COMPRESSION MODE BENCHMARKS — ctx_read modes on real-like files
// ═══════════════════════════════════════════════════════════════════════════

#[test]
fn bench_aggressive_mode_rust() {
    let content = generate_rust_file(200);
    let compressed = aggressive_compress(&content, Some("rs"));
    let (orig, comp, pct) = measure_text(&content, &compressed);

    eprintln!("\n  [aggressive mode, Rust 200L] {orig} → {comp} tokens ({pct:.1}% saved)");
    assert!(
        pct > 5.0,
        "Aggressive on Rust should save >5%, got {pct:.1}%"
    );
}

#[test]
fn bench_aggressive_mode_python() {
    let content = generate_python_file(200);
    let compressed = aggressive_compress(&content, Some("py"));
    let (orig, comp, pct) = measure_text(&content, &compressed);

    eprintln!("\n  [aggressive mode, Python 200L] {orig} → {comp} tokens ({pct:.1}% saved)");
    assert!(
        pct > 5.0,
        "Aggressive on Python should save >5%, got {pct:.1}%"
    );
}

#[test]
fn bench_aggressive_mode_typescript() {
    let content = generate_typescript_file(200);
    let compressed = aggressive_compress(&content, Some("ts"));
    let (orig, comp, pct) = measure_text(&content, &compressed);

    eprintln!("\n  [aggressive mode, TypeScript 200L] {orig} → {comp} tokens ({pct:.1}% saved)");
    assert!(
        pct > 15.0,
        "Aggressive on TS should save >15%, got {pct:.1}%"
    );
}

#[test]
fn bench_entropy_mode_rust() {
    let content = generate_rust_file(200);
    let result = entropy_compress(&content);
    let (orig, comp, pct) = measure_text(&content, &result.output);

    eprintln!("\n  [entropy mode, Rust 200L] {orig} → {comp} tokens ({pct:.1}% saved)");
    eprintln!("  Techniques: {:?}", result.techniques);
    assert!(pct > 3.0, "Entropy on Rust should save >3%, got {pct:.1}%");
}

#[test]
fn bench_signatures_mode_rust() {
    let content = generate_rust_file(200);
    let sigs = extract_signatures(&content, "rs");

    let sig_output = sigs.iter().fold(String::new(), |mut s, sig| {
        use std::fmt::Write;
        let _ = writeln!(s, "{}", sig.to_compact());
        s
    });
    let (orig, comp, pct) = measure_text(&content, &sig_output);

    eprintln!(
        "\n  [signatures mode, Rust 200L] {orig} → {comp} tokens ({pct:.1}% saved), {} signatures",
        sigs.len()
    );
    assert!(
        pct > 50.0,
        "Signatures on Rust should save >50%, got {pct:.1}%"
    );
    assert!(
        sigs.len() > 5,
        "Should extract >5 signatures, got {}",
        sigs.len()
    );
}

#[test]
fn bench_signatures_mode_typescript() {
    let content = generate_typescript_file(200);
    let sigs = extract_signatures(&content, "ts");

    let sig_output = sigs.iter().fold(String::new(), |mut s, sig| {
        use std::fmt::Write;
        let _ = writeln!(s, "{}", sig.to_compact());
        s
    });
    let (orig, comp, pct) = measure_text(&content, &sig_output);

    eprintln!(
        "\n  [signatures mode, TS 200L] {orig} → {comp} tokens ({pct:.1}% saved), {} signatures",
        sigs.len()
    );
    assert!(
        pct > 40.0,
        "Signatures on TS should save >40%, got {pct:.1}%"
    );
}

#[test]
fn bench_lightweight_cleanup() {
    let content = generate_rust_file(200);
    let cleaned = lightweight_cleanup(&content);
    let (orig, comp, pct) = measure_text(&content, &cleaned);

    eprintln!("\n  [lightweight cleanup, Rust 200L] {orig} → {comp} tokens ({pct:.1}% saved)");
    assert!(comp <= orig, "Cleanup should never increase token count");
}

#[test]
fn bench_safeguard_prevents_over_compression() {
    let original = generate_rust_file(100);
    let over_compressed = "fn main() {}";
    let result = safeguard_ratio(&original, over_compressed);

    let orig_tokens = count_tokens(&original);
    let result_tokens = count_tokens(&result);
    let ratio = result_tokens as f64 / orig_tokens as f64;

    eprintln!(
        "\n  [safeguard] Original: {} tokens, Over-compressed: {} tokens, Result: {} tokens, Ratio: {:.2}",
        orig_tokens,
        count_tokens(over_compressed),
        result_tokens,
        ratio
    );
    assert!(
        ratio > 0.05,
        "Safeguard should prevent extreme compression on small outputs, ratio: {ratio:.2}"
    );
}

#[test]
fn bench_all_modes_comparison() {
    let content = generate_rust_file(300);
    let orig_tokens = count_tokens(&content);

    let aggressive = aggressive_compress(&content, Some("rs"));
    let entropy = entropy_compress(&content);
    let sigs = extract_signatures(&content, "rs");
    let sig_text = sigs.iter().fold(String::new(), |mut s, sig| {
        use std::fmt::Write;
        let _ = writeln!(s, "{}", sig.to_compact());
        s
    });
    let cleaned = lightweight_cleanup(&content);

    let modes = vec![
        ("full (baseline)", orig_tokens),
        ("aggressive", count_tokens(&aggressive)),
        ("entropy", count_tokens(&entropy.output)),
        ("signatures", count_tokens(&sig_text)),
        ("lightweight", count_tokens(&cleaned)),
    ];

    eprintln!("\n{}", "=".repeat(70));
    eprintln!("  ALL COMPRESSION MODES — Rust File (300 lines)");
    eprintln!("{}", "=".repeat(70));
    eprintln!("  {:<20} {:>8} {:>7}", "Mode", "Tokens", "Saved%");
    eprintln!("  {}", "-".repeat(40));

    for (name, tokens) in &modes {
        let pct = compression_ratio(orig_tokens, *tokens);
        eprintln!("  {name:<20} {tokens:>8} {pct:>6.1}%");
    }
    eprintln!("{}", "=".repeat(70));
}

// ═══════════════════════════════════════════════════════════════════════════
// SECTION 3.5: RRF EVICTION SCORING
// ═══════════════════════════════════════════════════════════════════════════

#[test]
fn bench_rrf_eviction_vs_legacy() {
    use std::time::{Duration, Instant};

    let now = Instant::now();
    let keys: Vec<String> = (0..10).map(|i| format!("file_{i}.rs")).collect();
    let entries: Vec<lean_ctx::core::cache::CacheEntry> = (0..10)
        .map(|i| {
            let e = lean_ctx::core::cache::CacheEntry::new(
                &format!("content_{i}"),
                format!("hash_{i}"),
                i + 1,
                (i + 1) * 100,
                format!("/file_{i}.rs"),
                None,
            );
            e.set_read_count((10 - i) as u32);
            e.set_last_access(
                now.checked_sub(Duration::from_secs(i as u64))
                    .unwrap_or(now),
            );
            e
        })
        .collect();

    let entry_refs: Vec<(&String, &lean_ctx::core::cache::CacheEntry)> =
        keys.iter().zip(entries.iter()).collect();

    let rrf_scores = lean_ctx::core::cache::eviction_scores_rrf(&entry_refs, now);
    assert_eq!(rrf_scores.len(), 10);

    let mut legacy_scores: Vec<(String, f64)> = entries
        .iter()
        .map(|e| (e.path.clone(), e.eviction_score_legacy(now)))
        .collect();
    let mut rrf_sorted: Vec<(String, f64)> = rrf_scores;
    rrf_sorted.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap());
    legacy_scores.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap());

    eprintln!("\n{}", "=".repeat(70));
    eprintln!("  RRF vs LEGACY EVICTION SCORING (10 entries)");
    eprintln!("{}", "=".repeat(70));
    eprintln!(
        "  {:>15} {:>12} {:>12}",
        "File", "RRF Score", "Legacy Score"
    );
    for i in 0..rrf_sorted.len().min(5) {
        let rrf_path = &rrf_sorted[i].0;
        let legacy_path = &legacy_scores[i].0;
        eprintln!(
            "  RRF#{}: {:>8} {:.6}  | Legacy#{}: {:>8} {:.6}",
            i + 1,
            rrf_path,
            rrf_sorted[i].1,
            i + 1,
            legacy_path,
            legacy_scores[i].1
        );
    }
    eprintln!("{}", "=".repeat(70));

    assert!(
        rrf_sorted[0].1 > rrf_sorted[9].1,
        "RRF: highest-scoring entry must rank above lowest"
    );
}

#[test]
fn bench_rrf_eviction_handles_single_entry() {
    use std::time::Instant;

    let now = Instant::now();
    let key = "solo.rs".to_string();
    let entry = lean_ctx::core::cache::CacheEntry::new(
        "single",
        "h".to_string(),
        1,
        50,
        "/solo.rs".to_string(),
        None,
    );

    let refs: Vec<(&String, &lean_ctx::core::cache::CacheEntry)> = vec![(&key, &entry)];
    let scores = lean_ctx::core::cache::eviction_scores_rrf(&refs, now);
    assert_eq!(scores.len(), 1);
    assert!(
        scores[0].1 > 0.0,
        "single entry must have positive RRF score"
    );
}

#[test]
fn bench_rrf_eviction_empty() {
    use std::time::Instant;
    let now = Instant::now();
    let scores = lean_ctx::core::cache::eviction_scores_rrf(&[], now);
    assert!(scores.is_empty());
}

// ═══════════════════════════════════════════════════════════════════════════
// SECTION 4: OUTPUT TOKEN BENCHMARKS — Response Compression
// ═══════════════════════════════════════════════════════════════════════════

#[test]
fn bench_response_compression_off() {
    let verbose_response = generate_verbose_llm_response();
    let compressed = ctx_response::handle(&verbose_response, CrpMode::Off);
    let (orig, comp, pct) = measure_text(&verbose_response, &compressed);

    eprintln!("\n  [ctx_response CRP Off] {orig} → {comp} tokens ({pct:.1}% saved)");
    assert!(
        pct > 5.0,
        "Even Off mode should strip some filler, got {pct:.1}%"
    );
}

#[test]
fn bench_response_compression_compact() {
    let verbose_response = generate_verbose_llm_response();
    let compressed = ctx_response::handle(&verbose_response, CrpMode::Compact);
    let (orig, comp, pct) = measure_text(&verbose_response, &compressed);

    eprintln!("\n  [ctx_response CRP Compact] {orig} → {comp} tokens ({pct:.1}% saved)");
    assert!(pct > 10.0, "Compact mode should save >10%, got {pct:.1}%");
}

#[test]
fn bench_response_compression_tdd() {
    let verbose_response = generate_verbose_llm_response();
    let compressed = ctx_response::handle(&verbose_response, CrpMode::Tdd);
    let (orig, comp, pct) = measure_text(&verbose_response, &compressed);

    eprintln!("\n  [ctx_response CRP TDD] {orig} → {comp} tokens ({pct:.1}% saved)");
    assert!(pct > 15.0, "TDD mode should save >15%, got {pct:.1}%");
}

#[test]
fn bench_response_filler_removal_accuracy() {
    let response_with_fillers = "\
Sure, I'd be happy to help you with that! Let me take a look at this for you.\n\n\
Great question! Here's what I found after analyzing the codebase thoroughly:\n\n\
I'll now walk you through the changes step by step so you can understand each modification.\n\n\
First, let me explain how the authentication flow works in the current implementation.\n\n\
The function `process_data` in `src/handlers.rs` has a critical bug on line 42 where the \
validation logic doesn't properly handle edge cases with expired tokens.\n\n\
```rust\npub fn process_data(input: &str) -> Result<Output, Error> {\n    let validated = validate(input)?;\n    Ok(Output::new(validated))\n}\n```\n\n\
As you can see, the function currently doesn't check for token expiration before processing.\n\n\
I've also updated the tests to cover the new edge cases. The test file now includes comprehensive \
coverage for both valid and invalid token scenarios.\n\n\
```rust\n#[test]\nfn test_process_data_valid() {\n    let result = process_data(\"hello\");\n    assert!(result.is_ok());\n}\n\n\
#[test]\nfn test_process_data_expired() {\n    let result = process_data(\"expired_token\");\n    assert!(result.is_err());\n}\n```\n\n\
Moving on to the next part, I also refactored the configuration module to improve maintainability.\n\n\
I hope this helps! Let me know if you have any other questions or if you'd like me to make any \
additional changes to the implementation. Feel free to ask if anything is unclear.\n\n\
Don't hesitate to reach out if you need further assistance with this or any other issues.";

    let result_off = ctx_response::handle(response_with_fillers, CrpMode::Off);
    let result_tdd = ctx_response::handle(response_with_fillers, CrpMode::Tdd);

    let orig_tokens = count_tokens(response_with_fillers);
    let off_tokens = count_tokens(&result_off);
    let tdd_tokens = count_tokens(&result_tdd);

    let off_savings = compression_ratio(orig_tokens, off_tokens);
    let tdd_savings = compression_ratio(orig_tokens, tdd_tokens);

    eprintln!("\n  [filler removal in context]");
    eprintln!("    Original:    {orig_tokens} tokens");
    eprintln!("    CRP Off:     {off_tokens} tokens ({off_savings:.1}% saved)");
    eprintln!("    CRP TDD:     {tdd_tokens} tokens ({tdd_savings:.1}% saved)");

    assert!(
        off_savings > 5.0,
        "Filler removal (Off mode) should save >5%, got {off_savings:.1}%"
    );
    assert!(
        tdd_tokens <= off_tokens,
        "TDD should compress at least as much as Off mode"
    );
}

// ═══════════════════════════════════════════════════════════════════════════
// SECTION 5: THINKING TOKEN BENCHMARKS — CRP/TDD Instruction Effectiveness
// ═══════════════════════════════════════════════════════════════════════════

#[test]
fn bench_thinking_reduction_cues_present() {
    let compact = lean_ctx::server::build_instructions_for_test(CrpMode::Compact);
    let tdd = lean_ctx::server::build_instructions_for_test(CrpMode::Tdd);

    // #579 condensed the instruction skeleton: efficiency cues now live in the
    // one-line `CRP MODE:` suffix (lowercase), not a standalone block.
    let compact_lc = compact.to_lowercase();
    let tdd_lc = tdd.to_lowercase();
    assert!(
        compact_lc.contains("trust tool outputs") || compact_lc.contains("output efficiency"),
        "Compact mode must contain output efficiency cue"
    );
    assert!(
        compact.contains("<=200 tok")
            || compact.contains("<=200 tokens")
            || compact.contains("≤200"),
        "Compact mode must contain token budget"
    );
    assert!(
        tdd_lc.contains("max density") || tdd_lc.contains("output efficiency"),
        "TDD mode must contain output efficiency cue"
    );
    assert!(
        tdd.contains("<=150 tok") || tdd.contains("<=150 tokens") || tdd.contains("≤150"),
        "TDD mode must contain strict token budget"
    );
    assert!(
        tdd_lc.contains("zero narration"),
        "TDD mode must contain zero-narration rule"
    );

    eprintln!("\n  [output efficiency] All cues present in CRP Compact and TDD modes ✓");
}

#[test]
fn bench_crp_mode_token_budgets() {
    let compact = lean_ctx::server::build_instructions_for_test(CrpMode::Compact);
    let tdd = lean_ctx::server::build_instructions_for_test(CrpMode::Tdd);

    let compact_budget_present = compact.contains("<=200 tok")
        || compact.contains("TARGET: <=200 tokens")
        || compact.contains("≤200");
    let tdd_budget_present = tdd.contains("<=150 tok")
        || tdd.contains("TOKEN BUDGET: <=150 tokens")
        || tdd.contains("BUDGET: <=150");

    eprintln!("\n  [token budgets]");
    eprintln!(
        "    Compact ≤200 tokens budget: {}",
        if compact_budget_present { "✓" } else { "✗" }
    );
    eprintln!(
        "    TDD ≤150 tokens budget:     {}",
        if tdd_budget_present { "✓" } else { "✗" }
    );

    assert!(
        compact_budget_present,
        "Compact mode must specify ≤200 token budget"
    );
    assert!(
        tdd_budget_present,
        "TDD mode must specify ≤150 token budget"
    );
}

#[test]
fn bench_tdd_symbols_token_efficiency() {
    let prose = "I added a new function called process_data to the module handlers in file server.rs at line 42. \
                 Then I removed lines 10 through 15 from the file because they were no longer needed. \
                 I also modified the function validate_token and renamed it to verify_jwt for clarity. \
                 The operation completed successfully with no warnings or errors detected.";

    let tdd = "+F1:42 fn process_data(handlers)\n\
               -F1:10-15\n\
               ~F1:42 validate_token -> verify_jwt\n\
               ok 0w";

    let prose_tokens = count_tokens(prose);
    let tdd_tokens = count_tokens(tdd);
    let savings_pct = compression_ratio(prose_tokens, tdd_tokens);

    eprintln!(
        "\n  [TDD symbols] Prose: {prose_tokens} tokens → TDD: {tdd_tokens} tokens ({savings_pct:.1}% saved)"
    );
    eprintln!(
        "  Semantic density: {:.1}x more meaning per token",
        prose_tokens as f64 / tdd_tokens as f64
    );
    assert!(
        savings_pct > 40.0,
        "TDD notation should save >40% vs prose, got {savings_pct:.1}%"
    );
}

// SECTION 6: PERFORMANCE BENCHMARKS removed — machine-dependent timing
// thresholds are unsuitable for OSS CI. Use `lean-ctx benchmark` CLI instead.

// ═══════════════════════════════════════════════════════════════════════════
// SECTION 7: REGRESSION GUARDS — Ensure no performance degradation
// ═══════════════════════════════════════════════════════════════════════════

#[test]
fn guard_no_inflation_aggressive() {
    for size in [50, 100, 200, 500] {
        let content = generate_rust_file(size);
        let compressed = aggressive_compress(&content, Some("rs"));
        let orig = count_tokens(&content);
        let comp = count_tokens(&compressed);
        assert!(
            comp <= orig,
            "Aggressive should never inflate! Size {size}: {orig} → {comp}"
        );
    }
    eprintln!("\n  [guard] Aggressive mode never inflates output ✓");
}

#[test]
fn guard_no_inflation_lightweight() {
    for size in [50, 100, 200, 500] {
        let content = generate_rust_file(size);
        let cleaned = lightweight_cleanup(&content);
        let orig = count_tokens(&content);
        let comp = count_tokens(&cleaned);
        assert!(
            comp <= orig,
            "Lightweight should never inflate! Size {size}: {orig} → {comp}"
        );
    }
    eprintln!("\n  [guard] Lightweight cleanup never inflates output ✓");
}

#[test]
fn guard_pattern_never_inflates() {
    let commands: Vec<(&str, String)> = vec![
        ("git log -p -5", generate_git_log_patch(5)),
        ("git status", generate_git_status()),
        ("cargo build", generate_cargo_build_success()),
        ("docker ps", generate_docker_ps(5)),
        ("npm install", generate_npm_install(10)),
    ];

    for (cmd, output) in &commands {
        if let Some(compressed) = lean_ctx::core::patterns::compress_output(cmd, output) {
            let orig = count_tokens(output);
            let comp = count_tokens(&compressed);
            assert!(
                comp <= orig,
                "Pattern '{cmd}' should never inflate: {orig} → {comp}"
            );
        }
    }
    eprintln!("\n  [guard] Pattern compression never inflates output ✓");
}

#[test]
fn guard_tool_descriptions_not_empty() {
    let descs = lean_ctx::server::tool_descriptions_for_test();
    for (name, desc) in &descs {
        assert!(
            !desc.is_empty(),
            "Tool '{name}' must have a non-empty description"
        );
        assert!(
            desc.len() > 10,
            "Tool '{name}' description too short ({} chars): '{desc}'",
            desc.len()
        );
    }
    eprintln!(
        "\n  [guard] All {} tools have meaningful descriptions ✓",
        descs.len()
    );
}

#[test]
fn guard_essential_instructions_present() {
    let instr = lean_ctx::server::build_instructions_for_test(CrpMode::Off);

    // #579: the static skeleton is capped at ≤400 tokens — workflow tools
    // (ctx_overview, ctx_compress) moved to the on-demand LEAN-CTX.md doc.
    // #496: the per-session skeleton (Bare profile) stays lean — it anchors the
    // mandatory tool mapping + the intent playbook. The CEP protocol, AUTO hints
    // and the LEAN-CTX.md reference now live in the dedicated rule file /
    // CLAUDE.md (FULL profile) that the agent loads alongside, keeping the
    // worst-case per-session MCP instructions under the 2048-char Claude cap.
    let required = vec![
        "ALWAYS use lean-ctx ctx_* tools",
        "ctx_read",
        "ctx_shell",
        "ctx_search",
        "ctx_tree",
        "ctx_compose",
        "ctx_session",
    ];

    for keyword in &required {
        assert!(
            instr.contains(keyword),
            "System instructions must contain '{keyword}'"
        );
    }
    eprintln!(
        "\n  [guard] All {} essential instruction keywords present ✓",
        required.len()
    );
}

// AUTONOMY TOKEN IMPACT removed — writes to ~/.lean-ctx/ (not hermetic for CI)

// ═══════════════════════════════════════════════════════════════════════════
// DATA GENERATORS
// ═══════════════════════════════════════════════════════════════════════════

fn print_compression_report(title: &str, scenarios: &[(&str, String, f64)]) {
    let mut total_orig = 0usize;
    let mut total_comp = 0usize;

    eprintln!("\n{}", "=".repeat(70));
    eprintln!("  {title}");
    eprintln!("{}", "=".repeat(70));
    eprintln!(
        "  {:<35} {:>8} {:>8} {:>7}",
        "Command", "Original", "Compr.", "Saved%"
    );
    eprintln!("  {}", "-".repeat(60));

    for (cmd, output, min_savings) in scenarios {
        let (orig, comp, pct) = measure_pattern(cmd, output);
        total_orig += orig;
        total_comp += comp;
        let status = if pct >= *min_savings { "✓" } else { "✗" };
        eprintln!("  {cmd:<35} {orig:>8} {comp:>8} {pct:>6.1}% {status}");
        assert!(
            pct >= *min_savings,
            "'{cmd}' should save ≥{min_savings}%, got {pct:.1}%"
        );
    }

    let total_pct = compression_ratio(total_orig, total_comp);
    eprintln!("  {}", "-".repeat(60));
    eprintln!(
        "  {:<35} {:>8} {:>8} {:>6.1}%",
        "TOTAL", total_orig, total_comp, total_pct
    );
    eprintln!("{}", "=".repeat(70));
}

fn generate_rust_file(lines: usize) -> String {
    let mut s = String::new();
    s.push_str("use std::collections::HashMap;\nuse std::sync::Arc;\n\n");
    s.push_str("/// Main application struct that handles all processing\n");
    s.push_str("pub struct Application {\n    config: Config,\n    state: Arc<State>,\n}\n\n");

    let mut line = 9;
    while line < lines {
        let fn_idx = line / 15;
        s.push_str(&format!(
            "/// Process item {fn_idx} with validation and error handling\n\
             pub fn process_item_{fn_idx}(input: &str, config: &Config) -> Result<Output, Error> {{\n\
             \tlet validated = validate_input(input)?;\n\
             \tlet transformed = transform_data(&validated, config);\n\
             \t// Apply business rules\n\
             \tif transformed.is_empty() {{\n\
             \t\treturn Err(Error::EmptyInput);\n\
             \t}}\n\
             \tlet result = compute_result(&transformed)?;\n\
             \tOk(Output::new(result))\n\
             }}\n\n"
        ));
        line += 13;
    }
    s
}

fn generate_python_file(lines: usize) -> String {
    let mut s = String::new();
    s.push_str("import os\nimport sys\nfrom typing import Dict, List, Optional\n\n");
    s.push_str("# Main service class for data processing\n");
    s.push_str("class DataService:\n    \"\"\"Service for processing data pipelines.\"\"\"\n\n");

    let mut line = 8;
    while line < lines {
        let fn_idx = line / 12;
        s.push_str(&format!(
            "    def process_batch_{fn_idx}(self, items: List[Dict], config: Optional[Dict] = None) -> List[Dict]:\n\
             \t\t\"\"\"Process batch {fn_idx} with validation.\"\"\"\n\
             \t\t# Validate inputs\n\
             \t\tvalidated = [self._validate(item) for item in items]\n\
             \t\tresults = []\n\
             \t\tfor item in validated:\n\
             \t\t\tif item is not None:\n\
             \t\t\t\tresults.append(self._transform(item))\n\
             \t\treturn results\n\n"
        ));
        line += 12;
    }
    s
}

fn generate_typescript_file(lines: usize) -> String {
    let mut s = String::new();
    s.push_str(
        "import { Request, Response } from 'express';\nimport { Database } from './db';\n\n",
    );
    s.push_str("// API controller for user management\n");
    s.push_str("export interface UserPayload {\n  name: string;\n  email: string;\n  role: 'admin' | 'user';\n}\n\n");

    let mut line = 9;
    while line < lines {
        let fn_idx = line / 14;
        s.push_str(&format!(
            "/**\n * Handle user request {fn_idx}\n */\n\
             export async function handleUser{fn_idx}(req: Request, res: Response): Promise<void> {{\n\
             \tconst {{ name, email }} = req.body as UserPayload;\n\
             \t// Validate the payload\n\
             \tif (!name || !email) {{\n\
             \t\tres.status(400).json({{ error: 'Missing fields' }});\n\
             \t\treturn;\n\
             \t}}\n\
             \tconst result = await Database.upsert({{ name, email }});\n\
             \tres.json({{ success: true, data: result }});\n\
             }}\n\n"
        ));
        line += 14;
    }
    s
}

fn generate_verbose_llm_response() -> String {
    "Sure, I'd be happy to help you with that! Let me take a look at the code.\n\n\
     Great question! Here's what I found after analyzing the codebase:\n\n\
     I'll now walk you through the changes step by step.\n\n\
     First, let me read the relevant files to understand the current state of things.\n\n\
     The function `process_data` in `src/handlers.rs` needs to be updated. Here are the changes:\n\n\
     ```rust\n\
     pub fn process_data(input: &str) -> Result<Output, Error> {\n\
         let validated = validate(input)?;\n\
         Ok(Output::new(validated))\n\
     }\n\
     ```\n\n\
     I've also updated the tests to cover the new edge cases. The test file now includes:\n\n\
     ```rust\n\
     #[test]\n\
     fn test_process_data() {\n\
         let result = process_data(\"hello\");\n\
         assert!(result.is_ok());\n\
     }\n\
     ```\n\n\
     I hope this helps! Let me know if you have any other questions or if you'd like me to make any additional changes.\n\n\
     Is there anything else I can assist you with?"
        .to_string()
}

fn generate_git_status() -> String {
    let mut s = String::from(
        "On branch feature/new-auth\nYour branch is ahead of 'origin/feature/new-auth' by 3 commits.\n  (use \"git push\" to publish your local commits)\n\nChanges to be committed:\n  (use \"git restore --staged <file>...\" to unstage)\n",
    );
    for i in 0..8 {
        s.push_str(&format!("\tmodified:   src/auth/handler_{i}.rs\n"));
    }
    s.push_str("\tnew file:   src/auth/middleware.rs\n\trenamed:    src/old.rs -> src/new.rs\n");
    s.push_str("\nChanges not staged for commit:\n  (use \"git add <file>...\" to update what will be committed)\n");
    for i in 0..5 {
        s.push_str(&format!("\tmodified:   tests/test_{i}.rs\n"));
    }
    s.push_str("\nUntracked files:\n  (use \"git add <file>...\" to include in what will be committed)\n\ttmp/debug.log\n\tnotes.md\n");
    s
}

fn generate_git_diff(files: usize) -> String {
    let mut s = String::new();
    for i in 0..files {
        s.push_str(&format!(
            "diff --git a/src/module_{i}.rs b/src/module_{i}.rs\n\
             index abc1234..def5678 100644\n\
             --- a/src/module_{i}.rs\n\
             +++ b/src/module_{i}.rs\n\
             @@ -10,8 +10,12 @@ fn existing_function() {{\n\
              \tlet x = 1;\n\
              \tlet y = 2;\n\
             -\tlet old_value = compute_old(x, y);\n\
             -\treturn old_value;\n\
             +\tlet new_value = compute_new(x, y);\n\
             +\t// Added validation\n\
             +\tif new_value > 0 {{\n\
             +\t\treturn Ok(new_value);\n\
             +\t}}\n\
             +\tErr(Error::Invalid)\n\
              }}\n\n"
        ));
    }
    s
}

fn generate_git_commit_with_hooks(hook_count: usize) -> String {
    let mut s = String::new();
    for i in 0..hook_count {
        s.push_str(&format!("check-{i:02}...........................passed\n"));
    }
    s.push_str("[feature/auth abc1234] feat: implement JWT authentication\n");
    s.push_str(" 8 files changed, 234 insertions(+), 45 deletions(-)\n");
    s.push_str(" create mode 100644 src/auth/jwt.rs\n");
    s.push_str(" create mode 100644 src/auth/middleware.rs\n");
    s
}

fn generate_git_push() -> String {
    "Enumerating objects: 15, done.\n\
     Counting objects: 100% (15/15), done.\n\
     Delta compression using up to 10 threads\n\
     Compressing objects: 100% (8/8), done.\n\
     Writing objects: 100% (9/9), 2.34 KiB | 2.34 MiB/s, done.\n\
     Total 9 (delta 5), reused 0 (delta 0), pack-reused 0\n\
     remote: Resolving deltas: 100% (5/5), completed with 3 local objects.\n\
     To github.com:user/repo.git\n\
        abc1234..def5678  main -> main\n"
        .to_string()
}

fn generate_cargo_build_success() -> String {
    "   Compiling serde v1.0.197\n\
     Compiling serde_json v1.0.115\n\
     Compiling tokio v1.37.0\n\
     Compiling my-app v0.1.0 (/home/user/project)\n\
     Finished `dev` profile [unoptimized + debuginfo] target(s) in 12.34s\n"
        .to_string()
}

fn generate_cargo_build_with_warnings(count: usize) -> String {
    let mut s = String::from("   Compiling my-app v0.1.0 (/home/user/project)\n");
    for i in 0..count {
        s.push_str(&format!(
            "warning: unused variable: `temp_{i}`\n\
              --> src/handlers/mod.rs:{}:9\n\
               |\n\
             {} |     let temp_{i} = calculate();\n\
               |         ^^^^^^^ help: if this is intentional, prefix it with an underscore: `_temp_{i}`\n\
               |\n",
            100 + i * 5,
            100 + i * 5
        ));
    }
    s.push_str(&format!(
        "warning: `my-app` (bin \"my-app\") generated {count} warnings\n\
         Finished `dev` profile [unoptimized + debuginfo] target(s) in 3.45s\n"
    ));
    s
}

fn generate_cargo_test(pass: usize, fail: usize) -> String {
    let mut s = String::from("\nrunning tests\n");
    for i in 0..pass {
        s.push_str(&format!("test tests::test_case_{i} ... ok\n"));
    }
    for i in 0..fail {
        s.push_str(&format!("test tests::test_failing_{i} ... FAILED\n"));
    }
    s.push_str(&format!(
        "\ntest result: {}. {} passed; {} failed; 0 ignored; 0 measured; 0 filtered out; finished in 1.23s\n",
        if fail > 0 { "FAILED" } else { "ok" },
        pass,
        fail
    ));
    s
}

fn generate_cargo_clippy(count: usize) -> String {
    let mut s = String::from("    Checking my-app v0.1.0\n");
    for i in 0..count {
        s.push_str(&format!(
            "warning: redundant clone\n\
              --> src/handler_{i}.rs:{}:14\n\
               |\n\
             {} |     data.clone()\n\
               |          ^^^^^^^^ help: remove this\n\
               = note: `#[warn(clippy::redundant_clone)]` on by default\n\n",
            20 + i * 3,
            20 + i * 3
        ));
    }
    s.push_str(&format!(
        "warning: `my-app` (bin \"my-app\") generated {count} warnings\n"
    ));
    s
}

fn generate_docker_ps(count: usize) -> String {
    let mut s = String::from(
        "CONTAINER ID   IMAGE                    COMMAND                  CREATED        STATUS        PORTS                    NAMES\n",
    );
    for i in 0..count {
        s.push_str(&format!(
            "abc{i:04}def   nginx:1.25-alpine        \"nginx -g 'daemon of…\"   {} hours ago   Up {} hours   0.0.0.0:80{}->80/tcp     web-{i}\n",
            2 + i,
            2 + i,
            80 + i
        ));
    }
    s
}

fn generate_docker_images(count: usize) -> String {
    let mut s = String::from("REPOSITORY          TAG       IMAGE ID       CREATED        SIZE\n");
    for i in 0..count {
        s.push_str(&format!(
            "myapp-{i}            latest    sha256:abc{i:04}   {} days ago    {}MB\n",
            1 + i,
            50 + i * 30
        ));
    }
    s
}

fn generate_docker_build(steps: usize) -> String {
    let mut s = String::new();
    for i in 1..=steps {
        s.push_str(&format!(
            "[{i}/{steps}] RUN apt-get install -y package-{i} && rm -rf /var/lib/apt/lists/*\n"
        ));
        s.push_str(&format!(
            " ---> Running in abc{i:04}def\n\
              ---> sha256:abc{i:05}def\n"
        ));
    }
    s.push_str("Successfully built sha256:final123456\nSuccessfully tagged myapp:latest\n");
    s
}

fn generate_npm_install(count: usize) -> String {
    let mut s = String::new();
    for i in 0..count {
        s.push_str(&format!(
            "npm warn deprecated package-{i}@1.0.{i}: Use package-{i}@2 instead\n"
        ));
    }
    s.push_str(&format!(
        "\nadded {} packages, and audited {} packages in 5s\n\n\
         {} packages are looking for funding\n  run `npm fund` for details\n\n\
         found 0 vulnerabilities\n",
        count + 50,
        count + 100,
        count / 3
    ));
    s
}

fn generate_npm_test_jest() -> String {
    "PASS src/components/Button.test.tsx\n\
     PASS src/components/Form.test.tsx\n\
     PASS src/utils/api.test.ts\n\
     FAIL src/hooks/useAuth.test.ts\n\
       ● useAuth › should handle token refresh\n\
     \n\
     Test Suites: 1 failed, 3 passed, 4 total\n\
     Tests:       1 failed, 15 passed, 16 total\n\
     Snapshots:   0 total\n\
     Time:        3.456 s\n\
     Ran all test suites.\n"
        .to_string()
}

fn generate_npm_ls(count: usize) -> String {
    let mut s = String::from("my-app@1.0.0 /home/user/project\n");
    for i in 0..count {
        s.push_str(&format!("├── package-{i}@{}.{}.0\n", 1 + i % 5, i % 10));
    }
    s.push_str("└── last-package@1.0.0\n");
    s
}

fn generate_pip_install(count: usize) -> String {
    let mut s = String::new();
    for i in 0..count {
        s.push_str(&format!(
            "Collecting package-{i}>=1.0\n\
             \tDownloading package_{i}-1.{i}.0-cp311-cp311-manylinux_2_17_x86_64.whl ({}kB)\n",
            500 + i * 100
        ));
    }
    s.push_str(&format!(
        "Successfully installed {}\n",
        (0..count)
            .map(|i| format!("package-{i}-1.{i}.0"))
            .collect::<Vec<_>>()
            .join(" ")
    ));
    s
}

fn generate_pip_list(count: usize) -> String {
    let mut s = String::from("Package          Version\n---------------- -------\n");
    for i in 0..count {
        s.push_str(&format!("package-{i:<8} {}.{}.0\n", 1 + i % 5, i % 10));
    }
    s
}

fn generate_kubectl_pods(count: usize) -> String {
    let mut s =
        String::from("NAME                              READY   STATUS    RESTARTS   AGE\n");
    for i in 0..count {
        let status = match i % 5 {
            0..=2 => "Running",
            3 => "Pending",
            _ => "CrashLoopBackOff",
        };
        s.push_str(&format!(
            "app-{i}-deployment-abc{i:04}def   1/1     {:<20} {}          {}d\n",
            status,
            i % 3,
            1 + i
        ));
    }
    s
}

fn generate_kubectl_pods_all(count: usize) -> String {
    let mut s = String::from(
        "NAMESPACE     NAME                              READY   STATUS    RESTARTS   AGE\n",
    );
    let namespaces = ["default", "kube-system", "monitoring", "app-prod"];
    for i in 0..count {
        let ns = namespaces[i % namespaces.len()];
        s.push_str(&format!(
            "{:<13} pod-{i}-abc{i:04}def                1/1     Running   0          {}d\n",
            ns,
            1 + i
        ));
    }
    s
}

fn generate_git_log_patch(n: usize) -> String {
    let mut output = String::new();
    for i in 0..n {
        let msg = format!("implement feature number {i} with improvements");
        output.push_str(&format!(
            "commit {i:07}abc1234567890abcdef1234567890\n\
             Author: Developer <dev@example.com>\n\
             Date:   Mon Mar {d} 10:{h:02}:00 2026 +0100\n\
             \n\
             {ty}: {msg}\n\
             \n\
             diff --git a/src/module_{i}.rs b/src/module_{i}.rs\n\
             index abc1234..def5678 100644\n\
             --- a/src/module_{i}.rs\n\
             +++ b/src/module_{i}.rs\n\
             @@ -1,{ctx} +1,{ctx2} @@\n",
            d = 10 + (i % 20),
            h = i % 24,
            ty = ["feat", "fix", "refactor", "docs", "test"][i % 5],
            ctx = 10 + i,
            ctx2 = 12 + i,
        ));
        for j in 0..(5 + i % 10) {
            output.push_str(&format!(" fn existing_function_{j}() {{}}\n"));
        }
        for j in 0..(3 + i % 5) {
            output.push_str(&format!("+fn new_function_{i}_{j}() {{ todo!() }}\n"));
        }
        for j in 0..(2 + i % 3) {
            output.push_str(&format!(
                "-fn old_function_{i}_{j}() {{ unimplemented!() }}\n"
            ));
        }
        output.push('\n');
    }
    output
}

fn generate_git_log_standard(n: usize) -> String {
    let mut output = String::new();
    for i in 0..n {
        let msg = format!("update component {i} with better error handling");
        output.push_str(&format!(
            "commit {i:07}abc1234567890abcdef1234567890\n\
             Author: Developer <dev@example.com>\n\
             Date:   Mon Mar {d} 10:{h:02}:00 2026 +0100\n\
             \n\
             {ty}: {msg}\n\
             \n\
             This is a longer commit message that describes the changes made.\n\
             It spans multiple lines to be more realistic.\n\
             \n",
            d = 10 + (i % 20),
            h = i % 24,
            ty = ["feat", "fix", "refactor", "docs", "test"][i % 5],
        ));
    }
    output
}

fn generate_git_log_stat(n: usize) -> String {
    let mut output = String::new();
    for i in 0..n {
        let msg = format!("update module {i}");
        output.push_str(&format!(
            "commit {i:07}abc1234567890abcdef1234567890\n\
             Author: Developer <dev@example.com>\n\
             Date:   Mon Mar {d} 10:{h:02}:00 2026 +0100\n\
             \n\
             {ty}: {msg}\n\
             \n",
            d = 10 + (i % 20),
            h = i % 24,
            ty = ["feat", "fix", "refactor", "docs", "test"][i % 5],
        ));
        for j in 0..(2 + i % 4) {
            let plus = 3 + j;
            let minus = 1 + j;
            let bar: String = "+".repeat(plus) + &"-".repeat(minus);
            output.push_str(&format!(
                " src/mod_{i}_{j}.rs | {total} {bar}\n",
                total = plus + minus,
            ));
        }
        let total_files = 2 + i % 4;
        output.push_str(&format!(
            " {total_files} files changed, {} insertions(+), {} deletions(-)\n\n",
            10 + i * 2,
            5 + i,
        ));
    }
    output
}

// ---------------------------------------------------------------------------
// v3.4.1 Optimization benchmarks: Token overhead + Latency
// ---------------------------------------------------------------------------

#[test]
fn bench_minimal_overhead_suppresses_all_meta_strings() {
    // LEAN_CTX_MINIMAL is process-global; serialize against the other instruction
    // benchmarks (cf. bench_lazy_default_vs_full_overhead) so a parallel test can't
    // clear it between set_var and build_instructions_for_test.
    let _lock = lean_ctx::core::data_dir::test_env_lock();
    // TODO: Audit that the environment access only happens in single-threaded code.
    unsafe { std::env::set_var("LEAN_CTX_MINIMAL", "1") };

    let instructions = lean_ctx::server::build_instructions_for_test(CrpMode::Tdd);
    let instr_tokens = count_tokens(&instructions);

    eprintln!("\n{}", "=".repeat(70));
    eprintln!("  MINIMAL OVERHEAD: Instructions Token Count");
    eprintln!("{}", "=".repeat(70));
    eprintln!("  Instructions (minimal):  {instr_tokens:>5} tokens");

    assert!(
        !instructions.contains("--- ACTIVE SESSION"),
        "minimal_overhead should suppress session block"
    );

    // TODO: Audit that the environment access only happens in single-threaded code.
    unsafe { std::env::remove_var("LEAN_CTX_MINIMAL") };

    let full_instructions = lean_ctx::server::build_instructions_for_test(CrpMode::Tdd);
    let full_tokens = count_tokens(&full_instructions);

    eprintln!("  Instructions (full):     {full_tokens:>5} tokens");
    let saved = full_tokens.saturating_sub(instr_tokens);
    eprintln!("  Tokens saved by minimal: {saved:>5}");
    eprintln!("{}", "=".repeat(70));

    assert!(
        instr_tokens <= full_tokens,
        "minimal instructions ({instr_tokens}) should be <= full ({full_tokens})"
    );
}

#[test]
fn bench_hash_fast_vs_full_correctness() {
    use lean_ctx::core::hasher::hash_str;
    use lean_ctx::server::helpers::hash_fast;

    let small = "a".repeat(1000);
    assert_eq!(
        hash_str(&small),
        hash_fast(&small),
        "hash_fast must match hash_str for small strings"
    );

    let exactly_16k = "x".repeat(16 * 1024);
    assert_eq!(
        hash_str(&exactly_16k),
        hash_fast(&exactly_16k),
        "hash_fast must match hash_str at the 16KB boundary"
    );

    let large = "b".repeat(100_000);
    let fast_hash = hash_fast(&large);
    assert_eq!(
        fast_hash.len(),
        64,
        "hash_fast should produce valid BLAKE3 hex"
    );
    assert_ne!(
        fast_hash,
        hash_fast(&"c".repeat(100_000)),
        "different large strings should produce different hashes"
    );

    eprintln!("\n{}", "=".repeat(70));
    eprintln!("  BLAKE3 FAST FINGERPRINT");
    eprintln!("{}", "=".repeat(70));
    let start_full = std::time::Instant::now();
    for _ in 0..100 {
        let _ = hash_str(&large);
    }
    let full_us = start_full.elapsed().as_micros();

    let start_fast = std::time::Instant::now();
    for _ in 0..100 {
        let _ = hash_fast(&large);
    }
    let fast_us = start_fast.elapsed().as_micros();

    let speedup = full_us as f64 / fast_us.max(1) as f64;
    eprintln!("  100x hash_str(100KB):     {full_us:>6} us");
    eprintln!("  100x hash_fast(100KB):    {fast_us:>6} us");
    eprintln!("  Speedup:                  {speedup:>6.1}x");
    eprintln!("{}", "=".repeat(70));

    assert!(
        speedup > 1.5,
        "hash_fast should be faster for 100KB, got {speedup:.1}x"
    );
}

#[test]
fn bench_terse_pipeline_compression() {
    use lean_ctx::core::config::CompressionLevel;
    use lean_ctx::core::terse;

    let input = "fn main() {\n    println!(\"hello\");\n}\n".repeat(500);

    let start_standard = std::time::Instant::now();
    for _ in 0..100 {
        let _ = terse::pipeline::compress(&input, &CompressionLevel::Standard, None);
    }
    let standard_us = start_standard.elapsed().as_micros();

    let start_off = std::time::Instant::now();
    for _ in 0..100 {
        let result = terse::pipeline::compress(&input, &CompressionLevel::Off, None);
        assert_eq!(result.output.len(), input.len());
    }
    let off_us = start_off.elapsed().as_micros();

    eprintln!("\n{}", "=".repeat(70));
    eprintln!("  TERSE PIPELINE BENCHMARK (100 iters, ~20KB input)");
    eprintln!("{}", "=".repeat(70));
    eprintln!("  Off (passthrough):  {off_us:>6} us");
    eprintln!("  Standard (4-layer): {standard_us:>6} us");
    eprintln!("{}", "=".repeat(70));
}

#[test]
fn bench_count_tokens_cache_effectiveness() {
    let text = "The quick brown fox jumps over the lazy dog. ".repeat(200);

    let start_cold = std::time::Instant::now();
    let t1 = count_tokens(&text);
    let cold_us = start_cold.elapsed().as_micros();

    let start_cached = std::time::Instant::now();
    let t2 = count_tokens(&text);
    let cached_us = start_cached.elapsed().as_micros();

    assert_eq!(t1, t2, "cached result must match");

    let slightly_different = format!("{text}X");
    let start_miss = std::time::Instant::now();
    let t3 = count_tokens(&slightly_different);
    let miss_us = start_miss.elapsed().as_micros();

    eprintln!("\n{}", "=".repeat(70));
    eprintln!("  COUNT_TOKENS CACHE BENCHMARK (~9KB input)");
    eprintln!("{}", "=".repeat(70));
    eprintln!("  Cold (first call):  {cold_us:>6} us  -> {t1} tokens");
    eprintln!("  Cached (same text): {cached_us:>6} us  -> {t2} tokens");
    eprintln!("  Miss (diff text):   {miss_us:>6} us  -> {t3} tokens");
    if cold_us > 0 {
        let speedup = cold_us as f64 / cached_us.max(1) as f64;
        eprintln!("  Cache speedup:      {speedup:>6.0}x");
    }
    eprintln!("{}", "=".repeat(70));

    assert!(
        cached_us <= cold_us + 5,
        "cached call should be faster than cold call"
    );
}

#[test]
fn bench_session_prepare_save_is_cpu_only() {
    use lean_ctx::core::session::SessionState;

    let mut session = SessionState::new();
    session.set_task("benchmark task for save split", None);
    for _ in 0..20 {
        session.record_tool_call(100, 200);
        session.stats.unsaved_changes = 0;
    }

    let start = std::time::Instant::now();
    for _ in 0..100 {
        session.stats.unsaved_changes = 5;
        if let Ok(prepared) = session.prepare_save() {
            std::mem::drop(prepared);
        }
    }
    let prepare_us = start.elapsed().as_micros();

    eprintln!("\n{}", "=".repeat(70));
    eprintln!("  SESSION PREPARE_SAVE BENCHMARK (100 iters)");
    eprintln!("{}", "=".repeat(70));
    eprintln!("  100x prepare_save (serialize only): {prepare_us:>6} us");
    eprintln!(
        "  Per call:                           {:>6} us",
        prepare_us / 100
    );
    eprintln!("  Note: write_to_disk() I/O runs in background thread.");
    eprintln!("{}", "=".repeat(70));

    assert!(
        prepare_us / 100 < 5000,
        "prepare_save should take <5ms per call, got {} us",
        prepare_us / 100
    );

    assert_eq!(
        session.stats.unsaved_changes, 0,
        "prepare_save must reset unsaved_changes"
    );
}

#[test]
fn bench_session_save_semantics() {
    use lean_ctx::core::session::SessionState;

    let mut session = SessionState::new();
    for _ in 0..6 {
        session.increment();
    }
    assert!(session.should_save(), "should_save after 6 mutations");

    let prepared = session.prepare_save().expect("prepare_save");
    assert_eq!(
        session.stats.unsaved_changes, 0,
        "prepare_save must reset counter immediately (for async path)"
    );
    assert!(
        !session.should_save(),
        "should_save must be false after prepare_save"
    );

    let write_result = prepared.write_to_disk();
    assert!(
        write_result.is_ok(),
        "write_to_disk to valid dir should succeed"
    );

    for _ in 0..6 {
        session.increment();
    }
    assert!(session.should_save());

    let result = session.save();
    assert!(result.is_ok(), "synchronous save should succeed");
    assert_eq!(
        session.stats.unsaved_changes, 0,
        "save must reset counter on success"
    );
    assert!(
        !session.should_save(),
        "should_save must be false after save"
    );
}

#[test]
fn bench_full_per_call_overhead_budget() {
    // Serialize the LEAN_CTX_MINIMAL toggle below against sibling env-mutating tests.
    let _lock = lean_ctx::core::data_dir::test_env_lock();
    let lazy_tools = lean_ctx::tool_defs::lazy_tool_defs();
    let full_tools = lean_ctx::tool_defs::granular_tool_defs();

    let instructions_minimal = {
        // TODO: Audit that the environment access only happens in single-threaded code.
        unsafe { std::env::set_var("LEAN_CTX_MINIMAL", "1") };
        let i = lean_ctx::server::build_instructions_for_test(CrpMode::Tdd);
        // TODO: Audit that the environment access only happens in single-threaded code.
        unsafe { std::env::remove_var("LEAN_CTX_MINIMAL") };
        i
    };
    let instructions_full = lean_ctx::server::build_instructions_for_test(CrpMode::Tdd);

    let lazy_tool_tokens: usize = lazy_tools
        .iter()
        .map(|t| {
            let desc = t.description.as_ref().map_or(0, |d| d.as_ref().len());
            let schema = serde_json::to_string(&t.input_schema)
                .unwrap_or_default()
                .len();
            (desc + schema) / 4
        })
        .sum();

    let full_tool_tokens: usize = full_tools
        .iter()
        .map(|t| {
            let desc = t.description.as_ref().map_or(0, |d| d.as_ref().len());
            let schema = serde_json::to_string(&t.input_schema)
                .unwrap_or_default()
                .len();
            (desc + schema) / 4
        })
        .sum();

    let minimal_instr_tokens = count_tokens(&instructions_minimal);
    let full_instr_tokens = count_tokens(&instructions_full);

    let best_case = minimal_instr_tokens + lazy_tool_tokens;
    let worst_case = full_instr_tokens + full_tool_tokens;

    eprintln!("\n{}", "=".repeat(70));
    eprintln!("  TOTAL PER-SESSION OVERHEAD BUDGET");
    eprintln!("{}", "=".repeat(70));
    eprintln!(
        "  Best case  (lazy+minimal):  {best_case:>5} tok  ({minimal_instr_tokens} instr + {lazy_tool_tokens} tools)"
    );
    eprintln!(
        "  Worst case (full+verbose):  {worst_case:>5} tok  ({full_instr_tokens} instr + {full_tool_tokens} tools)"
    );
    let reduction = (worst_case - best_case) as f64 / worst_case as f64 * 100.0;
    eprintln!("  Total reduction potential:   {reduction:>5.1}%");
    eprintln!("{}", "=".repeat(70));

    // Best case = lazy core tools (full registry schemas) + minimal
    // instructions, matching the live server's default surface (#141 SSOT).
    assert!(
        best_case < 4200,
        "Best-case overhead should be <4200 tokens, got {best_case}"
    );
    assert!(
        reduction > 50.0,
        "Reduction potential should be >50%, got {reduction:.1}%"
    );
}
