//! KMS Configuration Module
//!
//! Provides configuration structures and loading functionality following
//! the project's TOML-based config-driven architecture.

use std::collections::HashMap;
use std::path::Path;

use serde::{Deserialize, Serialize};
use validator::{Validate, ValidationError};

use crate::kms::error::KmsError;
use crate::kms::traits::ConfigValidation;
use crate::kms::types::{CacheBackend, CacheConfig, HsmConfig, KmsMode, RotationPolicy, ZkpConfig};

/// Main KMS configuration structure
#[derive(Debug, Clone, Serialize, Deserialize, Validate)]
pub struct KmsConfig {
    /// KMS operation mode
    pub mode: KmsMode,

    /// Local backend configuration
    #[validate(custom(function = "validate_local_config"))]
    pub local: LocalConfig,

    /// Remote backend configuration
    #[validate(custom(function = "validate_remote_config"))]
    pub remote: RemoteConfig,

    /// Cache configuration
    #[validate(nested)]
    pub cache: CacheConfig,

    /// Credential management configuration
    #[validate(nested)]
    pub credentials: CredentialConfig,

    /// Key rotation configuration
    #[validate(nested)]
    pub rotation: RotationPolicy,

    /// Audit configuration
    #[validate(nested)]
    pub audit: AuditConfig,

    /// HSM configuration
    pub hsm: HsmConfig,

    /// Zero-knowledge proof configuration
    pub zkp: ZkpConfig,

    /// Security configuration
    #[validate(nested)]
    pub security: SecurityConfig,

    /// Performance tuning configuration
    pub performance: PerformanceConfig,
}

/// Local KMS backend configuration
#[derive(Debug, Clone, Serialize, Deserialize, Validate)]
pub struct LocalConfig {
    /// SQLite database path
    #[validate(length(min = 1, message = "Database path cannot be empty"))]
    pub database_path: String,

    /// Master key derivation settings
    #[validate(nested)]
    pub master_key: MasterKeyConfig,

    /// Encryption settings
    pub encryption: EncryptionConfig,

    /// Backup settings
    pub backup: BackupConfig,

    /// Connection pool settings
    pub pool: DatabasePoolConfig,
}

/// Remote KMS backend configuration (Cosmian KMS)
#[derive(Debug, Clone, Serialize, Deserialize, Validate)]
pub struct RemoteConfig {
    /// KMS server URL
    #[validate(url(message = "Invalid server URL"))]
    pub server_url: String,

    /// Authentication method
    pub auth_method: AuthMethod,

    /// Client certificate path (for certificate auth)
    pub client_cert_path: Option<String>,

    /// Client private key path (for certificate auth)
    pub client_key_path: Option<String>,

    /// CA certificate path for server verification
    pub ca_cert_path: Option<String>,

    /// API token (for token auth)
    pub api_token: Option<String>,

    /// Username (for basic auth)
    pub username: Option<String>,

    /// Password (for basic auth) - will be loaded from secure storage
    pub password_key: Option<String>,

    /// Request timeout in seconds
    #[validate(range(
        min = 1,
        max = 300,
        message = "Timeout must be between 1 and 300 seconds"
    ))]
    pub timeout_seconds: u64,

    /// Verify SSL certificates
    pub verify_ssl: bool,

    /// Connection retry settings
    pub retry: RetryConfig,

    /// Request rate limiting
    pub rate_limit: RateLimitConfig,
}

/// Authentication method for remote KMS
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum AuthMethod {
    #[serde(rename = "certificate")]
    Certificate,
    #[serde(rename = "token")]
    Token,
    #[serde(rename = "basic")]
    Basic,
    #[serde(rename = "oauth")]
    OAuth,
}

/// Credential management configuration
#[derive(Debug, Clone, Serialize, Deserialize, Validate)]
pub struct CredentialConfig {
    /// Enable credential management
    pub enabled: bool,

    /// Credential storage backend
    pub storage: CredentialStorage,

    /// Supported cloud providers
    pub providers: HashMap<String, ProviderConfig>,

    /// Credential refresh settings
    pub refresh: RefreshConfig,

    /// Injection settings for automatic credential delivery
    pub injection: InjectionConfig,
}

/// Credential storage backend configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CredentialStorage {
    /// Storage type
    pub storage_type: CredentialStorageType,

    /// Encryption key for credential storage
    pub encryption_key_id: String,

    /// Database path (for SQLite storage)
    pub database_path: Option<String>,

    /// Vault settings (for external vault storage)
    pub vault: Option<VaultConfig>,
}

/// Credential storage type
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum CredentialStorageType {
    #[serde(rename = "sqlite")]
    SQLite,
    #[serde(rename = "vault")]
    Vault,
    #[serde(rename = "kms")]
    Kms,
}

/// Provider-specific configuration
#[derive(Debug, Clone, Serialize, Deserialize, Validate)]
pub struct ProviderConfig {
    /// Provider display name
    pub name: String,

    /// Provider type (aws, upcloud, etc.)
    pub provider_type: String,

    /// Credential refresh interval in seconds
    #[validate(range(min = 300, message = "Refresh interval must be at least 5 minutes"))]
    pub refresh_interval_seconds: u64,

    /// Credential expiry warning threshold in seconds
    pub expiry_warning_seconds: u64,

    /// Region or endpoint configuration
    pub regions: Vec<String>,

    /// Provider-specific settings
    pub settings: HashMap<String, String>,
}

/// Audit configuration
#[derive(Debug, Clone, Serialize, Deserialize, Validate)]
pub struct AuditConfig {
    /// Enable audit logging
    pub enabled: bool,

    /// Audit storage backend
    pub backend: AuditBackend,

    /// Audit log retention period in days
    #[validate(range(
        min = 1,
        max = 2555,
        message = "Retention must be between 1 and 2555 days"
    ))]
    pub retention_days: u32,

    /// Log level for audit events
    pub log_level: AuditLogLevel,

    /// Include request/response data in audit logs
    pub include_data: bool,

    /// Maximum audit log file size in MB
    pub max_file_size_mb: u64,

    /// Audit log format
    pub format: AuditLogFormat,

    /// Database configuration (for database backend)
    pub database: Option<AuditDatabaseConfig>,

    /// Syslog configuration (for syslog backend)
    pub syslog: Option<SyslogConfig>,
}

/// Audit backend type
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum AuditBackend {
    #[serde(rename = "file")]
    File,
    #[serde(rename = "database")]
    Database,
    #[serde(rename = "syslog")]
    Syslog,
    #[serde(rename = "stdout")]
    Stdout,
}

/// Audit log level
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum AuditLogLevel {
    #[serde(rename = "error")]
    Error,
    #[serde(rename = "warn")]
    Warn,
    #[serde(rename = "info")]
    Info,
    #[serde(rename = "debug")]
    Debug,
}

/// Audit log format
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum AuditLogFormat {
    #[serde(rename = "json")]
    Json,
    #[serde(rename = "structured")]
    Structured,
    #[serde(rename = "text")]
    Text,
}

/// Master key configuration
#[derive(Debug, Clone, Serialize, Deserialize, Validate)]
pub struct MasterKeyConfig {
    /// Key derivation method
    pub derivation_method: KeyDerivationMethod,

    /// Key source (file, environment, etc.)
    pub source: KeySource,

    /// Key file path (for file source)
    pub key_file_path: Option<String>,

    /// Environment variable name (for env source)
    pub env_var_name: Option<String>,

    /// Salt for key derivation
    pub salt: Option<String>,

    /// Key derivation iterations
    #[validate(range(min = 10000, message = "Minimum 10,000 iterations required"))]
    pub iterations: u32,
}

/// Key derivation method
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum KeyDerivationMethod {
    #[serde(rename = "pbkdf2")]
    Pbkdf2,
    #[serde(rename = "scrypt")]
    Scrypt,
    #[serde(rename = "argon2")]
    Argon2,
    #[serde(rename = "hkdf")]
    Hkdf,
}

/// Key source
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum KeySource {
    #[serde(rename = "file")]
    File,
    #[serde(rename = "environment")]
    Environment,
    #[serde(rename = "generated")]
    Generated,
    #[serde(rename = "hsm")]
    Hsm,
}

/// Encryption configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EncryptionConfig {
    /// Default encryption algorithm
    pub default_algorithm: String,

    /// Key size in bits
    pub key_size_bits: u32,

    /// Use authenticated encryption
    pub authenticated: bool,

    /// Additional authenticated data (AAD) source
    pub aad_source: Option<String>,
}

/// Backup configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BackupConfig {
    /// Enable automatic backups
    pub enabled: bool,

    /// Backup directory path
    pub backup_dir: String,

    /// Backup interval in hours
    pub interval_hours: u32,

    /// Maximum number of backups to retain
    pub max_backups: u32,

    /// Compress backups
    pub compress: bool,

    /// Encrypt backups
    pub encrypt: bool,
}

/// Database pool configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DatabasePoolConfig {
    /// Maximum number of connections
    pub max_connections: u32,

    /// Connection timeout in seconds
    pub connect_timeout_seconds: u64,

    /// Idle timeout in seconds
    pub idle_timeout_seconds: u64,

    /// Maximum lifetime of a connection in seconds
    pub max_lifetime_seconds: u64,
}

/// Retry configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RetryConfig {
    /// Maximum number of retry attempts
    pub max_attempts: u32,

    /// Initial retry delay in milliseconds
    pub initial_delay_ms: u64,

    /// Maximum retry delay in milliseconds
    pub max_delay_ms: u64,

    /// Backoff multiplier
    pub backoff_multiplier: f64,
}

/// Rate limiting configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RateLimitConfig {
    /// Enable rate limiting
    pub enabled: bool,

    /// Requests per second limit
    pub requests_per_second: u32,

    /// Burst size
    pub burst_size: u32,
}

/// Refresh configuration for credentials
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RefreshConfig {
    /// Enable automatic refresh
    pub enabled: bool,

    /// Refresh check interval in seconds
    pub check_interval_seconds: u64,

    /// Refresh threshold (percentage of credential lifetime)
    pub threshold_percentage: f64,

    /// Maximum refresh attempts
    pub max_attempts: u32,
}

/// Credential injection configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InjectionConfig {
    /// Enable automatic credential injection
    pub enabled: bool,

    /// Injection methods
    pub methods: Vec<InjectionMethod>,

    /// Target applications/services
    pub targets: HashMap<String, InjectionTarget>,
}

/// Credential injection method
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum InjectionMethod {
    #[serde(rename = "environment")]
    Environment,
    #[serde(rename = "file")]
    File,
    #[serde(rename = "api")]
    Api,
    #[serde(rename = "webhook")]
    Webhook,
}

/// Injection target configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InjectionTarget {
    /// Target name
    pub name: String,

    /// Injection method for this target
    pub method: InjectionMethod,

    /// Target-specific configuration
    pub config: HashMap<String, String>,
}

/// Vault configuration for external secret storage
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VaultConfig {
    /// Vault server URL
    pub url: String,

    /// Authentication method
    pub auth_method: String,

    /// Token or other auth credentials
    pub credentials: HashMap<String, String>,

    /// Mount path
    pub mount_path: String,
}

/// Audit database configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AuditDatabaseConfig {
    /// Database connection URL
    pub url: String,

    /// Table name for audit logs
    pub table_name: String,

    /// Connection pool settings
    pub pool: DatabasePoolConfig,
}

/// Syslog configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SyslogConfig {
    /// Syslog server address
    pub server: String,

    /// Syslog facility
    pub facility: String,

    /// Use TLS
    pub tls: bool,
}

/// Security configuration
#[derive(Debug, Clone, Serialize, Deserialize, Validate)]
pub struct SecurityConfig {
    /// Require strong authentication
    pub require_strong_auth: bool,

    /// Minimum key length in bits
    #[validate(range(min = 128, message = "Minimum key length is 128 bits"))]
    pub min_key_length_bits: u32,

    /// Maximum key age in days before warning
    pub max_key_age_days: u32,

    /// Enable perfect forward secrecy
    pub enable_pfs: bool,

    /// Allowed encryption algorithms
    pub allowed_algorithms: Vec<String>,

    /// Blocked/deprecated algorithms
    pub blocked_algorithms: Vec<String>,

    /// Security policy enforcement level
    pub policy_enforcement: PolicyEnforcement,
}

/// Security policy enforcement level
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum PolicyEnforcement {
    #[serde(rename = "strict")]
    Strict,
    #[serde(rename = "moderate")]
    Moderate,
    #[serde(rename = "permissive")]
    Permissive,
}

/// Performance configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PerformanceConfig {
    /// Worker thread count
    pub worker_threads: Option<usize>,

    /// Operation timeout in seconds
    pub operation_timeout_seconds: u64,

    /// Batch processing settings
    pub batch: BatchConfig,

    /// Memory limits
    pub memory: MemoryConfig,
}

/// Batch processing configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BatchConfig {
    /// Enable batch processing
    pub enabled: bool,

    /// Maximum batch size
    pub max_batch_size: usize,

    /// Batch timeout in seconds
    pub timeout_seconds: u64,
}

/// Memory configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MemoryConfig {
    /// Maximum cache size in MB
    pub max_cache_size_mb: u64,

    /// Memory usage warning threshold in MB
    pub warning_threshold_mb: u64,
}

impl Default for KmsConfig {
    fn default() -> Self {
        Self {
            mode: KmsMode::Local,
            local: LocalConfig::default(),
            remote: RemoteConfig::default(),
            cache: CacheConfig::default(),
            credentials: CredentialConfig::default(),
            rotation: RotationPolicy::default(),
            audit: AuditConfig::default(),
            hsm: HsmConfig::default(),
            zkp: ZkpConfig::default(),
            security: SecurityConfig::default(),
            performance: PerformanceConfig::default(),
        }
    }
}

impl Default for LocalConfig {
    fn default() -> Self {
        Self {
            database_path: "./data/kms.db".to_string(),
            master_key: MasterKeyConfig::default(),
            encryption: EncryptionConfig::default(),
            backup: BackupConfig::default(),
            pool: DatabasePoolConfig::default(),
        }
    }
}

impl Default for RemoteConfig {
    fn default() -> Self {
        Self {
            server_url: "https://localhost:9998".to_string(),
            auth_method: AuthMethod::Certificate,
            client_cert_path: None,
            client_key_path: None,
            ca_cert_path: None,
            api_token: None,
            username: None,
            password_key: None,
            timeout_seconds: 30,
            verify_ssl: true,
            retry: RetryConfig::default(),
            rate_limit: RateLimitConfig::default(),
        }
    }
}

impl Default for CacheConfig {
    fn default() -> Self {
        Self {
            enabled: true,
            backend: CacheBackend::Memory,
            default_ttl_seconds: 3600,         // 1 hour
            max_size_bytes: 100 * 1024 * 1024, // 100MB
            redis_url: None,
            local_dir: Some("./cache/kms".to_string()),
        }
    }
}

impl Default for CredentialConfig {
    fn default() -> Self {
        Self {
            enabled: true,
            storage: CredentialStorage::default(),
            providers: HashMap::new(),
            refresh: RefreshConfig::default(),
            injection: InjectionConfig::default(),
        }
    }
}

impl Default for CredentialStorage {
    fn default() -> Self {
        Self {
            storage_type: CredentialStorageType::SQLite,
            encryption_key_id: "credential_encryption_key".to_string(),
            database_path: Some("./data/credentials.db".to_string()),
            vault: None,
        }
    }
}

impl Default for AuditConfig {
    fn default() -> Self {
        Self {
            enabled: true,
            backend: AuditBackend::File,
            retention_days: 90,
            log_level: AuditLogLevel::Info,
            include_data: false,
            max_file_size_mb: 100,
            format: AuditLogFormat::Json,
            database: None,
            syslog: None,
        }
    }
}

impl Default for MasterKeyConfig {
    fn default() -> Self {
        Self {
            derivation_method: KeyDerivationMethod::Hkdf,
            source: KeySource::Generated,
            key_file_path: None,
            env_var_name: None,
            salt: None,
            iterations: 100_000,
        }
    }
}

impl Default for EncryptionConfig {
    fn default() -> Self {
        Self {
            default_algorithm: "AES-256-GCM".to_string(),
            key_size_bits: 256,
            authenticated: true,
            aad_source: None,
        }
    }
}

impl Default for BackupConfig {
    fn default() -> Self {
        Self {
            enabled: true,
            backup_dir: "./backups/kms".to_string(),
            interval_hours: 24,
            max_backups: 7,
            compress: true,
            encrypt: true,
        }
    }
}

impl Default for DatabasePoolConfig {
    fn default() -> Self {
        Self {
            max_connections: 10,
            connect_timeout_seconds: 30,
            idle_timeout_seconds: 600,
            max_lifetime_seconds: 1800,
        }
    }
}

impl Default for RetryConfig {
    fn default() -> Self {
        Self {
            max_attempts: 3,
            initial_delay_ms: 1000,
            max_delay_ms: 30000,
            backoff_multiplier: 2.0,
        }
    }
}

impl Default for RateLimitConfig {
    fn default() -> Self {
        Self {
            enabled: false,
            requests_per_second: 100,
            burst_size: 10,
        }
    }
}

impl Default for RefreshConfig {
    fn default() -> Self {
        Self {
            enabled: true,
            check_interval_seconds: 300, // 5 minutes
            threshold_percentage: 0.8,   // 80%
            max_attempts: 3,
        }
    }
}

impl Default for InjectionConfig {
    fn default() -> Self {
        Self {
            enabled: false,
            methods: vec![InjectionMethod::Environment],
            targets: HashMap::new(),
        }
    }
}

impl Default for SecurityConfig {
    fn default() -> Self {
        Self {
            require_strong_auth: true,
            min_key_length_bits: 256,
            max_key_age_days: 90,
            enable_pfs: true,
            allowed_algorithms: vec![
                "AES-256-GCM".to_string(),
                "ChaCha20Poly1305".to_string(),
                "RSA-4096".to_string(),
                "ECDSA-P384".to_string(),
            ],
            blocked_algorithms: vec![
                "DES".to_string(),
                "3DES".to_string(),
                "RC4".to_string(),
                "MD5".to_string(),
            ],
            policy_enforcement: PolicyEnforcement::Strict,
        }
    }
}

impl Default for PerformanceConfig {
    fn default() -> Self {
        Self {
            worker_threads: None, // Use system default
            operation_timeout_seconds: 30,
            batch: BatchConfig::default(),
            memory: MemoryConfig::default(),
        }
    }
}

impl Default for BatchConfig {
    fn default() -> Self {
        Self {
            enabled: true,
            max_batch_size: 100,
            timeout_seconds: 10,
        }
    }
}

impl Default for MemoryConfig {
    fn default() -> Self {
        Self {
            max_cache_size_mb: 500,
            warning_threshold_mb: 1000,
        }
    }
}

impl ConfigValidation for KmsConfig {
    fn validate(&self) -> Result<(), KmsError> {
        // Validate using the validator crate
        Validate::validate(self)
            .map_err(|e| KmsError::validation("config", format!("Validation failed: {}", e)))?;

        // Custom validation logic
        match self.mode {
            KmsMode::Local => {
                if self.local.database_path.is_empty() {
                    return Err(KmsError::validation(
                        "local.database_path",
                        "Database path cannot be empty",
                    ));
                }
            }
            KmsMode::Remote => {
                if self.remote.server_url.is_empty() {
                    return Err(KmsError::validation(
                        "remote.server_url",
                        "Server URL cannot be empty",
                    ));
                }
            }
            KmsMode::Hybrid => {
                if self.local.database_path.is_empty() {
                    return Err(KmsError::validation(
                        "local.database_path",
                        "Database path required for hybrid mode",
                    ));
                }
                if self.remote.server_url.is_empty() {
                    return Err(KmsError::validation(
                        "remote.server_url",
                        "Server URL required for hybrid mode",
                    ));
                }
            }
        }

        Ok(())
    }

    fn get_warnings(&self) -> Vec<String> {
        let mut warnings = Vec::new();

        if !self.audit.enabled {
            warnings
                .push("Audit logging is disabled - consider enabling for compliance".to_string());
        }

        if !self.rotation.enabled {
            warnings.push("Key rotation is disabled - consider enabling for security".to_string());
        }

        if !self.remote.verify_ssl {
            warnings.push("SSL verification is disabled - this reduces security".to_string());
        }

        if self.security.policy_enforcement != PolicyEnforcement::Strict {
            warnings.push(
                "Security policy enforcement is not strict - consider using strict mode"
                    .to_string(),
            );
        }

        warnings
    }

    fn summary(&self) -> HashMap<String, String> {
        let mut summary = HashMap::new();

        summary.insert("mode".to_string(), format!("{:?}", self.mode));
        summary.insert("cache_enabled".to_string(), self.cache.enabled.to_string());
        summary.insert("audit_enabled".to_string(), self.audit.enabled.to_string());
        summary.insert(
            "rotation_enabled".to_string(),
            self.rotation.enabled.to_string(),
        );
        summary.insert("hsm_enabled".to_string(), self.hsm.enabled.to_string());
        summary.insert("zkp_enabled".to_string(), self.zkp.enabled.to_string());
        summary.insert(
            "credentials_enabled".to_string(),
            self.credentials.enabled.to_string(),
        );

        summary
    }
}

/// Load KMS configuration from TOML file
pub async fn load_kms_config(config_path: &Path) -> Result<KmsConfig, KmsError> {
    let content = tokio::fs::read_to_string(config_path)
        .await
        .map_err(|e| KmsError::io(format!("Failed to read config file: {}", e)))?;

    let config: KmsConfig = toml::from_str(&content)
        .map_err(|e| KmsError::config(format!("Failed to parse TOML config: {}", e)))?;

    // Validate configuration
    ConfigValidation::validate(&config)?;

    Ok(config)
}

/// Save KMS configuration to TOML file
pub async fn save_kms_config(config: &KmsConfig, config_path: &Path) -> Result<(), KmsError> {
    // Validate before saving
    ConfigValidation::validate(config)?;

    let content = toml::to_string_pretty(config)
        .map_err(|e| KmsError::serialization(format!("Failed to serialize config: {}", e)))?;

    // Ensure directory exists
    if let Some(parent) = config_path.parent() {
        tokio::fs::create_dir_all(parent)
            .await
            .map_err(|e| KmsError::io(format!("Failed to create config directory: {}", e)))?;
    }

    tokio::fs::write(config_path, content)
        .await
        .map_err(|e| KmsError::io(format!("Failed to write config file: {}", e)))?;

    Ok(())
}

// Validation functions
fn validate_local_config(config: &LocalConfig) -> Result<(), ValidationError> {
    if config.database_path.is_empty() {
        return Err(ValidationError::new("Database path cannot be empty"));
    }
    Ok(())
}

fn validate_remote_config(config: &RemoteConfig) -> Result<(), ValidationError> {
    if config.server_url.is_empty() {
        return Err(ValidationError::new("Server URL cannot be empty"));
    }
    Ok(())
}
