//! Configuration validation utilities

use crate::config::{ProtocolType, ServerConfig, WorkerClass};
use crate::errors::{FalcornError, Result};
use std::net::{IpAddr, SocketAddr};
use std::path::Path;

/// Configuration validator
pub struct ConfigValidator;

impl ConfigValidator {
    /// Validate complete configuration
    pub fn validate(config: &ServerConfig) -> Result<ValidationReport> {
        let mut report = ValidationReport::new();

        // Validate network settings
        Self::validate_network(config, &mut report)?;

        // Validate worker settings
        Self::validate_workers(config, &mut report)?;

        // Validate application settings
        Self::validate_application(config, &mut report)?;

        // Validate performance settings
        Self::validate_performance(config, &mut report)?;

        // Validate logging settings
        Self::validate_logging(config, &mut report)?;

        // Validate control settings
        Self::validate_control(config, &mut report)?;

        // Validate SSL settings
        Self::validate_ssl(config, &mut report)?;

        // Validate Server Interface
        Self::validate_server_interface(config, &mut report)?;

        if report.has_errors() {
            Err(FalcornError::InvalidConfig {
                reason: format!("{}", report.format_errors()),
            })
        } else {
            Ok(report)
        }
    }

    /// Validate network configuration
    fn validate_network(config: &ServerConfig, report: &mut ValidationReport) -> Result<()> {
        // Validate host
        if config.host.is_empty() {
            report.add_error("host cannot be empty");
        } else {
            // Try to parse as IP address
            if config.host.parse::<IpAddr>().is_err() && config.host != "localhost" {
                report.add_warning(&format!("host '{}' is not a valid IP address", config.host));
            }
        }

        // Validate port
        if config.port == 0 {
            report.add_error("port must be greater than 0");
        } else if config.port < 1024 {
            report.add_warning(&format!(
                "port {} requires root privileges on Unix systems",
                config.port
            ));
        }

        // Validate backlog
        if config.backlog == 0 {
            report.add_error("backlog must be greater than 0");
        } else if config.backlog > 65535 {
            report.add_warning("backlog is very large, may cause issues");
        }

        // Try to parse full socket address
        let addr_str = format!("{}:{}", config.host, config.port);
        if addr_str.parse::<SocketAddr>().is_err() {
            report.add_error(&format!("invalid bind address: {}", addr_str));
        }

        Ok(())
    }

    /// Validate worker configuration
    fn validate_workers(config: &ServerConfig, report: &mut ValidationReport) -> Result<()> {
        let worker_count = config.worker_count();

        if worker_count == 0 {
            report.add_error("worker count must be greater than 0");
        } else if worker_count > 256 {
            report.add_warning(&format!(
                "very high worker count ({}), may cause resource exhaustion",
                worker_count
            ));
        }

        // Validate timeout
        if config.worker_timeout == 0 {
            report.add_error("worker_timeout must be greater than 0");
        } else if config.worker_timeout > 300 {
            report.add_warning(&format!(
                "worker_timeout is very long ({}s)",
                config.worker_timeout
            ));
        }

        // Validate max_requests
        if let Some(max_req) = config.max_requests {
            if max_req == 0 {
                report.add_error("max_requests must be greater than 0 if set");
            } else if max_req < 100 {
                report.add_warning("max_requests is very low, workers will restart frequently");
            }
        }

        // Validate max_requests_jitter
        if let Some(jitter) = config.max_requests_jitter {
            if config.max_requests.is_none() {
                report.add_warning("max_requests_jitter is set but max_requests is not");
            } else if let Some(max_req) = config.max_requests {
                if jitter > max_req {
                    report.add_error("max_requests_jitter cannot be greater than max_requests");
                }
            }
        }

        Ok(())
    }

    /// Validate application configuration
    fn validate_application(config: &ServerConfig, report: &mut ValidationReport) -> Result<()> {
        // Validate module name
        if config.app_module.is_empty() {
            report.add_error("app_module cannot be empty");
        } else if config.app_module.contains(' ') {
            report.add_error("app_module cannot contain spaces");
        }

        // Validate callable name
        if config.app_callable.is_empty() {
            report.add_error("app_callable cannot be empty");
        } else if config.app_callable.contains(' ') {
            report.add_error("app_callable cannot contain spaces");
        }

        // Validate pythonpath entries
        for path in &config.pythonpath {
            let path_obj = Path::new(path);
            if !path_obj.exists() {
                report.add_warning(&format!("pythonpath entry does not exist: {}", path));
            }
        }

        if !matches!(
            config.asgi_event_loop.to_lowercase().as_str(),
            "asyncio" | "uvloop"
        ) {
            report.add_error("asgi_event_loop must be one of: asyncio, uvloop");
        }
        if !matches!(
            config.asgi_scheduler.to_lowercase().as_str(),
            "task" | "callback"
        ) {
            report.add_error("asgi_scheduler must be one of: task, callback");
        }
        if config.asgi_submit_timeout_ms == 0 {
            report.add_error("asgi_submit_timeout_ms must be greater than 0");
        }
        if config.asgi_max_inflight == 0 {
            report.add_error("asgi_max_inflight must be greater than 0");
        } else if config.asgi_max_inflight > 1_000_000 {
            report.add_warning(&format!(
                "asgi_max_inflight ({}) is very high",
                config.asgi_max_inflight
            ));
        }
        if config.asgi_send_pool_size == 0 {
            report.add_error("asgi_send_pool_size must be greater than 0");
        } else if config.asgi_send_pool_size > 1_000_000 {
            report.add_warning(&format!(
                "asgi_send_pool_size ({}) is very high",
                config.asgi_send_pool_size
            ));
        }

        Ok(())
    }

    /// Validate performance settings
    fn validate_performance(config: &ServerConfig, report: &mut ValidationReport) -> Result<()> {
        // Validate buffer size
        if config.buffer_size == 0 {
            report.add_error("buffer_size must be greater than 0");
        } else if config.buffer_size < 1024 {
            report.add_warning(&format!(
                "buffer_size ({}) is very small",
                config.buffer_size
            ));
        } else if config.buffer_size > 1_048_576 {
            report.add_warning(&format!(
                "buffer_size ({}) is very large",
                config.buffer_size
            ));
        }

        // Validate keepalive timeout
        if config.keepalive_timeout > 300 {
            report.add_warning(&format!(
                "keepalive_timeout is very long ({}s)",
                config.keepalive_timeout
            ));
        }

        // Validate max connections
        if config.max_connections == 0 {
            report.add_error("max_connections must be greater than 0");
        } else if config.max_connections > 100_000 {
            report.add_warning(&format!(
                "max_connections ({}) is very high",
                config.max_connections
            ));
        }

        // Validate sync worker concurrency settings
        if config.sync_handler_threads == 0 {
            report.add_error("sync_handler_threads must be greater than 0");
        } else if config.sync_handler_threads > 512 {
            report.add_warning(&format!(
                "sync_handler_threads ({}) is very high",
                config.sync_handler_threads
            ));
        }

        if config.sync_conn_queue_capacity == 0 {
            report.add_error("sync_conn_queue_capacity must be greater than 0");
        } else if config.sync_conn_queue_capacity > 1_000_000 {
            report.add_warning(&format!(
                "sync_conn_queue_capacity ({}) is very high",
                config.sync_conn_queue_capacity
            ));
        }

        // Validate async runtime settings
        if config.async_worker_threads == 0 {
            report.add_error("async_worker_threads must be greater than 0");
        } else if config.async_worker_threads > 256 {
            report.add_warning(&format!(
                "async_worker_threads ({}) is very high",
                config.async_worker_threads
            ));
        }

        if config.async_max_blocking_threads == 0 {
            report.add_error("async_max_blocking_threads must be greater than 0");
        } else if config.async_max_blocking_threads > 10_000 {
            report.add_warning(&format!(
                "async_max_blocking_threads ({}) is very high",
                config.async_max_blocking_threads
            ));
        }

        // Check if max_connections is reasonable for worker count
        let worker_count = config.worker_count();
        let conn_per_worker = config.max_connections / worker_count;
        if conn_per_worker > 10_000 {
            report.add_warning(&format!(
                "very high connections per worker ({}), may cause performance issues",
                conn_per_worker
            ));
        }

        Ok(())
    }

    /// Validate logging settings
    fn validate_logging(config: &ServerConfig, report: &mut ValidationReport) -> Result<()> {
        // Validate log level
        let valid_levels = ["TRACE", "DEBUG", "INFO", "WARN", "ERROR"];
        if !valid_levels.contains(&config.log_level.to_uppercase().as_str()) {
            report.add_error(&format!(
                "invalid log_level '{}', must be one of: {}",
                config.log_level,
                valid_levels.join(", ")
            ));
        }

        // Validate log paths
        if let Some(ref access_log) = config.access_log {
            Self::validate_log_path(access_log, "access_log", report);
        }

        if let Some(ref error_log) = config.error_log {
            Self::validate_log_path(error_log, "error_log", report);
        }
        if let Some(ref plugin_socket) = config.plugin_socket {
            if plugin_socket.trim().is_empty() {
                report.add_error("plugin_socket cannot be empty");
            } else {
                Self::validate_log_path(plugin_socket, "plugin_socket", report);
            }
        }
        if let Some(ref plugin_auth_token) = config.plugin_auth_token {
            if plugin_auth_token.trim().is_empty() {
                report.add_error("plugin_auth_token cannot be empty");
            }
            if config.plugin_socket.is_none() {
                report.add_warning("plugin_auth_token is set but plugin_socket is not configured");
            }
        }
        match config.plugin_drop_policy.to_lowercase().as_str() {
            "drop_oldest" | "disconnect" => {}
            other => {
                report.add_error(&format!(
                    "invalid plugin_drop_policy '{}', expected 'drop_oldest' or 'disconnect'",
                    other
                ));
            }
        }
        if config.plugin_max_lagged_events == 0 {
            report.add_error("plugin_max_lagged_events must be greater than 0");
        }
        if config.plugin_write_timeout_ms == 0 {
            report.add_error("plugin_write_timeout_ms must be greater than 0");
        }
        if config.plugin_bus_capacity == 0 {
            report.add_error("plugin_bus_capacity must be greater than 0");
        }
        if config.plugin_bus_capacity < 128 {
            report.add_warning("plugin_bus_capacity is low; slow plugins may drop too many events");
        }
        if config.plugin_max_clients == 0 {
            report.add_error("plugin_max_clients must be greater than 0");
        }
        if config.plugin_subscribe_timeout_ms == 0 {
            report.add_error("plugin_subscribe_timeout_ms must be greater than 0");
        }
        if config.plugin_auth_fail_window_secs == 0 {
            report.add_error("plugin_auth_fail_window_secs must be greater than 0");
        }
        if config.plugin_auth_fail_max_attempts == 0 {
            report.add_error("plugin_auth_fail_max_attempts must be greater than 0");
        }
        match config.plugin_acl_mode.to_lowercase().as_str() {
            "off" | "same_user" => {}
            other => {
                report.add_error(&format!(
                    "invalid plugin_acl_mode '{}', expected 'off' or 'same_user'",
                    other
                ));
            }
        }
        if config.plugin_socket_mode == 0 || config.plugin_socket_mode > 0o777 {
            report.add_error("plugin_socket_mode must be a valid Unix permission (0o001..0o777)");
        }
        if config.metrics_interval_ms == 0 {
            report.add_error("metrics_interval_ms must be greater than 0");
        } else if config.metrics_interval_ms < 200 {
            report.add_warning("metrics_interval_ms is very low; may increase overhead");
        }
        if config.metrics_top_errors_limit == 0 {
            report.add_error("metrics_top_errors_limit must be greater than 0");
        }

        Ok(())
    }

    /// Validate control settings
    fn validate_control(config: &ServerConfig, report: &mut ValidationReport) -> Result<()> {
        if let Some(ref control_socket) = config.control_socket {
            if control_socket.trim().is_empty() {
                report.add_error("control_socket cannot be empty");
            } else {
                Self::validate_log_path(control_socket, "control_socket", report);
            }
        }
        if let Some(ref control_auth_token) = config.control_auth_token {
            if control_auth_token.trim().is_empty() {
                report.add_error("control_auth_token cannot be empty");
            }
            if config.control_socket.is_none() {
                report
                    .add_warning("control_auth_token is set but control_socket is not configured");
            }
        }
        match config.control_acl_mode.to_lowercase().as_str() {
            "off" | "same_user" => {}
            other => {
                report.add_error(&format!(
                    "invalid control_acl_mode '{}', expected 'off' or 'same_user'",
                    other
                ));
            }
        }
        if config.control_socket_mode == 0 || config.control_socket_mode > 0o777 {
            report.add_error("control_socket_mode must be a valid Unix permission (0o001..0o777)");
        }

        Ok(())
    }

    /// Validate SSL configuration
    fn validate_ssl(config: &ServerConfig, report: &mut ValidationReport) -> Result<()> {
        if config.ssl_enabled {
            // Check if both cert and key are provided
            if config.ssl_certfile.is_none() {
                report.add_error("ssl_certfile must be set when SSL is enabled");
            }

            if config.ssl_keyfile.is_none() {
                report.add_error("ssl_keyfile must be set when SSL is enabled");
            }

            // Validate cert file
            if let Some(ref certfile) = config.ssl_certfile {
                let cert_path = Path::new(certfile);
                if !cert_path.exists() {
                    report.add_error(&format!("ssl_certfile does not exist: {}", certfile));
                }
            }

            // Validate key file
            if let Some(ref keyfile) = config.ssl_keyfile {
                let key_path = Path::new(keyfile);
                if !key_path.exists() {
                    report.add_error(&format!("ssl_keyfile does not exist: {}", keyfile));
                }
            }
        } else {
            // Warn if SSL files are set but SSL is disabled
            if config.ssl_certfile.is_some() || config.ssl_keyfile.is_some() {
                report.add_warning("SSL certificate/key files are set but SSL is not enabled");
            }
        }

        Ok(())
    }

    /// Validate log file path
    fn validate_log_path(path: &str, name: &str, report: &mut ValidationReport) {
        let log_path = Path::new(path);

        // Check if parent directory exists
        if let Some(parent) = log_path.parent() {
            if !parent.exists() {
                report.add_warning(&format!(
                    "{} parent directory does not exist: {}",
                    name,
                    parent.display()
                ));
            }
        }

        // Check if file is writable (if it exists)
        if log_path.exists() {
            if let Ok(metadata) = log_path.metadata() {
                if metadata.permissions().readonly() {
                    report.add_error(&format!("{} is read-only: {}", name, path));
                }
            }
        }
    }

    // Validate Server Interface
    fn validate_server_interface(
        config: &ServerConfig,
        report: &mut ValidationReport,
    ) -> Result<()> {
        if config.protocol == ProtocolType::Asgi && config.worker_class == WorkerClass::Sync {
            report.add_error(&format!("ASGI Interface requires async worker class",));
        }

        if config.protocol == ProtocolType::Wsgi && config.worker_class == WorkerClass::Async {
            report.add_error(&format!("WSGI Interface requires sync worker class",));
        }

        if config.h2c_enabled {
            if config.worker_class != WorkerClass::Async {
                report.add_error("H2C requires async worker class");
            }
            if config.protocol != ProtocolType::Asgi {
                report.add_error("H2C is currently supported only with ASGI protocol");
            }
        }

        Ok(())
    }
}

/// Validation report containing errors and warnings
#[derive(Debug, Default)]
pub struct ValidationReport {
    errors: Vec<String>,
    warnings: Vec<String>,
}

impl ValidationReport {
    /// Create a new empty report
    pub fn new() -> Self {
        Self {
            errors: Vec::new(),
            warnings: Vec::new(),
        }
    }

    /// Add an error
    pub fn add_error(&mut self, error: &str) {
        self.errors.push(error.to_string());
    }

    /// Add a warning
    pub fn add_warning(&mut self, warning: &str) {
        self.warnings.push(warning.to_string());
    }

    /// Check if there are any errors
    pub fn has_errors(&self) -> bool {
        !self.errors.is_empty()
    }

    /// Check if there are any warnings
    pub fn has_warnings(&self) -> bool {
        !self.warnings.is_empty()
    }

    /// Get all errors
    pub fn errors(&self) -> &[String] {
        &self.errors
    }

    /// Get all warnings
    pub fn warnings(&self) -> &[String] {
        &self.warnings
    }

    /// Format errors as string
    pub fn format_errors(&self) -> String {
        self.errors.join("; ")
    }

    /// Format warnings as string
    pub fn format_warnings(&self) -> String {
        self.warnings.join("; ")
    }

    /// Format full report
    pub fn format(&self) -> String {
        let mut output = String::new();

        if !self.errors.is_empty() {
            output.push_str("Errors:\n");
            for error in &self.errors {
                output.push_str(&format!("  - {}\n", error));
            }
        }

        if !self.warnings.is_empty() {
            output.push_str("Warnings:\n");
            for warning in &self.warnings {
                output.push_str(&format!("  - {}\n", warning));
            }
        }

        if output.is_empty() {
            output.push_str("✓ Configuration is valid\n");
        }

        output
    }
}

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

    #[test]
    fn test_valid_config() {
        let config = ServerConfig {
            host: "127.0.0.1".to_string(),
            port: 8000,
            backlog: 2048,
            workers: Some(4),
            protocol: crate::config::ProtocolType::Wsgi,
            h2c_enabled: false,
            strict_http: false,
            worker_class: WorkerClass::Sync,
            worker_timeout: 30,
            worker_mode: false,
            max_requests: Some(1000),
            max_requests_jitter: Some(50),
            buffer_size: 4096,
            keepalive_timeout: 5,
            max_connections: 2048,
            sync_handler_threads: 4,
            sync_conn_queue_capacity: 4096,
            async_worker_threads: 4,
            async_max_blocking_threads: 100,
            app_module: "myapp".to_string(),
            app_callable: "application".to_string(),
            pythonpath: vec![],
            asgi_event_loop: "asyncio".to_string(),
            asgi_event_loop_fallback: true,
            asgi_submit_timeout_ms: 30_000,
            asgi_scheduler: "task".to_string(),
            asgi_max_inflight: 256,
            asgi_send_pool_size: 4096,
            log_stdout: false,
            log_level: "INFO".to_string(),
            access_log_format: "".to_string(),
            access_log: None,
            error_log: None,
            plugin_socket: None,
            plugin_auth_token: None,
            plugin_drop_policy: "drop_oldest".to_string(),
            plugin_max_lagged_events: 2048,
            plugin_write_timeout_ms: 500,
            plugin_bus_capacity: 8192,
            plugin_max_clients: 1024,
            plugin_subscribe_timeout_ms: 5000,
            plugin_auth_fail_window_secs: 60,
            plugin_auth_fail_max_attempts: 10,
            plugin_acl_mode: "same_user".to_string(),
            plugin_socket_mode: 0o660,
            metrics_interval_ms: 1000,
            metrics_workers_enabled: false,
            metrics_os_enabled: false,
            metrics_percentiles_enabled: false,
            metrics_top_errors_limit: 5,
            control_socket: Some(crate::control::ipc::DEFAULT_CONTROL_SOCKET.to_string()),
            control_auth_token: None,
            control_acl_mode: "same_user".to_string(),
            control_socket_mode: 0o660,
            config_path: None,
            ssl_enabled: false,
            ssl_certfile: None,
            ssl_keyfile: None,
        };

        let report = ConfigValidator::validate(&config).unwrap();
        assert!(!report.has_errors());
    }

    #[test]
    fn test_invalid_port() {
        let mut config = ServerConfig::new("app".to_string(), "app".to_string());
        config.port = 0;

        let result = ConfigValidator::validate(&config);
        assert!(result.is_err());
    }

    #[test]
    fn test_warnings() {
        let mut config = ServerConfig::new("app".to_string(), "app".to_string());
        config.port = 80; // Requires root
        config.workers = Some(300); // Very high

        let report = ConfigValidator::validate(&config).unwrap();
        assert!(report.has_warnings());
    }

    #[test]
    fn test_h2c_requires_async_asgi() {
        let mut config = ServerConfig::new("app".to_string(), "app".to_string());
        config.h2c_enabled = true;
        config.protocol = crate::config::ProtocolType::Wsgi;
        config.worker_class = WorkerClass::Sync;

        let result = ConfigValidator::validate(&config);
        assert!(result.is_err());
    }
}
