use std::path::Path;

use serde::{Deserialize, Serialize};
use tracing::info;

use crate::error::{infrastructure, ControlCenterError, Result};

/// Main configuration structure
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Config {
    pub server: ServerConfig,
    pub database: DatabaseConfig,
    pub jwt: JwtConfig,
    pub rate_limiting: RateLimitConfig,
    pub cors: CorsConfig,
    pub security: SecurityConfig,
    pub logging: LoggingConfig,
}

/// Server configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ServerConfig {
    pub host: String,
    pub port: u16,
    pub workers: Option<usize>,
    pub keep_alive: Option<u64>,
    pub max_connections: Option<usize>,
}

/// Database configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DatabaseConfig {
    pub url: String,
    pub namespace: String,
    pub database: String,
    pub username: Option<String>,
    pub password: Option<String>,
}

/// JWT configuration
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct JwtConfig {
    #[serde(default)]
    pub issuer: String,
    #[serde(default)]
    pub audience: String,
    #[serde(default)]
    pub access_token_expiration_hours: i64,
    #[serde(default)]
    pub refresh_token_expiration_hours: i64,
    #[serde(default)]
    pub private_key_pem: String,
    #[serde(default)]
    pub public_key_pem: String,
}

/// Rate limiting configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RateLimitConfig {
    pub max_requests: u32,
    pub window_seconds: u64,
    pub per_ip: bool,
    pub global: bool,
}

/// CORS configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CorsConfig {
    pub allowed_origins: Vec<String>,
    pub allowed_methods: Vec<String>,
    pub allowed_headers: Vec<String>,
    pub expose_headers: Vec<String>,
    pub max_age: u64,
    pub allow_credentials: bool,
}

/// Security configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SecurityConfig {
    pub session_cleanup_interval_minutes: u64,
    pub max_sessions_per_user: Option<usize>,
    pub password_min_length: usize,
    pub password_require_special_chars: bool,
    pub password_require_numbers: bool,
    pub password_require_uppercase: bool,
    pub failed_login_lockout_attempts: Option<u32>,
    pub failed_login_lockout_duration_minutes: Option<u64>,
}

/// Logging configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LoggingConfig {
    pub level: String,
    pub format: String,
    pub file_path: Option<String>,
    pub max_file_size: Option<String>,
    pub max_files: Option<usize>,
}

impl Default for Config {
    fn default() -> Self {
        // Generate default JWT keys
        let key_pair = crate::services::jwt::generate_rsa_key_pair()
            .expect("Failed to generate default RSA key pair");

        Self {
            server: ServerConfig::default(),
            database: DatabaseConfig::default(),
            jwt: JwtConfig {
                issuer: "control-center".to_string(),
                audience: "control-center-api".to_string(),
                access_token_expiration_hours: 1,
                refresh_token_expiration_hours: 24 * 7,
                private_key_pem: key_pair.private_key_pem,
                public_key_pem: key_pair.public_key_pem,
            },
            rate_limiting: RateLimitConfig::default(),
            cors: CorsConfig::default(),
            security: SecurityConfig::default(),
            logging: LoggingConfig::default(),
        }
    }
}

impl Default for ServerConfig {
    fn default() -> Self {
        Self {
            host: "0.0.0.0".to_string(),
            port: 9012,
            workers: None,
            keep_alive: Some(75),
            max_connections: Some(1000),
        }
    }
}

impl Default for DatabaseConfig {
    fn default() -> Self {
        Self {
            url: "127.0.0.1:8000".to_string(),
            namespace: "control_center".to_string(),
            database: "main".to_string(),
            username: Some("root".to_string()),
            password: Some("root".to_string()),
        }
    }
}

impl Default for RateLimitConfig {
    fn default() -> Self {
        Self {
            max_requests: 100,
            window_seconds: 60,
            per_ip: true,
            global: false,
        }
    }
}

impl Default for CorsConfig {
    fn default() -> Self {
        Self {
            allowed_origins: vec!["http://localhost:3000".to_string()],
            allowed_methods: vec![
                "GET".to_string(),
                "POST".to_string(),
                "PUT".to_string(),
                "DELETE".to_string(),
                "PATCH".to_string(),
                "OPTIONS".to_string(),
            ],
            allowed_headers: vec![
                "content-type".to_string(),
                "authorization".to_string(),
                "accept".to_string(),
                "x-requested-with".to_string(),
                "x-session-id".to_string(),
            ],
            expose_headers: vec![
                "x-total-count".to_string(),
                "x-rate-limit-remaining".to_string(),
                "x-rate-limit-limit".to_string(),
                "x-rate-limit-reset".to_string(),
            ],
            max_age: 86400, // 24 hours
            allow_credentials: true,
        }
    }
}

impl Default for SecurityConfig {
    fn default() -> Self {
        Self {
            session_cleanup_interval_minutes: 60, // 1 hour
            max_sessions_per_user: Some(5),
            password_min_length: 8,
            password_require_special_chars: false,
            password_require_numbers: false,
            password_require_uppercase: false,
            failed_login_lockout_attempts: Some(5),
            failed_login_lockout_duration_minutes: Some(15),
        }
    }
}

impl Default for LoggingConfig {
    fn default() -> Self {
        Self {
            level: "info".to_string(),
            format: "json".to_string(),
            file_path: None,
            max_file_size: Some("100MB".to_string()),
            max_files: Some(10),
        }
    }
}

impl Config {
    /// Load configuration from file
    pub fn load_from_file<P: AsRef<Path>>(path: P) -> Result<Self> {
        let content = std::fs::read_to_string(path).map_err(|e| {
            ControlCenterError::Infrastructure(infrastructure::InfrastructureError::Configuration(
                format!("Failed to read config file: {}", e),
            ))
        })?;

        let config: Config = toml::from_str(&content).map_err(|e| {
            ControlCenterError::Infrastructure(infrastructure::InfrastructureError::Configuration(
                format!("Failed to parse config file: {}", e),
            ))
        })?;

        config.validate()?;

        info!("Configuration loaded successfully");
        Ok(config)
    }

    /// Load configuration from environment variables and files
    pub fn load() -> Result<Self> {
        // Try to load from file first
        let config_paths = [
            "config.toml",
            "config/config.toml",
            "/etc/control-center/config.toml",
        ];

        for path in &config_paths {
            if Path::new(path).exists() {
                info!("Loading configuration from: {}", path);
                return Self::load_from_file(path);
            }
        }

        // If no config file found, use defaults
        info!("No config file found, using defaults");
        let config = Self::default();
        config.validate()?;

        Ok(config)
    }

    /// Validate configuration
    pub fn validate(&self) -> Result<()> {
        // Validate server configuration
        if self.server.port == 0 {
            return Err(ControlCenterError::Infrastructure(
                infrastructure::InfrastructureError::Configuration(
                    "Server port must be greater than 0".to_string(),
                ),
            ));
        }

        // Validate JWT configuration
        if self.jwt.access_token_expiration_hours == 0 {
            return Err(ControlCenterError::Infrastructure(
                infrastructure::InfrastructureError::Configuration(
                    "JWT access token expiration must be greater than 0".to_string(),
                ),
            ));
        }

        // Validate security configuration
        if self.security.password_min_length < 4 {
            return Err(ControlCenterError::Infrastructure(
                infrastructure::InfrastructureError::Configuration(
                    "Password minimum length must be at least 4".to_string(),
                ),
            ));
        }

        // Validate logging level
        match self.logging.level.to_lowercase().as_str() {
            "trace" | "debug" | "info" | "warn" | "error" => {}
            _ => {
                return Err(ControlCenterError::Infrastructure(
                    infrastructure::InfrastructureError::Configuration(
                        "Invalid logging level. Must be one of: trace, debug, info, warn, error"
                            .to_string(),
                    ),
                ));
            }
        }

        Ok(())
    }

    /// Save configuration to file
    pub fn save_to_file<P: AsRef<Path>>(&self, path: P) -> Result<()> {
        let content = toml::to_string_pretty(self).map_err(|e| {
            ControlCenterError::Infrastructure(infrastructure::InfrastructureError::Configuration(
                format!("Failed to serialize config: {}", e),
            ))
        })?;

        std::fs::write(path, content).map_err(|e| {
            ControlCenterError::Infrastructure(infrastructure::InfrastructureError::Configuration(
                format!("Failed to write config file: {}", e),
            ))
        })?;

        Ok(())
    }

    /// Get server bind address
    pub fn bind_address(&self) -> String {
        format!("{}:{}", self.server.host, self.server.port)
    }
}

/// Helper function to create a default config file
pub fn create_default_config_file<P: AsRef<Path>>(path: P) -> Result<()> {
    let config = Config::default();
    config.save_to_file(path)?;
    info!("Default configuration file created");
    Ok(())
}
