//! Runtime toggles for the MCP server (environment variables).

/// User-visible error when [`plaintext_store_disabled`] blocks `envseal_store`.
pub const STORE_DISABLED_MESSAGE: &str = "envseal_store is disabled (ENVSEAL_MCP_DISABLE_STORE) — use envseal_request_key so the secret never transits the MCP layer";

/// User-visible error when [`plaintext_import_disabled`] blocks `envseal_import`.
pub const IMPORT_DISABLED_MESSAGE: &str = "envseal_import is disabled (ENVSEAL_MCP_DISABLE_IMPORT) — run imports from a trusted host with `envseal import` (or a GUI), not through agent-controlled MCP tool arguments";

/// `1`, `true`, `yes`, or `on` (ASCII case-insensitive); empty/whitespace after trim ⇒ unset.
fn env_mcp_flag_truthy(var: &str) -> bool {
    match std::env::var(var) {
        Ok(raw) => {
            let v = raw.trim();
            if v.is_empty() {
                return false;
            }
            matches!(v, "1")
                || v.eq_ignore_ascii_case("true")
                || v.eq_ignore_ascii_case("yes")
                || v.eq_ignore_ascii_case("on")
        }
        Err(_) => false,
    }
}

/// When `ENVSEAL_MCP_DISABLE_STORE` is set to a truthy value (after trim), store is blocked.
///
/// Use on production agent hosts so new secret material is only entered via GUI (`envseal_request_key`).
pub fn plaintext_store_disabled() -> bool {
    env_mcp_flag_truthy("ENVSEAL_MCP_DISABLE_STORE")
}

/// When `ENVSEAL_MCP_DISABLE_IMPORT` is set to a truthy value (after trim), `.env` import is blocked.
///
/// `envseal_import` reads arbitrary paths under the server cwd and can pull many secrets in one
/// call — use this when the MCP session must not choose migration sources.
pub fn plaintext_import_disabled() -> bool {
    env_mcp_flag_truthy("ENVSEAL_MCP_DISABLE_IMPORT")
}

#[cfg(test)]
pub(crate) static TEST_ENV_LOCK: std::sync::Mutex<()> = std::sync::Mutex::new(());

#[cfg(test)]
mod tests {
    use super::{plaintext_import_disabled, plaintext_store_disabled};

    fn run_with_env(name: &str, var: Option<&str>, assert_fn: impl FnOnce()) {
        let _guard = super::TEST_ENV_LOCK.lock().expect("serialize env tests");
        match var {
            None => std::env::remove_var(name),
            Some(value) => std::env::set_var(name, value),
        }
        assert_fn();
        std::env::remove_var(name);
    }

    // --- ENVSEAL_MCP_DISABLE_STORE

    fn run_with_store_env(var: Option<&str>, assert_fn: impl FnOnce()) {
        run_with_env("ENVSEAL_MCP_DISABLE_STORE", var, assert_fn);
    }

    #[test]
    fn absent_env_does_not_disable_store() {
        run_with_store_env(None, || assert!(!plaintext_store_disabled()));
    }

    #[test]
    fn literal_one_disables_store() {
        run_with_store_env(Some("1"), || assert!(plaintext_store_disabled()));
    }

    #[test]
    fn true_any_ascii_case_disables_store() {
        run_with_store_env(Some("true"), || assert!(plaintext_store_disabled()));
        run_with_store_env(Some("TRUE"), || assert!(plaintext_store_disabled()));
        run_with_store_env(Some("TrUe"), || assert!(plaintext_store_disabled()));
    }

    #[test]
    fn yes_any_ascii_case_disables_store() {
        run_with_store_env(Some("yes"), || assert!(plaintext_store_disabled()));
        run_with_store_env(Some("YES"), || assert!(plaintext_store_disabled()));
        run_with_store_env(Some("YeS"), || assert!(plaintext_store_disabled()));
    }

    #[test]
    fn non_truthy_values_do_not_disable_store() {
        run_with_store_env(Some("0"), || assert!(!plaintext_store_disabled()));
        run_with_store_env(Some(""), || assert!(!plaintext_store_disabled()));
        run_with_store_env(Some("false"), || assert!(!plaintext_store_disabled()));
        run_with_store_env(Some("maybe"), || assert!(!plaintext_store_disabled()));
    }

    #[test]
    fn on_any_ascii_case_disables_store() {
        run_with_store_env(Some("on"), || assert!(plaintext_store_disabled()));
        run_with_store_env(Some("ON"), || assert!(plaintext_store_disabled()));
        run_with_store_env(Some("On"), || assert!(plaintext_store_disabled()));
    }

    #[test]
    fn whitespace_around_value_is_trimmed_for_store() {
        run_with_store_env(Some("  1  "), || assert!(plaintext_store_disabled()));
        run_with_store_env(Some("\tyes\n"), || assert!(plaintext_store_disabled()));
        run_with_store_env(Some("   "), || assert!(!plaintext_store_disabled()));
    }

    // --- ENVSEAL_MCP_DISABLE_IMPORT (same truthy semantics as store)

    fn run_with_import_env(var: Option<&str>, assert_fn: impl FnOnce()) {
        run_with_env("ENVSEAL_MCP_DISABLE_IMPORT", var, assert_fn);
    }

    #[test]
    fn import_absent_allows() {
        run_with_import_env(None, || assert!(!plaintext_import_disabled()));
    }

    #[test]
    fn import_one_disables() {
        run_with_import_env(Some("1"), || assert!(plaintext_import_disabled()));
    }

    #[test]
    fn import_on_disables() {
        run_with_import_env(Some("on"), || assert!(plaintext_import_disabled()));
    }

    #[test]
    fn import_false_allows() {
        run_with_import_env(Some("0"), || assert!(!plaintext_import_disabled()));
    }

    // --- independent flags (single lock: do not nest `run_with_env` — Mutex is not re-entrant)

    #[test]
    fn import_and_store_flags_are_independent() {
        let _guard = super::TEST_ENV_LOCK.lock().expect("serialize env tests");
        std::env::set_var("ENVSEAL_MCP_DISABLE_STORE", "1");
        std::env::remove_var("ENVSEAL_MCP_DISABLE_IMPORT");
        assert!(plaintext_store_disabled());
        assert!(!plaintext_import_disabled());
        std::env::remove_var("ENVSEAL_MCP_DISABLE_STORE");

        std::env::set_var("ENVSEAL_MCP_DISABLE_IMPORT", "1");
        assert!(!plaintext_store_disabled());
        assert!(plaintext_import_disabled());
        std::env::remove_var("ENVSEAL_MCP_DISABLE_IMPORT");
    }
}
