use std::env;

use platform_config::format::ConfigLoader;
use serde::{Deserialize, Serialize};
use tracing::{debug, info, warn};

use crate::types::KmsBackendConfig;

/// Vault-service configuration structure
///
/// This structure is loaded from vault-service.ncl or vault-service.toml
/// via the ConfigLoader trait, providing unified configuration management
/// across all platform services.
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct VaultServiceConfig {
    #[serde(default)]
    pub vault_service: VaultServiceSettings,
}

/// Vault service settings
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct VaultServiceSettings {
    #[serde(default)]
    pub server: ServerConfig,
    #[serde(default)]
    pub vault: VaultConfig,
}

/// HTTP server configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ServerConfig {
    pub host: String,
    pub port: u16,
    #[serde(default = "default_workers")]
    pub workers: usize,
    #[serde(default = "default_keep_alive")]
    pub keep_alive: u64,
    #[serde(default = "default_max_connections")]
    pub max_connections: usize,
}

impl Default for ServerConfig {
    fn default() -> Self {
        Self {
            host: "127.0.0.1".to_string(),
            port: 8200,
            workers: 4,
            keep_alive: 75,
            max_connections: 100,
        }
    }
}

/// Vault backend configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VaultConfig {
    #[serde(default = "default_backend_type")]
    pub backend_type: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub storage_backend: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub server_url: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub auth_token: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub mount_point: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub key_name: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub tls_verify: Option<bool>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub public_key_path: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub private_key_path: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub api_key: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub default_key_id: Option<String>,
}

impl Default for VaultConfig {
    fn default() -> Self {
        Self {
            backend_type: "secretumvault".to_string(),
            storage_backend: None,
            server_url: Some("http://localhost:8200".to_string()),
            auth_token: None,
            mount_point: Some("transit".to_string()),
            key_name: Some("provisioning-master".to_string()),
            tls_verify: Some(false),
            public_key_path: None,
            private_key_path: None,
            api_key: None,
            default_key_id: None,
        }
    }
}

fn default_workers() -> usize {
    4
}

fn default_keep_alive() -> u64 {
    75
}

fn default_max_connections() -> usize {
    100
}

fn default_backend_type() -> String {
    "secretumvault".to_string()
}

impl VaultServiceConfig {
    /// Convert to KmsBackendConfig for use by the KMS service
    pub fn to_kms_backend_config(
        &self,
    ) -> std::result::Result<KmsBackendConfig, Box<dyn std::error::Error + Send + Sync>> {
        let backend = &self.vault_service.vault;
        let backend_type = backend.backend_type.as_str();

        debug!("Converting to KMS backend config:");
        debug!("  backend_type: {}", backend_type);
        debug!("  server_url: {:?}", backend.server_url);
        debug!("  storage_backend: {:?}", backend.storage_backend);
        debug!("  auth_token: {}", backend.auth_token.is_some());
        debug!("  mount_point: {:?}", backend.mount_point);
        debug!("  key_name: {:?}", backend.key_name);
        debug!("  tls_verify: {:?}", backend.tls_verify);

        let kms_config = match backend_type {
            "secretumvault" => KmsBackendConfig::Secretumvault {
                server_url: backend
                    .server_url
                    .clone()
                    .unwrap_or_else(|| "http://localhost:8200".to_string()),
                storage_backend: backend.storage_backend.clone().unwrap_or_else(|| {
                    warn!(
                        "⚠ storage_backend not configured - using unsafe default 'secretumvault'"
                    );
                    "secretumvault".to_string()
                }),
                auth_token: backend.auth_token.clone(),
                mount_point: backend
                    .mount_point
                    .clone()
                    .unwrap_or_else(|| "transit".to_string()),
                key_name: backend
                    .key_name
                    .clone()
                    .unwrap_or_else(|| "provisioning-master".to_string()),
                tls_verify: backend.tls_verify.unwrap_or(false),
            },
            "age" => KmsBackendConfig::Age {
                public_key_path: backend.public_key_path.clone().ok_or_else(|| {
                    Box::new(std::io::Error::new(
                        std::io::ErrorKind::InvalidInput,
                        "public_key_path required for age backend",
                    )) as Box<dyn std::error::Error + Send + Sync>
                })?,
                private_key_path: backend.private_key_path.clone().ok_or_else(|| {
                    Box::new(std::io::Error::new(
                        std::io::ErrorKind::InvalidInput,
                        "private_key_path required for age backend",
                    )) as Box<dyn std::error::Error + Send + Sync>
                })?,
            },
            "cosmian" => KmsBackendConfig::Cosmian {
                server_url: backend.server_url.clone().ok_or_else(|| {
                    Box::new(std::io::Error::new(
                        std::io::ErrorKind::InvalidInput,
                        "server_url required for cosmian backend",
                    )) as Box<dyn std::error::Error + Send + Sync>
                })?,
                api_key: backend.api_key.clone().ok_or_else(|| {
                    Box::new(std::io::Error::new(
                        std::io::ErrorKind::InvalidInput,
                        "api_key required for cosmian backend",
                    )) as Box<dyn std::error::Error + Send + Sync>
                })?,
                default_key_id: backend
                    .default_key_id
                    .clone()
                    .unwrap_or_else(|| "provisioning-master-key".to_string()),
                tls_verify: backend.tls_verify.unwrap_or(true),
            },
            "rustyvault" => KmsBackendConfig::Rustyvault {
                server_url: backend.server_url.clone().ok_or_else(|| {
                    Box::new(std::io::Error::new(
                        std::io::ErrorKind::InvalidInput,
                        "server_url required for rustyvault backend",
                    )) as Box<dyn std::error::Error + Send + Sync>
                })?,
                token: backend.auth_token.clone(),
                mount_point: backend
                    .mount_point
                    .clone()
                    .unwrap_or_else(|| "transit".to_string()),
                key_name: backend
                    .key_name
                    .clone()
                    .unwrap_or_else(|| "provisioning-master".to_string()),
                tls_verify: backend.tls_verify.unwrap_or(false),
            },
            _ => {
                return Err(Box::new(std::io::Error::new(
                    std::io::ErrorKind::InvalidInput,
                    format!("Unsupported KMS backend: {}", backend_type),
                ))
                    as Box<dyn std::error::Error + Send + Sync>)
            }
        };

        Ok(kms_config)
    }
}

impl ConfigLoader for VaultServiceConfig {
    fn service_name() -> &'static str {
        "vault-service"
    }

    fn load_from_hierarchy() -> std::result::Result<Self, Box<dyn std::error::Error + Send + Sync>>
    {
        info!("Loading vault-service configuration from hierarchy...");

        // Use default hierarchy: files -> defaults
        if let Some(path) = platform_config::resolve_config_path(Self::service_name()) {
            info!("✓ Found vault-service config at: {}", path.display());
            let config = Self::from_path(&path)?;
            debug!("Loaded configuration from file:");
            debug!("  server.host: {}", config.vault_service.server.host);
            debug!("  server.port: {}", config.vault_service.server.port);
            debug!(
                "  vault.backend_type: {}",
                config.vault_service.vault.backend_type
            );
            debug!(
                "  vault.storage_backend: {:?}",
                config.vault_service.vault.storage_backend
            );
            debug!(
                "  vault.server_url: {:?}",
                config.vault_service.vault.server_url
            );
            debug!(
                "  vault.mount_point: {:?}",
                config.vault_service.vault.mount_point
            );
            debug!(
                "  vault.key_name: {:?}",
                config.vault_service.vault.key_name
            );
            return Ok(config);
        }

        // Fallback to defaults
        warn!("⚠ No vault-service config found in standard locations - using hardcoded defaults");
        let config = Self::default();
        debug!("Using default configuration:");
        debug!("  server.host: {}", config.vault_service.server.host);
        debug!("  server.port: {}", config.vault_service.server.port);
        debug!(
            "  vault.backend_type: {}",
            config.vault_service.vault.backend_type
        );
        debug!(
            "  vault.storage_backend: {:?}",
            config.vault_service.vault.storage_backend
        );
        Ok(config)
    }

    fn apply_env_overrides(
        &mut self,
    ) -> std::result::Result<(), Box<dyn std::error::Error + Send + Sync>> {
        let mut overrides_applied = false;

        // Server configuration overrides
        if let Ok(host) = env::var("VAULT_SERVER_HOST") {
            info!("ENV override: VAULT_SERVER_HOST = {}", host);
            self.vault_service.server.host = host;
            overrides_applied = true;
        }
        if let Ok(port) = env::var("VAULT_SERVER_PORT") {
            info!("ENV override: VAULT_SERVER_PORT = {}", port);
            self.vault_service.server.port = port.parse()?;
            overrides_applied = true;
        }
        if let Ok(workers) = env::var("VAULT_WORKERS") {
            info!("ENV override: VAULT_WORKERS = {}", workers);
            self.vault_service.server.workers = workers.parse()?;
            overrides_applied = true;
        }

        // Vault backend configuration overrides
        if let Ok(backend) = env::var("VAULT_BACKEND_TYPE") {
            info!("ENV override: VAULT_BACKEND_TYPE = {}", backend);
            self.vault_service.vault.backend_type = backend;
            overrides_applied = true;
        }
        if let Ok(server_url) = env::var("VAULT_SERVER_URL") {
            info!("ENV override: VAULT_SERVER_URL = {}", server_url);
            self.vault_service.vault.server_url = Some(server_url);
            overrides_applied = true;
        }
        if let Ok(auth_token) = env::var("VAULT_AUTH_TOKEN") {
            info!("ENV override: VAULT_AUTH_TOKEN = ***");
            self.vault_service.vault.auth_token = Some(auth_token);
            overrides_applied = true;
        }
        if let Ok(mount_point) = env::var("VAULT_MOUNT_POINT") {
            info!("ENV override: VAULT_MOUNT_POINT = {}", mount_point);
            self.vault_service.vault.mount_point = Some(mount_point);
            overrides_applied = true;
        }
        if let Ok(key_name) = env::var("VAULT_KEY_NAME") {
            info!("ENV override: VAULT_KEY_NAME = {}", key_name);
            self.vault_service.vault.key_name = Some(key_name);
            overrides_applied = true;
        }
        if let Ok(tls_verify) = env::var("VAULT_TLS_VERIFY") {
            info!("ENV override: VAULT_TLS_VERIFY = {}", tls_verify);
            self.vault_service.vault.tls_verify = Some(tls_verify.parse()?);
            overrides_applied = true;
        }

        // Storage backend specific overrides
        if let Ok(storage_backend) = env::var("VAULT_STORAGE_BACKEND") {
            info!("ENV override: VAULT_STORAGE_BACKEND = {}", storage_backend);
            self.vault_service.vault.storage_backend = Some(storage_backend);
            overrides_applied = true;
        }
        if let Ok(public_key_path) = env::var("VAULT_PUBLIC_KEY_PATH") {
            info!("ENV override: VAULT_PUBLIC_KEY_PATH = {}", public_key_path);
            self.vault_service.vault.public_key_path = Some(public_key_path);
            overrides_applied = true;
        }
        if let Ok(private_key_path) = env::var("VAULT_PRIVATE_KEY_PATH") {
            info!(
                "ENV override: VAULT_PRIVATE_KEY_PATH = {}",
                private_key_path
            );
            self.vault_service.vault.private_key_path = Some(private_key_path);
            overrides_applied = true;
        }
        if let Ok(api_key) = env::var("VAULT_API_KEY") {
            info!("ENV override: VAULT_API_KEY = ***");
            self.vault_service.vault.api_key = Some(api_key);
            overrides_applied = true;
        }
        if let Ok(default_key_id) = env::var("VAULT_DEFAULT_KEY_ID") {
            info!("ENV override: VAULT_DEFAULT_KEY_ID = {}", default_key_id);
            self.vault_service.vault.default_key_id = Some(default_key_id);
            overrides_applied = true;
        }

        if !overrides_applied {
            debug!("No environment variable overrides detected");
        }

        Ok(())
    }
}

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

    #[test]
    fn test_default_config() {
        let config = VaultServiceConfig::default();
        assert_eq!(config.vault_service.server.host, "127.0.0.1");
        assert_eq!(config.vault_service.server.port, 8200);
        assert_eq!(config.vault_service.vault.backend_type, "secretumvault");
    }

    #[test]
    fn test_to_kms_backend_config_secretumvault() {
        let config = VaultServiceConfig::default();
        let kms_config = config.to_kms_backend_config().expect("Should convert");

        if let KmsBackendConfig::Secretumvault {
            server_url,
            storage_backend,
            ..
        } = kms_config
        {
            assert_eq!(server_url, "http://localhost:8200");
            assert_eq!(storage_backend, "secretumvault");
        } else {
            panic!("Expected Secretumvault backend");
        }
    }

    #[test]
    fn test_env_overrides() {
        env::set_var("VAULT_SERVER_HOST", "0.0.0.0");
        env::set_var("VAULT_SERVER_PORT", "8300");

        let mut config = VaultServiceConfig::default();
        config
            .apply_env_overrides()
            .expect("Should apply overrides");

        assert_eq!(config.vault_service.server.host, "0.0.0.0");
        assert_eq!(config.vault_service.server.port, 8300);

        env::remove_var("VAULT_SERVER_HOST");
        env::remove_var("VAULT_SERVER_PORT");
    }
}
