use anyhow::Result;
use serde::{Deserialize, Serialize};
use std::path::Path;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MarkBaseConfig {
    pub server: ServerConfig,
    pub postgresql: PostgreSQLConfig,
    pub authentication: AuthenticationConfig,
    pub test: TestConfig,
    pub logging: LoggingConfig,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ServerConfig {
    pub host: String,
    pub port: u16,
    pub log_level: String,
    pub auth_db_path: String,
    pub users_db_dir: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PostgreSQLConfig {
    pub host: String,
    pub port: u16,
    pub user: String,
    pub password: String,
    pub database: String,
    pub connection_pool_size: u8,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AuthenticationConfig {
    pub bcrypt_cost: u32,
    pub token_validity_hours: u8,
    pub session_storage: String,
    pub max_sessions_per_user: u8,
    pub default_user: String,
    pub default_password: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TestConfig {
    pub users: Vec<String>,
    pub password: String,
    pub login_test_iterations: u16,
    pub verify_test_iterations: u16,
    pub api_test_iterations: u16,
    pub performance_report: bool,
    pub output_format: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LoggingConfig {
    pub level: String,
    pub file_path: String,
    pub console_output: bool,
    pub structured_logging: bool,
}

impl MarkBaseConfig {
    pub fn load(path: &Path) -> Result<Self> {
        let content = std::fs::read_to_string(path)?;
        let config: MarkBaseConfig = toml::from_str(&content)?;
        Ok(config)
    }
    
    pub fn save(&self, path: &Path) -> Result<()> {
        let content = toml::to_string_pretty(self)?;
        std::fs::write(path, content)?;
        Ok(())
    }
    
    pub fn default_config() -> Self {
        Self {
            server: ServerConfig {
                host: "127.0.0.1".to_string(),
                port: 11438,
                log_level: "info".to_string(),
                auth_db_path: "data/auth.sqlite".to_string(),
                users_db_dir: "data/users".to_string(),
            },
            postgresql: PostgreSQLConfig {
                host: "127.0.0.1".to_string(),
                port: 5432,
                user: "sftpgo".to_string(),
                password: "sftpgo_pass_2026".to_string(),
                database: "sftpgo".to_string(),
                connection_pool_size: 5,
            },
            authentication: AuthenticationConfig {
                bcrypt_cost: 10,
                token_validity_hours: 24,
                session_storage: "memory".to_string(),
                max_sessions_per_user: 5,
                default_user: "demo".to_string(),
                default_password: "demo123".to_string(),
            },
            test: TestConfig {
                users: vec!["warren".to_string(), "momentry".to_string(), "demo".to_string()],
                password: "demo123".to_string(),
                login_test_iterations: 10,
                verify_test_iterations: 100,
                api_test_iterations: 50,
                performance_report: true,
                output_format: "markdown".to_string(),
            },
            logging: LoggingConfig {
                level: "info".to_string(),
                file_path: "logs/markbase.log".to_string(),
                console_output: true,
                structured_logging: false,
            },
        }
    }
    
    pub fn merge_env(&mut self) {
        if let Ok(host) = std::env::var("MB_HOST") {
            self.server.host = host;
        }
        if let Ok(port) = std::env::var("MB_PORT") {
            if let Ok(p) = port.parse() {
                self.server.port = p;
            }
        }
        if let Ok(log_level) = std::env::var("MB_LOG_LEVEL") {
            self.server.log_level = log_level;
        }
        
        if let Ok(pg_host) = std::env::var("PG_HOST") {
            self.postgresql.host = pg_host;
        }
        if let Ok(pg_port) = std::env::var("PG_PORT") {
            if let Ok(p) = pg_port.parse() {
                self.postgresql.port = p;
            }
        }
        if let Ok(pg_user) = std::env::var("PG_USER") {
            self.postgresql.user = pg_user;
        }
        if let Ok(pg_password) = std::env::var("PG_PASSWORD") {
            self.postgresql.password = pg_password;
        }
        if let Ok(pg_database) = std::env::var("PG_DATABASE") {
            self.postgresql.database = pg_database;
        }
        
        if let Ok(bcrypt_cost) = std::env::var("MB_BCRYPT_COST") {
            if let Ok(c) = bcrypt_cost.parse() {
                self.authentication.bcrypt_cost = c;
            }
        }
        if let Ok(token_hours) = std::env::var("MB_TOKEN_VALIDITY_HOURS") {
            if let Ok(h) = token_hours.parse() {
                self.authentication.token_validity_hours = h;
            }
        }
    }
    
    pub fn get(&self, key: &str) -> Option<String> {
        match key {
            "server.host" => Some(self.server.host.clone()),
            "server.port" => Some(self.server.port.to_string()),
            "server.log_level" => Some(self.server.log_level.clone()),
            "server.auth_db_path" => Some(self.server.auth_db_path.clone()),
            "server.users_db_dir" => Some(self.server.users_db_dir.clone()),
            
            "postgresql.host" => Some(self.postgresql.host.clone()),
            "postgresql.port" => Some(self.postgresql.port.to_string()),
            "postgresql.user" => Some(self.postgresql.user.clone()),
            "postgresql.password" => Some(self.postgresql.password.clone()),
            "postgresql.database" => Some(self.postgresql.database.clone()),
            "postgresql.connection_pool_size" => Some(self.postgresql.connection_pool_size.to_string()),
            
            "authentication.bcrypt_cost" => Some(self.authentication.bcrypt_cost.to_string()),
            "authentication.token_validity_hours" => Some(self.authentication.token_validity_hours.to_string()),
            "authentication.session_storage" => Some(self.authentication.session_storage.clone()),
            "authentication.max_sessions_per_user" => Some(self.authentication.max_sessions_per_user.to_string()),
            "authentication.default_user" => Some(self.authentication.default_user.clone()),
            "authentication.default_password" => Some(self.authentication.default_password.clone()),
            
            "test.users" => Some(serde_json::to_string(&self.test.users).unwrap_or_default()),
            "test.password" => Some(self.test.password.clone()),
            "test.login_test_iterations" => Some(self.test.login_test_iterations.to_string()),
            "test.verify_test_iterations" => Some(self.test.verify_test_iterations.to_string()),
            "test.api_test_iterations" => Some(self.test.api_test_iterations.to_string()),
            "test.performance_report" => Some(self.test.performance_report.to_string()),
            "test.output_format" => Some(self.test.output_format.clone()),
            
            "logging.level" => Some(self.logging.level.clone()),
            "logging.file_path" => Some(self.logging.file_path.clone()),
            "logging.console_output" => Some(self.logging.console_output.to_string()),
            "logging.structured_logging" => Some(self.logging.structured_logging.to_string()),
            
            _ => None,
        }
    }
    
    pub fn set(&mut self, key: &str, value: &str) -> Result<()> {
        match key {
            "server.host" => self.server.host = value.to_string(),
            "server.port" => self.server.port = value.parse()?,
            "server.log_level" => self.server.log_level = value.to_string(),
            "server.auth_db_path" => self.server.auth_db_path = value.to_string(),
            "server.users_db_dir" => self.server.users_db_dir = value.to_string(),
            
            "postgresql.host" => self.postgresql.host = value.to_string(),
            "postgresql.port" => self.postgresql.port = value.parse()?,
            "postgresql.user" => self.postgresql.user = value.to_string(),
            "postgresql.password" => self.postgresql.password = value.to_string(),
            "postgresql.database" => self.postgresql.database = value.to_string(),
            "postgresql.connection_pool_size" => self.postgresql.connection_pool_size = value.parse()?,
            
            "authentication.bcrypt_cost" => self.authentication.bcrypt_cost = value.parse()?,
            "authentication.token_validity_hours" => self.authentication.token_validity_hours = value.parse()?,
            "authentication.session_storage" => self.authentication.session_storage = value.to_string(),
            "authentication.max_sessions_per_user" => self.authentication.max_sessions_per_user = value.parse()?,
            "authentication.default_user" => self.authentication.default_user = value.to_string(),
            "authentication.default_password" => self.authentication.default_password = value.to_string(),
            
            "test.password" => self.test.password = value.to_string(),
            "test.login_test_iterations" => self.test.login_test_iterations = value.parse()?,
            "test.verify_test_iterations" => self.test.verify_test_iterations = value.parse()?,
            "test.api_test_iterations" => self.test.api_test_iterations = value.parse()?,
            "test.performance_report" => self.test.performance_report = value.parse()?,
            "test.output_format" => self.test.output_format = value.to_string(),
            
            "logging.level" => self.logging.level = value.to_string(),
            "logging.file_path" => self.logging.file_path = value.to_string(),
            "logging.console_output" => self.logging.console_output = value.parse()?,
            "logging.structured_logging" => self.logging.structured_logging = value.parse()?,
            
            _ => return Err(anyhow::anyhow!("Invalid config key: {}", key)),
        }
        Ok(())
    }
    
    pub fn validate(&self) -> Result<()> {
        if self.server.port < 1024 {
            return Err(anyhow::anyhow!("Invalid server port: {}. Must be >= 1024", self.server.port));
        }
        
        if self.postgresql.port == 0 {
            return Err(anyhow::anyhow!("Invalid PostgreSQL port: {}", self.postgresql.port));
        }
        
        if self.authentication.bcrypt_cost < 4 || self.authentication.bcrypt_cost > 31 {
            return Err(anyhow::anyhow!("Invalid bcrypt_cost: {}. Must be 4-31", self.authentication.bcrypt_cost));
        }
        
        if self.authentication.token_validity_hours == 0 {
            return Err(anyhow::anyhow!("Invalid token_validity_hours: {}. Must be >= 1", 
                self.authentication.token_validity_hours));
        }
        
        if self.test.users.is_empty() {
            return Err(anyhow::anyhow!("test.users must not be empty"));
        }
        
        Ok(())
    }
}