use compliance_core::config::{BreakpilotConfig, PlcRuntimeConfig};
use compliance_core::AgentConfig;
use secrecy::SecretString;

use crate::error::AgentError;

fn env_var(key: &str) -> Result<String, AgentError> {
    std::env::var(key).map_err(|_| AgentError::Config(format!("Missing env var: {key}")))
}

fn env_var_opt(key: &str) -> Option<String> {
    std::env::var(key).ok().filter(|v| !v.is_empty())
}

fn env_secret_opt(key: &str) -> Option<SecretString> {
    env_var_opt(key).map(SecretString::from)
}

pub fn load_config() -> Result<AgentConfig, AgentError> {
    Ok(AgentConfig {
        mongodb_uri: env_var("MONGODB_URI")?,
        mongodb_database: env_var_opt("MONGODB_DATABASE")
            .unwrap_or_else(|| "compliance_scanner".to_string()),
        litellm_url: env_var_opt("LITELLM_URL")
            .unwrap_or_else(|| "http://localhost:4000".to_string()),
        litellm_api_key: SecretString::from(env_var_opt("LITELLM_API_KEY").unwrap_or_default()),
        litellm_model: env_var_opt("LITELLM_MODEL").unwrap_or_else(|| "gpt-4o".to_string()),
        litellm_embed_model: env_var_opt("LITELLM_EMBED_MODEL")
            .unwrap_or_else(|| "text-embedding-3-small".to_string()),
        github_token: env_secret_opt("GITHUB_TOKEN"),
        github_webhook_secret: env_secret_opt("GITHUB_WEBHOOK_SECRET"),
        gitlab_url: env_var_opt("GITLAB_URL"),
        gitlab_token: env_secret_opt("GITLAB_TOKEN"),
        gitlab_webhook_secret: env_secret_opt("GITLAB_WEBHOOK_SECRET"),
        jira_url: env_var_opt("JIRA_URL"),
        jira_email: env_var_opt("JIRA_EMAIL"),
        jira_api_token: env_secret_opt("JIRA_API_TOKEN"),
        jira_project_key: env_var_opt("JIRA_PROJECT_KEY"),
        searxng_url: env_var_opt("SEARXNG_URL"),
        nvd_api_key: env_secret_opt("NVD_API_KEY"),
        agent_port: env_var_opt("AGENT_PORT")
            .and_then(|p| p.parse().ok())
            .unwrap_or(3001),
        scan_schedule: env_var_opt("SCAN_SCHEDULE").unwrap_or_else(|| "0 0 */6 * * *".to_string()),
        cve_monitor_schedule: env_var_opt("CVE_MONITOR_SCHEDULE")
            .unwrap_or_else(|| "0 0 * * * *".to_string()),
        git_clone_base_path: env_var_opt("GIT_CLONE_BASE_PATH")
            .unwrap_or_else(|| "/tmp/compliance-scanner/repos".to_string()),
        artifact_store_base_path: env_var_opt("ARTIFACT_STORE_BASE_PATH")
            .unwrap_or_else(|| "/data/compliance-scanner/artifacts".to_string()),
        ssh_key_path: env_var_opt("SSH_KEY_PATH")
            .unwrap_or_else(|| "/data/compliance-scanner/ssh/id_ed25519".to_string()),
        keycloak_url: env_var_opt("KEYCLOAK_URL"),
        keycloak_realm: env_var_opt("KEYCLOAK_REALM"),
        keycloak_admin_username: env_var_opt("KEYCLOAK_ADMIN_USERNAME"),
        keycloak_admin_password: env_secret_opt("KEYCLOAK_ADMIN_PASSWORD"),
        pentest_verification_email: env_var_opt("PENTEST_VERIFICATION_EMAIL"),
        pentest_imap_host: env_var_opt("PENTEST_IMAP_HOST"),
        pentest_imap_port: env_var_opt("PENTEST_IMAP_PORT").and_then(|p| p.parse().ok()),
        pentest_imap_tls: env_var_opt("PENTEST_IMAP_TLS")
            .map(|v| v == "1" || v.eq_ignore_ascii_case("true"))
            .unwrap_or(true),
        pentest_imap_username: env_var_opt("PENTEST_IMAP_USERNAME"),
        pentest_imap_password: env_secret_opt("PENTEST_IMAP_PASSWORD"),
        admin_api_token: env_secret_opt("ADMIN_API_TOKEN"),
        tenant_registry_url: env_var_opt("TENANT_REGISTRY_URL"),
        plc_runtime: load_plc_runtime_config(),
        werkbank_runner_token: env_secret_opt("WERKBANK_RUNNER_TOKEN"),
        breakpilot: load_breakpilot_config(),
    })
}

/// Build the ephemeral soft-PLC provisioning config from the environment,
/// falling back to [`PlcRuntimeConfig::default`] for any unset knob. Disabled
/// unless `PLC_RUNTIME_ENABLED` is truthy — it requires Docker access.
fn load_plc_runtime_config() -> PlcRuntimeConfig {
    let d = PlcRuntimeConfig::default();
    PlcRuntimeConfig {
        enabled: env_var_opt("PLC_RUNTIME_ENABLED")
            .map(|v| v == "1" || v.eq_ignore_ascii_case("true"))
            .unwrap_or(d.enabled),
        image: env_var_opt("PLC_RUNTIME_IMAGE").unwrap_or(d.image),
        network: env_var_opt("PLC_RUNTIME_NETWORK").unwrap_or(d.network),
        memory: env_var_opt("PLC_RUNTIME_MEMORY").unwrap_or(d.memory),
        cpus: env_var_opt("PLC_RUNTIME_CPUS").unwrap_or(d.cpus),
        max_lifetime_secs: env_var_opt("PLC_RUNTIME_MAX_LIFETIME_SECS")
            .and_then(|v| v.parse().ok())
            .unwrap_or(d.max_lifetime_secs),
        openplc_user: env_var_opt("PLC_RUNTIME_OPENPLC_USER").unwrap_or(d.openplc_user),
        openplc_password: env_secret_opt("PLC_RUNTIME_OPENPLC_PASSWORD")
            .unwrap_or(d.openplc_password),
    }
}

/// Assemble the breakpilot OSCAL-catalog source from env, defaulting the snapshot
/// directory. A missing `BREAKPILOT_BASE_URL` leaves the controls provider off.
fn load_breakpilot_config() -> BreakpilotConfig {
    let d = BreakpilotConfig::default();
    BreakpilotConfig {
        base_url: env_var_opt("BREAKPILOT_BASE_URL"),
        token: env_secret_opt("BREAKPILOT_TOKEN"),
        snapshot_dir: env_var_opt("BREAKPILOT_SNAPSHOT_DIR").unwrap_or(d.snapshot_dir),
        semantic_mapping: env_var_opt("BREAKPILOT_SEMANTIC_MAPPING")
            .map(|v| v == "1" || v.eq_ignore_ascii_case("true"))
            .unwrap_or(d.semantic_mapping),
        grounded_control_checks: env_var_opt("BREAKPILOT_GROUNDED_CHECKS")
            .map(|v| v == "1" || v.eq_ignore_ascii_case("true"))
            .unwrap_or(d.grounded_control_checks),
    }
}
