//! Test Configuration for Foxhunt HFT Trading System
//!
//! This module provides configuration utilities for test environments,
//! including database setup, service configuration, and test parameters.

use rust_decimal::Decimal;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::env;

/// Test environment configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
#[derive(Default)]
pub struct TestConfig {
    pub database: DatabaseConfig,
    pub services: ServiceConfig,
    pub performance: PerformanceConfig,
    pub market_data: MarketDataConfig,
    pub risk: RiskConfig,
}

/// Database configuration for tests
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DatabaseConfig {
    pub url: String,
    pub max_connections: u32,
    pub connection_timeout_ms: u64,
    pub query_timeout_ms: u64,
    pub enable_logging: bool,
    pub auto_migrate: bool,
    pub cleanup_on_drop: bool,
}

/// Service configuration for tests
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ServiceConfig {
    pub trading_service: ServiceEndpoint,
    pub ml_training_service: ServiceEndpoint,
    pub backtesting_service: ServiceEndpoint,
    pub enable_mocks: bool,
    pub mock_latency_ms: u64,
    pub mock_failure_rate: f64,
}

/// Service endpoint configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ServiceEndpoint {
    pub host: String,
    pub port: u16,
    pub use_tls: bool,
    pub timeout_ms: u64,
    pub max_retries: u32,
}

/// Performance testing configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PerformanceConfig {
    pub max_latency_ns: u64,
    pub min_throughput_ops_per_sec: f64,
    pub test_duration_secs: u64,
    pub warmup_duration_secs: u64,
    pub concurrent_operations: usize,
    pub stress_test_multiplier: f64,
}

/// Market data configuration for tests
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MarketDataConfig {
    pub default_symbols: Vec<String>,
    pub tick_rate_hz: u32,
    pub price_precision: u32,
    pub volume_range: (u64, u64),
    pub volatility_range: (f64, f64),
    pub enable_realistic_data: bool,
}

/// Risk management configuration for tests
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RiskConfig {
    pub default_var_limit: Decimal,
    pub default_position_limit: Decimal,
    pub default_concentration_limit: Decimal,
    pub stress_test_scenarios: Vec<String>,
    pub enable_circuit_breakers: bool,
    pub max_drawdown_limit: Decimal,
}


impl Default for DatabaseConfig {
    fn default() -> Self {
        Self {
            url: env::var("TEST_DATABASE_URL").unwrap_or_else(|_| {
                "postgresql://foxhunt_test:test_password@localhost:5432/foxhunt_test".to_string()
            }),
            max_connections: 10,
            connection_timeout_ms: 5000,
            query_timeout_ms: 30000,
            enable_logging: false, // Disable SQL logging in tests by default
            auto_migrate: true,
            cleanup_on_drop: true,
        }
    }
}

impl Default for ServiceConfig {
    fn default() -> Self {
        Self {
            trading_service: ServiceEndpoint {
                host: "localhost".to_string(),
                port: 8001,
                use_tls: false,
                timeout_ms: 5000,
                max_retries: 3,
            },
            ml_training_service: ServiceEndpoint {
                host: "localhost".to_string(),
                port: 8002,
                use_tls: false,
                timeout_ms: 10000,
                max_retries: 3,
            },
            backtesting_service: ServiceEndpoint {
                host: "localhost".to_string(),
                port: 8003,
                use_tls: false,
                timeout_ms: 30000,
                max_retries: 3,
            },
            enable_mocks: true, // Enable mocks by default for unit tests
            mock_latency_ms: 1,
            mock_failure_rate: 0.0, // No failures by default
        }
    }
}

impl Default for PerformanceConfig {
    fn default() -> Self {
        Self {
            max_latency_ns: 50_000, // 50µs
            min_throughput_ops_per_sec: 1000.0,
            test_duration_secs: 10,
            warmup_duration_secs: 2,
            concurrent_operations: 10,
            stress_test_multiplier: 2.0,
        }
    }
}

impl Default for MarketDataConfig {
    fn default() -> Self {
        use super::ALL_TEST_SYMBOLS;

        Self {
            default_symbols: ALL_TEST_SYMBOLS.iter().map(|&s| s.to_string()).collect(),
            tick_rate_hz: 1000, // 1000 ticks per second
            price_precision: 2, // 2 decimal places
            volume_range: (100, 10000),
            volatility_range: (0.01, 0.05), // 1% to 5% daily volatility
            enable_realistic_data: true,
        }
    }
}

impl Default for RiskConfig {
    fn default() -> Self {
        Self {
            default_var_limit: Decimal::from(100000),         // $100k
            default_position_limit: Decimal::from(1000000),   // $1M
            default_concentration_limit: Decimal::new(25, 2), // 25%
            stress_test_scenarios: vec![
                "market_crash".to_string(),
                "interest_rate_shock".to_string(),
                "liquidity_crisis".to_string(),
            ],
            enable_circuit_breakers: true,
            max_drawdown_limit: Decimal::new(20, 2), // 20%
        }
    }
}

impl TestConfig {
    /// Load configuration from environment variables and defaults
    pub fn from_env() -> Self {
        let mut config = Self::default();

        // Override with environment variables if present
        if let Ok(db_url) = env::var("TEST_DATABASE_URL") {
            config.database.url = db_url;
        }

        if let Ok(max_connections) = env::var("TEST_DB_MAX_CONNECTIONS") {
            if let Ok(connections) = max_connections.parse() {
                config.database.max_connections = connections;
            }
        }

        if let Ok(enable_mocks) = env::var("TEST_ENABLE_MOCKS") {
            config.services.enable_mocks = enable_mocks.to_lowercase() == "true";
        }

        if let Ok(max_latency) = env::var("TEST_MAX_LATENCY_NS") {
            if let Ok(latency) = max_latency.parse() {
                config.performance.max_latency_ns = latency;
            }
        }

        config
    }

    /// Create configuration for unit tests (fast, mocked)
    pub fn for_unit_tests() -> Self {
        Self {
            database: DatabaseConfig {
                max_connections: 5,
                connection_timeout_ms: 1000,
                query_timeout_ms: 5000,
                enable_logging: false,
                cleanup_on_drop: true,
                ..DatabaseConfig::default()
            },
            services: ServiceConfig {
                enable_mocks: true,
                mock_latency_ms: 0, // No artificial latency for unit tests
                mock_failure_rate: 0.0,
                ..ServiceConfig::default()
            },
            performance: PerformanceConfig {
                test_duration_secs: 1,
                warmup_duration_secs: 0,
                concurrent_operations: 1,
                ..PerformanceConfig::default()
            },
            market_data: MarketDataConfig {
                tick_rate_hz: 10, // Low tick rate for fast tests
                ..MarketDataConfig::default()
            },
            ..Self::default()
        }
    }

    /// Create configuration for integration tests (realistic)
    pub fn for_integration_tests() -> Self {
        Self {
            services: ServiceConfig {
                enable_mocks: false, // Use real services
                ..ServiceConfig::default()
            },
            performance: PerformanceConfig {
                test_duration_secs: 30,
                warmup_duration_secs: 5,
                concurrent_operations: 50,
                ..PerformanceConfig::default()
            },
            ..Self::default()
        }
    }

    /// Create configuration for performance tests (demanding)
    pub fn for_performance_tests() -> Self {
        Self {
            services: ServiceConfig {
                enable_mocks: false,
                ..ServiceConfig::default()
            },
            performance: PerformanceConfig {
                max_latency_ns: 14_000, // 14µs target for HFT
                min_throughput_ops_per_sec: 10000.0,
                test_duration_secs: 60,
                warmup_duration_secs: 10,
                concurrent_operations: 100,
                stress_test_multiplier: 5.0,
                ..PerformanceConfig::default()
            },
            market_data: MarketDataConfig {
                tick_rate_hz: 10000, // High frequency data
                ..MarketDataConfig::default()
            },
            ..Self::default()
        }
    }

    /// Create configuration for stress tests (extreme)
    pub fn for_stress_tests() -> Self {
        Self {
            performance: PerformanceConfig {
                test_duration_secs: 300, // 5 minutes
                concurrent_operations: 1000,
                stress_test_multiplier: 10.0,
                ..PerformanceConfig::default()
            },
            market_data: MarketDataConfig {
                tick_rate_hz: 50000,            // Extreme tick rate
                volatility_range: (0.05, 0.20), // Higher volatility
                ..MarketDataConfig::default()
            },
            ..Self::default()
        }
    }

    /// Validate configuration values
    pub fn validate(&self) -> Result<(), String> {
        if self.database.max_connections == 0 {
            return Err("Database max_connections must be greater than 0".to_string());
        }

        if self.performance.max_latency_ns == 0 {
            return Err("Performance max_latency_ns must be greater than 0".to_string());
        }

        if self.performance.min_throughput_ops_per_sec <= 0.0 {
            return Err("Performance min_throughput_ops_per_sec must be positive".to_string());
        }

        if self.services.mock_failure_rate < 0.0 || self.services.mock_failure_rate > 1.0 {
            return Err("Services mock_failure_rate must be between 0.0 and 1.0".to_string());
        }

        if self.market_data.tick_rate_hz == 0 {
            return Err("Market data tick_rate_hz must be greater than 0".to_string());
        }

        if self.risk.default_var_limit <= Decimal::ZERO {
            return Err("Risk default_var_limit must be positive".to_string());
        }

        if self.risk.default_concentration_limit <= Decimal::ZERO
            || self.risk.default_concentration_limit > Decimal::ONE
        {
            return Err("Risk default_concentration_limit must be between 0 and 1".to_string());
        }

        Ok(())
    }

    /// Get database URL with test database suffix if not already present
    pub fn get_test_database_url(&self) -> String {
        let url = &self.database.url;
        if url.contains("_test") {
            url.clone()
        } else {
            // Append _test to database name
            if let Some(last_slash) = url.rfind('/') {
                let (base, db_name) = url.split_at(last_slash + 1);
                format!("{}{}_test", base, db_name)
            } else {
                format!("{}_test", url)
            }
        }
    }

    /// Create environment variables map for child processes
    pub fn to_env_vars(&self) -> HashMap<String, String> {
        let mut env_vars = HashMap::new();

        env_vars.insert(
            "TEST_DATABASE_URL".to_string(),
            self.get_test_database_url(),
        );
        env_vars.insert(
            "TEST_DB_MAX_CONNECTIONS".to_string(),
            self.database.max_connections.to_string(),
        );
        env_vars.insert(
            "TEST_ENABLE_MOCKS".to_string(),
            self.services.enable_mocks.to_string(),
        );
        env_vars.insert(
            "TEST_MAX_LATENCY_NS".to_string(),
            self.performance.max_latency_ns.to_string(),
        );
        env_vars.insert(
            "TEST_MIN_THROUGHPUT".to_string(),
            self.performance.min_throughput_ops_per_sec.to_string(),
        );
        env_vars.insert(
            "RUST_LOG".to_string(),
            if self.database.enable_logging {
                "debug"
            } else {
                "warn"
            }
            .to_string(),
        );

        env_vars
    }
}

/// Test configuration builder for fluent configuration
#[derive(Debug)]
pub struct TestConfigBuilder {
    config: TestConfig,
}

impl TestConfigBuilder {
    pub fn new() -> Self {
        Self {
            config: TestConfig::default(),
        }
    }

    pub fn with_database_url(mut self, url: impl Into<String>) -> Self {
        self.config.database.url = url.into();
        self
    }

    pub fn with_max_connections(mut self, max_connections: u32) -> Self {
        self.config.database.max_connections = max_connections;
        self
    }

    pub fn with_mocks_enabled(mut self, enabled: bool) -> Self {
        self.config.services.enable_mocks = enabled;
        self
    }

    pub fn with_max_latency_ns(mut self, latency: u64) -> Self {
        self.config.performance.max_latency_ns = latency;
        self
    }

    pub fn with_test_duration(mut self, duration_secs: u64) -> Self {
        self.config.performance.test_duration_secs = duration_secs;
        self
    }

    pub fn with_concurrent_operations(mut self, operations: usize) -> Self {
        self.config.performance.concurrent_operations = operations;
        self
    }

    pub fn with_var_limit(mut self, limit: Decimal) -> Self {
        self.config.risk.default_var_limit = limit;
        self
    }

    pub fn build(self) -> Result<TestConfig, String> {
        self.config.validate()?;
        Ok(self.config)
    }
}

impl Default for TestConfigBuilder {
    fn default() -> Self {
        Self::new()
    }
}

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

    #[test]
    fn test_default_config() {
        let config = TestConfig::default();
        assert!(config.validate().is_ok());
        assert!(config.database.max_connections > 0);
        assert!(config.performance.max_latency_ns > 0);
        assert!(!config.market_data.default_symbols.is_empty());
    }

    #[test]
    fn test_unit_test_config() {
        let config = TestConfig::for_unit_tests();
        assert!(config.validate().is_ok());
        assert!(config.services.enable_mocks);
        assert_eq!(config.services.mock_latency_ms, 0);
        assert_eq!(config.performance.test_duration_secs, 1);
    }

    #[test]
    fn test_performance_test_config() {
        let config = TestConfig::for_performance_tests();
        assert!(config.validate().is_ok());
        assert!(!config.services.enable_mocks);
        assert_eq!(config.performance.max_latency_ns, 14_000);
        assert!(config.performance.concurrent_operations >= 100);
    }

    #[test]
    fn test_config_builder() {
        let config = TestConfigBuilder::new()
            .with_max_connections(20)
            .with_mocks_enabled(false)
            .with_max_latency_ns(10_000)
            .build()
            .unwrap();

        assert_eq!(config.database.max_connections, 20);
        assert!(!config.services.enable_mocks);
        assert_eq!(config.performance.max_latency_ns, 10_000);
    }

    #[test]
    fn test_config_validation() {
        let mut config = TestConfig::default();

        // Test invalid max_connections
        config.database.max_connections = 0;
        assert!(config.validate().is_err());

        // Test invalid failure rate
        config.database.max_connections = 10;
        config.services.mock_failure_rate = 1.5;
        assert!(config.validate().is_err());

        // Test valid config
        config.services.mock_failure_rate = 0.1;
        assert!(config.validate().is_ok());
    }

    #[test]
    fn test_test_database_url() {
        let config = TestConfig::default();
        let test_url = config.get_test_database_url();
        assert!(test_url.contains("_test"));

        // Test with already test database
        let mut config_with_test = config.clone();
        config_with_test.database.url = "postgresql://user:pass@localhost/db_test".to_string();
        let test_url2 = config_with_test.get_test_database_url();
        assert_eq!(test_url2, config_with_test.database.url);
    }

    #[test]
    fn test_env_vars_generation() {
        let config = TestConfig::default();
        let env_vars = config.to_env_vars();

        assert!(env_vars.contains_key("TEST_DATABASE_URL"));
        assert!(env_vars.contains_key("TEST_DB_MAX_CONNECTIONS"));
        assert!(env_vars.contains_key("TEST_ENABLE_MOCKS"));
        assert!(env_vars.contains_key("RUST_LOG"));
    }
}
