// Configuration module for VAPORA Backend
// Loads config from vapora.toml with environment variable interpolation

use std::fs;
use std::path::Path;

use serde::{Deserialize, Serialize};
use vapora_shared::{Result, VaporaError};

/// Main configuration structure
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Config {
    pub server: ServerConfig,
    pub database: DatabaseConfig,
    pub nats: NatsConfig,
    pub auth: AuthConfig,
    pub logging: LoggingConfig,
    pub metrics: MetricsConfig,
}

/// Server configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ServerConfig {
    pub host: String,
    pub port: u16,
    pub tls: TlsConfig,
}

/// TLS configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TlsConfig {
    pub enabled: bool,
    #[serde(default)]
    pub cert_path: String,
    #[serde(default)]
    pub key_path: String,
}

/// Database configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DatabaseConfig {
    pub url: String,
    pub max_connections: u32,
}

/// NATS configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NatsConfig {
    pub url: String,
    pub stream_name: String,
}

/// Authentication configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AuthConfig {
    pub jwt_secret: String,
    pub jwt_expiration_hours: u32,
}

/// Logging configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LoggingConfig {
    pub level: String,
    pub json: bool,
}

/// Metrics configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MetricsConfig {
    pub enabled: bool,
    pub port: u16,
}

impl Config {
    /// Load configuration from a TOML file with environment variable
    /// interpolation
    pub fn load<P: AsRef<Path>>(path: P) -> Result<Self> {
        let path = path.as_ref();

        // Read file content
        let content = fs::read_to_string(path).map_err(|e| {
            VaporaError::ConfigError(format!("Failed to read config file {:?}: {}", path, e))
        })?;

        // Interpolate environment variables
        let interpolated = Self::interpolate_env_vars(&content)?;

        // Parse TOML
        let config: Config = toml::from_str(&interpolated)?;

        // Validate configuration
        config.validate()?;

        Ok(config)
    }

    /// Interpolate environment variables in format ${VAR} or ${VAR:-default}
    fn interpolate_env_vars(content: &str) -> Result<String> {
        let mut result = content.to_string();
        let re = regex::Regex::new(r"\$\{([^}:]+)(?::-(.*?))?\}")
            .map_err(|e| VaporaError::ConfigError(format!("Invalid regex pattern: {}", e)))?;

        // Process each match
        for cap in re.captures_iter(content) {
            let full_match = cap
                .get(0)
                .ok_or_else(|| VaporaError::ConfigError("Failed to get regex match".to_string()))?;
            let var_name = cap
                .get(1)
                .ok_or_else(|| VaporaError::ConfigError("Failed to get variable name".to_string()))?
                .as_str();
            let default_value = cap.get(2).map(|m| m.as_str()).unwrap_or("");

            // Get environment variable or use default
            let value = std::env::var(var_name).unwrap_or_else(|_| default_value.to_string());

            // Replace in result
            result = result.replace(full_match.as_str(), &value);
        }

        Ok(result)
    }

    /// Validate configuration values
    fn validate(&self) -> Result<()> {
        // Validate server config
        if self.server.host.is_empty() {
            return Err(VaporaError::ConfigError(
                "Server host cannot be empty".to_string(),
            ));
        }
        if self.server.port == 0 {
            return Err(VaporaError::ConfigError(
                "Server port must be > 0".to_string(),
            ));
        }

        // Validate TLS config if enabled
        if self.server.tls.enabled {
            if self.server.tls.cert_path.is_empty() {
                return Err(VaporaError::ConfigError(
                    "TLS cert_path required when TLS is enabled".to_string(),
                ));
            }
            if self.server.tls.key_path.is_empty() {
                return Err(VaporaError::ConfigError(
                    "TLS key_path required when TLS is enabled".to_string(),
                ));
            }
        }

        // Validate database config
        if self.database.url.is_empty() {
            return Err(VaporaError::ConfigError(
                "Database URL cannot be empty".to_string(),
            ));
        }
        if self.database.max_connections == 0 {
            return Err(VaporaError::ConfigError(
                "Database max_connections must be > 0".to_string(),
            ));
        }

        // Validate NATS config
        if self.nats.url.is_empty() {
            return Err(VaporaError::ConfigError(
                "NATS URL cannot be empty".to_string(),
            ));
        }

        // Validate auth config
        if self.auth.jwt_secret.is_empty() {
            return Err(VaporaError::ConfigError(
                "JWT secret cannot be empty".to_string(),
            ));
        }
        if self.auth.jwt_expiration_hours == 0 {
            return Err(VaporaError::ConfigError(
                "JWT expiration hours must be > 0".to_string(),
            ));
        }

        // Validate logging config
        let valid_log_levels = ["trace", "debug", "info", "warn", "error"];
        if !valid_log_levels.contains(&self.logging.level.as_str()) {
            return Err(VaporaError::ConfigError(format!(
                "Invalid log level '{}'. Must be one of: {:?}",
                self.logging.level, valid_log_levels
            )));
        }

        Ok(())
    }
}

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

    #[test]
    fn test_env_var_interpolation() {
        std::env::set_var("TEST_VAR", "test_value");

        let input = "host = \"${TEST_VAR}\"";
        let result = Config::interpolate_env_vars(input).unwrap();
        assert_eq!(result, "host = \"test_value\"");
    }

    #[test]
    fn test_env_var_with_default() {
        let input = "host = \"${NONEXISTENT_VAR:-default_value}\"";
        let result = Config::interpolate_env_vars(input).unwrap();
        assert_eq!(result, "host = \"default_value\"");
    }

    #[test]
    fn test_validate_empty_host() {
        let config = Config {
            server: ServerConfig {
                host: "".to_string(),
                port: 8080,
                tls: TlsConfig {
                    enabled: false,
                    cert_path: "".to_string(),
                    key_path: "".to_string(),
                },
            },
            database: DatabaseConfig {
                url: "ws://localhost:8000".to_string(),
                max_connections: 10,
            },
            nats: NatsConfig {
                url: "nats://localhost:4222".to_string(),
                stream_name: "vapora".to_string(),
            },
            auth: AuthConfig {
                jwt_secret: "secret".to_string(),
                jwt_expiration_hours: 24,
            },
            logging: LoggingConfig {
                level: "info".to_string(),
                json: false,
            },
            metrics: MetricsConfig {
                enabled: true,
                port: 9090,
            },
        };

        assert!(config.validate().is_err());
    }
}
