use lean_ctx::core::config::{CompressionLevel, Config, TerseAgent};
use lean_ctx::instructions;
use lean_ctx::tools::CrpMode;
use std::sync::Mutex;

static ENV_LOCK: Mutex<()> = Mutex::new(());

fn lock() -> std::sync::MutexGuard<'static, ()> {
    ENV_LOCK
        .lock()
        .unwrap_or_else(std::sync::PoisonError::into_inner)
}

fn set_compression(compression: &str) {
    // TODO: Audit that the environment access only happens in single-threaded code.
    unsafe { std::env::set_var("LEAN_CTX_COMPRESSION", compression) };
    // TODO: Audit that the environment access only happens in single-threaded code.
    unsafe { std::env::remove_var("LEAN_CTX_TERSE_AGENT") };
    // TODO: Audit that the environment access only happens in single-threaded code.
    unsafe { std::env::remove_var("LEAN_CTX_OUTPUT_DENSITY") };
}

fn set_legacy_terse(terse: &str) {
    // TODO: Audit that the environment access only happens in single-threaded code.
    unsafe { std::env::remove_var("LEAN_CTX_COMPRESSION") };
    // TODO: Audit that the environment access only happens in single-threaded code.
    unsafe { std::env::set_var("LEAN_CTX_TERSE_AGENT", terse) };
    // TODO: Audit that the environment access only happens in single-threaded code.
    unsafe { std::env::remove_var("LEAN_CTX_OUTPUT_DENSITY") };
    isolate_config_with_compression_off();
}

fn isolate_config_with_compression_off() {
    let tmp = std::env::temp_dir().join("lean_ctx_test_config_legacy");
    let _ = std::fs::create_dir_all(&tmp);
    let _ = std::fs::write(tmp.join("config.toml"), "compression_level = \"off\"\n");
    // TODO: Audit that the environment access only happens in single-threaded code.
    unsafe { std::env::set_var("LEAN_CTX_DATA_DIR", &tmp) };
}

fn cleanup_env() {
    // TODO: Audit that the environment access only happens in single-threaded code.
    unsafe { std::env::remove_var("LEAN_CTX_COMPRESSION") };
    // TODO: Audit that the environment access only happens in single-threaded code.
    unsafe { std::env::remove_var("LEAN_CTX_TERSE_AGENT") };
    // TODO: Audit that the environment access only happens in single-threaded code.
    unsafe { std::env::remove_var("LEAN_CTX_OUTPUT_DENSITY") };
    // TODO: Audit that the environment access only happens in single-threaded code.
    unsafe { std::env::remove_var("LEAN_CTX_DATA_DIR") };
}

// ── TerseAgent unit tests ──

#[test]
fn terse_agent_default_is_off() {
    let ta = TerseAgent::default();
    assert!(matches!(ta, TerseAgent::Off));
}

#[test]
fn terse_agent_from_env() {
    let _g = lock();
    // TODO: Audit that the environment access only happens in single-threaded code.
    unsafe { std::env::set_var("LEAN_CTX_TERSE_AGENT", "full") };
    assert!(matches!(TerseAgent::from_env(), TerseAgent::Full));

    // TODO: Audit that the environment access only happens in single-threaded code.
    unsafe { std::env::set_var("LEAN_CTX_TERSE_AGENT", "lite") };
    assert!(matches!(TerseAgent::from_env(), TerseAgent::Lite));

    // TODO: Audit that the environment access only happens in single-threaded code.
    unsafe { std::env::set_var("LEAN_CTX_TERSE_AGENT", "ultra") };
    assert!(matches!(TerseAgent::from_env(), TerseAgent::Ultra));

    // TODO: Audit that the environment access only happens in single-threaded code.
    unsafe { std::env::set_var("LEAN_CTX_TERSE_AGENT", "off") };
    assert!(matches!(TerseAgent::from_env(), TerseAgent::Off));

    cleanup_env();
}

// ── Legacy LEAN_CTX_TERSE_AGENT routes through CompressionLevel ──

#[test]
fn legacy_terse_lite_routes_to_compression_lite() {
    let _g = lock();
    set_legacy_terse("lite");
    let text = instructions::build_instructions_for_test(CrpMode::Off);
    assert!(
        text.contains("OUTPUT STYLE: concise"),
        "legacy terse lite should route to compression lite prompt"
    );
    cleanup_env();
}

#[test]
fn legacy_terse_full_routes_to_compression_standard() {
    let _g = lock();
    set_legacy_terse("full");
    let text = instructions::build_instructions_for_test(CrpMode::Off);
    assert!(
        text.contains("OUTPUT STYLE: dense"),
        "legacy terse full should route to compression standard prompt"
    );
    cleanup_env();
}

#[test]
fn legacy_terse_ultra_routes_to_compression_max() {
    let _g = lock();
    set_legacy_terse("ultra");
    let text = instructions::build_instructions_for_test(CrpMode::Off);
    assert!(
        text.contains("OUTPUT STYLE: expert-terse"),
        "legacy terse ultra should route to compression max prompt"
    );
    cleanup_env();
}

#[test]
fn legacy_terse_off_no_output_style() {
    let _g = lock();
    set_legacy_terse("off");
    let text = instructions::build_instructions_for_test(CrpMode::Off);
    assert!(
        !text.contains("OUTPUT STYLE"),
        "terse off should not inject OUTPUT STYLE block"
    );
    cleanup_env();
}

// ── Unified CompressionLevel env var ──

#[test]
fn compression_env_overrides_legacy_terse_agent() {
    let _g = lock();
    // TODO: Audit that the environment access only happens in single-threaded code.
    unsafe { std::env::set_var("LEAN_CTX_COMPRESSION", "max") };
    // TODO: Audit that the environment access only happens in single-threaded code.
    unsafe { std::env::set_var("LEAN_CTX_TERSE_AGENT", "lite") };
    let text = instructions::build_instructions_for_test(CrpMode::Off);
    assert!(
        text.contains("expert-terse"),
        "compression env should override legacy terse_agent"
    );
    cleanup_env();
}

#[test]
fn compression_level_lite_injects_concise() {
    let _g = lock();
    set_compression("lite");
    let text = instructions::build_instructions_for_test(CrpMode::Off);
    assert!(
        text.contains("OUTPUT STYLE: concise"),
        "compression lite should inject concise prompt"
    );
    cleanup_env();
}

#[test]
fn compression_level_standard_injects_dense() {
    let _g = lock();
    set_compression("standard");
    let text = instructions::build_instructions_for_test(CrpMode::Off);
    assert!(
        text.contains("OUTPUT STYLE: dense"),
        "compression standard should inject dense prompt"
    );
    assert!(
        text.contains("fn, cfg, impl"),
        "compression standard should mention abbreviations"
    );
    cleanup_env();
}

#[test]
fn compression_level_max_injects_expert_terse() {
    let _g = lock();
    set_compression("max");
    let text = instructions::build_instructions_for_test(CrpMode::Off);
    assert!(
        text.contains("OUTPUT STYLE: expert-terse"),
        "compression max should inject expert-terse prompt"
    );
    cleanup_env();
}

#[test]
fn compression_level_off_no_output_style() {
    let _g = lock();
    set_compression("off");
    let text = instructions::build_instructions_for_test(CrpMode::Off);
    assert!(
        !text.contains("OUTPUT STYLE"),
        "compression off should not inject any OUTPUT STYLE block"
    );
    cleanup_env();
}

// ── Config deserialization ──

#[test]
fn terse_agent_config_deserializes() {
    let toml = r#"
terse_agent = "full"
"#;
    let config: Config = toml::from_str(toml).expect("should parse terse_agent from toml");
    assert!(matches!(config.terse_agent, TerseAgent::Full));
}

#[test]
fn terse_agent_config_default_off() {
    let toml = "";
    let config: Config = toml::from_str(toml).expect("empty toml should use defaults");
    assert!(matches!(config.terse_agent, TerseAgent::Off));
}

#[test]
fn compression_level_config_deserializes() {
    let toml = r#"compression_level = "standard""#;
    let config: Config = toml::from_str(toml).expect("should parse compression_level");
    assert!(matches!(
        config.compression_level,
        CompressionLevel::Standard
    ));
}

#[test]
fn compression_level_config_default_lite() {
    let toml = "";
    let config: Config = toml::from_str(toml).expect("empty toml should use defaults");
    assert!(matches!(config.compression_level, CompressionLevel::Lite));
}
