//! Mock Services for Integration Testing
//!
//! This module provides comprehensive mock implementations of all trading system
//! services for integration testing, including realistic latency simulation,
//! configurable failure rates, and comprehensive response patterns.

use std::collections::HashMap;
use std::sync::{Arc, atomic::{AtomicU16, AtomicU64, Ordering}};
use std::time::{Duration, Instant};

use tokio::sync::{mpsc, RwLock, Mutex};
use uuid::Uuid;
use serde_json::json;
use chrono::{DateTime, Utc};

use fxt::prelude::*;
use crate::fixtures::*;

pub mod mock_trading_service;
pub mod mock_backtesting_service;
pub mod mock_database;
pub mod mock_ml_infrastructure;


/// Mock trading service implementation for comprehensive integration testing
/// 
/// Provides realistic simulation of the trading service including configurable
/// latency, failure rates, order lifecycle management, and circuit breaker behavior.
pub struct MockTradingService {
    /// TCP port the mock service is listening on
    port: u16,
    /// Handle to the background server task
    server_handle: Option<tokio::task::JoinHandle<()>>,
    /// Configured responses for specific order IDs
    order_responses: Arc<RwLock<HashMap<String, SubmitOrderResponse>>>,
    /// Configured risk rejections for testing risk management
    risk_rejections: Arc<RwLock<HashMap<String, String>>>,
    /// Counter for testing failure sequences
    failure_sequence_count: Arc<AtomicU64>,
    /// Simulated order lifecycle progressions
    lifecycle_simulations: Arc<RwLock<HashMap<String, Vec<OrderStatus>>>>,
    /// Circuit breaker state for testing resilience
    circuit_breaker_status: Arc<RwLock<CircuitBreakerStatus>>,
    /// Overall trading system status
    trading_status: Arc<RwLock<TradingStatus>>,
    /// Service configuration including latency and failure rates
    config: MockServiceConfig,
}

/// Configuration for mock service behavior
/// 
/// Controls latency simulation, failure injection, chaos testing,
/// and connection limits for realistic testing scenarios.
#[derive(Debug, Clone)]
pub struct MockServiceConfig {
    /// Simulated service latency in milliseconds
    pub latency_ms: u64,
    /// Failure injection rate (0.0 to 1.0)
    pub failure_rate: f64,
    /// Whether to enable chaos testing features
    pub enable_chaos: bool,
    /// Maximum number of concurrent connections
    pub max_connections: usize,
}

impl Default for MockServiceConfig {
    fn default() -> Self {
        Self {
            latency_ms: 10,
            failure_rate: 0.01, // 1%
            enable_chaos: false,
            max_connections: 100,
        }
    }
}

// OrderStatus now imported from canonical source
use common::OrderStatus;

/// Circuit breaker state for testing resilience patterns
/// 
/// Tracks failure counts and circuit breaker state to validate
/// proper handling of service degradation scenarios.
#[derive(Debug, Clone)]
pub struct CircuitBreakerStatus {
    /// Whether the circuit breaker is currently open
    pub is_open: bool,
    /// Number of consecutive failures
    pub failure_count: u32,
    /// Timestamp of the most recent failure
    pub last_failure_time: Option<DateTime<Utc>>,
}

impl Default for CircuitBreakerStatus {
    fn default() -> Self {
        Self {
            is_open: false,
            failure_count: 0,
            last_failure_time: None,
        }
    }
}

/// Overall trading system operational status
/// 
/// Tracks system health, emergency stop state, heartbeat,
/// and key operational metrics for monitoring and testing.
#[derive(Debug, Clone)]
pub struct TradingStatus {
    /// Whether the trading system is currently active
    pub is_active: bool,
    /// Whether emergency stop has been triggered
    pub emergency_stop_active: bool,
    /// Timestamp of the last system heartbeat
    pub last_heartbeat: DateTime<Utc>,
    /// Number of currently active orders
    pub active_orders: u64,
    /// Total trading volume processed
    pub total_volume: Decimal,
}

impl Default for TradingStatus {
    fn default() -> Self {
        Self {
            is_active: true,
            emergency_stop_active: false,
            last_heartbeat: Utc::now(),
            active_orders: 0,
            total_volume: Decimal::ZERO,
        }
    }
}

/// Order submission request structure for testing
/// 
/// Represents the structure of order submission requests
/// used to test the trading service API.
#[derive(Debug, Clone)]
pub struct SubmitOrderRequest {
    /// Trading symbol (e.g., "AAPL", "BTCUSD")
    pub symbol: String,
    /// Order side as integer (Buy/Sell enum value)
    pub side: i32,
    /// Order type as integer (Market/Limit enum value)
    pub order_type: i32,
    /// Order quantity
    pub quantity: f64,
    /// Limit price (None for market orders)
    pub price: Option<f64>,
    /// Client-provided order identifier
    pub client_order_id: String,
    /// Additional order metadata
    pub metadata: HashMap<String, serde_json::Value>,
}

/// Order submission response structure for testing
/// 
/// Contains the result of an order submission including
/// success status, assigned order ID, and execution timing.
#[derive(Debug, Clone)]
pub struct SubmitOrderResponse {
    /// Whether the order was successfully submitted
    pub success: bool,
    /// System-assigned order identifier
    pub order_id: String,
    /// Response message or error description
    pub message: String,
    /// Order processing time in nanoseconds
    pub execution_time_ns: u64,
}

/// Start backtest request structure
#[derive(Debug, Clone)]
pub struct StartBacktestRequest {
    pub backtest_id: String,
    pub enable_monitoring: bool,
}

/// Start backtest response structure
#[derive(Debug, Clone)]
pub struct StartBacktestResponse {
    pub success: bool,
    pub message: String,
}

impl MockTradingService {
    /// Create a new mock trading service with default configuration
    /// 
    /// # Returns
    /// * `Ok(MockTradingService)` - Configured mock service ready to start
    ///
    /// * `Err(FxtError)` - If port allocation or initialization failed
    pub async fn new() -> FxtResult<Self> {
        Self::new_with_config(MockServiceConfig::default()).await
    }

    /// Create a new mock trading service with custom configuration
    /// 
    /// # Arguments
    /// * `config` - Custom service configuration including latency and failure rates
    /// 
    /// # Returns
    /// * `Ok(MockTradingService)` - Configured mock service
    ///
    /// * `Err(FxtError)` - If port allocation failed
    pub async fn new_with_config(config: MockServiceConfig) -> FxtResult<Self> {
        let port = TEST_PORT_MANAGER.allocate_port().await;

        Ok(Self {
            port,
            server_handle: None,
            order_responses: Arc::new(RwLock::new(HashMap::new())),
            risk_rejections: Arc::new(RwLock::new(HashMap::new())),
            failure_sequence_count: Arc::new(AtomicU64::new(0)),
            lifecycle_simulations: Arc::new(RwLock::new(HashMap::new())),
            circuit_breaker_status: Arc::new(RwLock::new(CircuitBreakerStatus::default())),
            trading_status: Arc::new(RwLock::new(TradingStatus::default())),
            config,
        })
    }

    /// Get the TCP port number the mock service is bound to
    /// 
    /// # Returns
    ///
    /// The port number allocated for this mock service
    pub fn port(&self) -> u16 {
        self.port
    }

    /// Configure a specific response for an order submission
    /// 
    /// # Arguments
    /// * `client_order_id` - Client order ID to configure response for
    ///
    /// * `response` - The response to return for this order
    pub async fn configure_order_response(&self, client_order_id: &str, response: SubmitOrderResponse) {
        let mut responses = self.order_responses.write().await;
        responses.insert(client_order_id.to_string(), response);
    }

    /// Configure a risk rejection for testing risk management
    /// 
    /// # Arguments
    /// * `client_order_id` - Order ID to reject
    ///
    /// * `reason` - Rejection reason message
    pub async fn configure_risk_rejection(&self, client_order_id: &str, reason: String) {
        let mut rejections = self.risk_rejections.write().await;
        rejections.insert(client_order_id.to_string(), reason);
    }

    /// Configure failure sequence for circuit breaker testing
    pub async fn configure_failure_sequence(&self, count: u64) {
        self.failure_sequence_count.store(count, Ordering::Relaxed);
    }

    /// Configure order lifecycle simulation
    pub async fn configure_lifecycle_simulation(&self, order_id: &str, statuses: Vec<OrderStatus>) {
        let mut simulations = self.lifecycle_simulations.write().await;
        simulations.insert(order_id.to_string(), statuses);
    }

    /// Start the mock trading service and begin accepting connections
    /// 
    /// # Returns
    /// * `Ok(())` - Service started successfully
    ///
    /// * `Err(FxtError)` - If service failed to start
    pub async fn start(&mut self) -> FxtResult<()> {
        let port = self.port;
        let config = self.config.clone();
        let order_responses = Arc::clone(&self.order_responses);
        let risk_rejections = Arc::clone(&self.risk_rejections);
        let failure_sequence = Arc::clone(&self.failure_sequence_count);
        let circuit_breaker = Arc::clone(&self.circuit_breaker_status);
        let trading_status = Arc::clone(&self.trading_status);

        let handle = tokio::spawn(async move {
            let listener = tokio::net::TcpListener::bind(format!("127.0.0.1:{}", port))
                .await
                .expect("Failed to bind mock trading service");

            while let Ok((stream, _)) = listener.accept().await {
                let responses = Arc::clone(&order_responses);
                let rejections = Arc::clone(&risk_rejections);
                let failure_seq = Arc::clone(&failure_sequence);
                let cb_status = Arc::clone(&circuit_breaker);
                let trade_status = Arc::clone(&trading_status);
                let service_config = config.clone();

                tokio::spawn(async move {
                    Self::handle_connection(stream, responses, rejections, failure_seq, cb_status, trade_status, service_config).await;
                });
            }
        });

        self.server_handle = Some(handle);
        Ok(())
    }

    /// Handle individual client connection
    async fn handle_connection(
        stream: tokio::net::TcpStream,
        order_responses: Arc<RwLock<HashMap<String, SubmitOrderResponse>>>,
        risk_rejections: Arc<RwLock<HashMap<String, String>>>,
        failure_sequence: Arc<AtomicU64>,
        circuit_breaker: Arc<RwLock<CircuitBreakerStatus>>,
        trading_status: Arc<RwLock<TradingStatus>>,
        config: MockServiceConfig,
    ) {
        // Simulate service latency
        if config.latency_ms > 0 {
            tokio::time::sleep(Duration::from_millis(config.latency_ms)).await;
        }

        // Simulate random failures if configured
        if config.enable_chaos && fastrand::f64() < config.failure_rate {
            return; // Drop connection to simulate failure
        }

        // Handle gRPC-style protocol simulation
        // In a real implementation, this would use tonic/gRPC
        // For testing, we'll simulate the essential behavior
    }

    /// Stop the mock service and release resources
    pub async fn stop(&mut self) {
        if let Some(handle) = self.server_handle.take() {
            handle.abort();
        }
        TEST_PORT_MANAGER.release_port(self.port).await;
    }
}

impl Drop for MockTradingService {
    fn drop(&mut self) {
        if let Some(handle) = self.server_handle.take() {
            handle.abort();
        }
    }
}

/// Mock backtesting service for integration testing
pub struct MockBacktestingService {
    port: u16,
    server_handle: Option<tokio::task::JoinHandle<()>>,
    backtest_responses: Arc<RwLock<HashMap<String, BacktestResult>>>,
    ml_responses: Arc<RwLock<HashMap<String, MLBacktestConfig>>>,
    ensemble_responses: Arc<RwLock<HashMap<String, EnsembleResults>>>,
    config: MockServiceConfig,
}

/// Backtest result structure
#[derive(Debug, Clone)]
pub struct BacktestResult {
    pub backtest_id: String,
    pub strategy_name: String,
    pub total_return: f64,
    pub annualized_return: f64,
    pub max_drawdown: f64,
    pub sharpe_ratio: f64,
    pub total_trades: u64,
    pub win_rate: f64,
    pub avg_trade_return: f64,
    pub final_value: f64,
    pub execution_time_ms: u64,
    pub events_processed: u64,
}

/// ML backtest configuration
#[derive(Debug, Clone)]
pub struct MLBacktestConfig {
    pub model_name: String,
    pub expected_return: f64,
    pub expected_sharpe: f64,
    pub expected_trades: u64,
}

/// Ensemble results structure
#[derive(Debug, Clone)]
pub struct EnsembleResults {
    pub ensemble_return: f64,
    pub ensemble_sharpe: f64,
    pub individual_returns: Vec<f64>,
    pub individual_sharpes: Vec<f64>,
    pub diversification_benefit: f64,
    pub model_weights_final: Vec<f64>,
    pub rebalance_count: u32,
}

/// Create backtest request structure
#[derive(Debug, Clone)]
pub struct CreateBacktestRequest {
    pub name: String,
    pub strategy_type: String,
    pub symbol: String,
    pub start_date: i64,
    pub end_date: i64,
    pub initial_capital: f64,
    pub parameters: serde_json::Value,
    pub enable_real_time_monitoring: bool,
}

/// Create backtest response structure
#[derive(Debug, Clone)]
pub struct CreateBacktestResponse {
    pub success: bool,
    pub backtest_id: String,
    pub message: String,
}

impl MockBacktestingService {
    /// Create new mock backtesting service
    pub async fn new() -> FxtResult<Self> {
        let port = TEST_PORT_MANAGER.allocate_port().await;

        Ok(Self {
            port,
            server_handle: None,
            backtest_responses: Arc::new(RwLock::new(HashMap::new())),
            ml_responses: Arc::new(RwLock::new(HashMap::new())),
            ensemble_responses: Arc::new(RwLock::new(HashMap::new())),
            config: MockServiceConfig::default(),
        })
    }

    /// Get the port the mock service is running on
    pub fn port(&self) -> u16 {
        self.port
    }

    /// Configure backtest response
    pub async fn configure_backtest_response(&self, name: &str, result: BacktestResult) {
        let mut responses = self.backtest_responses.write().await;
        responses.insert(name.to_string(), result);
    }

    /// Configure ML backtest response
    pub async fn configure_ml_backtest_response(
        &self,
        name: &str,
        model_name: &str,
        expected_return: f64,
        expected_sharpe: f64,
        expected_trades: u64,
    ) {
        let mut responses = self.ml_responses.write().await;
        responses.insert(name.to_string(), MLBacktestConfig {
            model_name: model_name.to_string(),
            expected_return,
            expected_sharpe,
            expected_trades,
        });
    }

    /// Configure ensemble response
    pub async fn configure_ensemble_response(&self, name: &str, results: EnsembleResults) {
        let mut responses = self.ensemble_responses.write().await;
        responses.insert(name.to_string(), results);
    }

    /// Stop the mock service
    pub async fn stop(&mut self) {
        if let Some(handle) = self.server_handle.take() {
            handle.abort();
        }
        TEST_PORT_MANAGER.release_port(self.port).await;
    }
}

/// Test database manager for PostgreSQL integration testing
pub struct TestDatabaseManager {
    pool: sqlx::PgPool,
    config: TestDatabaseConfig,
}

/// Test database configuration
#[derive(Debug, Clone)]
pub struct TestDatabaseConfig {
    pub database_url: String,
    pub max_connections: u32,
    pub enable_cleanup: bool,
    pub test_schema: String,
}

impl Default for TestDatabaseConfig {
    fn default() -> Self {
        Self {
            database_url: std::env::var("TEST_DATABASE_URL")
                .unwrap_or_else(|_| "postgresql://foxhunt_test:test_password@localhost:5432/foxhunt_test".to_string()),
            max_connections: 20,
            enable_cleanup: true,
            test_schema: "test_".to_string(),
        }
    }
}

impl TestDatabaseManager {
    /// Create new test database manager
    pub async fn new() -> FxtResult<Self> {
        Self::new_with_config(TestDatabaseConfig::default()).await
    }

    /// Create new test database manager with custom configuration
    pub async fn new_with_config(config: TestDatabaseConfig) -> FxtResult<Self> {
        let pool = sqlx::PgPool::connect(&config.database_url)
            .await
            .map_err(|e| FxtError::DatabaseError(format!("Failed to connect to test database: {}", e)))?;

        Ok(Self { pool, config })
    }

    /// Get connection pool
    pub fn get_pool(&self) -> &sqlx::PgPool {
        &self.pool
    }

    /// Verify order was persisted
    pub async fn verify_order_persisted(&self, order_id: &str) -> FxtResult<bool> {
        let count: i64 = sqlx::query_scalar("SELECT COUNT(*) FROM orders WHERE order_id = $1")
            .bind(order_id)
            .fetch_one(&self.pool)
            .await
            .map_err(|e| FxtError::DatabaseError(format!("Failed to verify order persistence: {}", e)))?;

        Ok(count > 0)
    }

    /// Clean up test data
    pub async fn cleanup(&self) -> FxtResult<()> {
        if self.config.enable_cleanup {
            let cleanup_queries = vec![
                "DELETE FROM trading_events WHERE source LIKE '%test%'",
                "DELETE FROM orders WHERE client_order_id LIKE '%test%'",
                "DELETE FROM positions WHERE account_id LIKE '%test%'",
                "DELETE FROM risk_events WHERE account_id LIKE '%test%'",
                "DELETE FROM market_data WHERE symbol LIKE '%TEST%'",
                "DELETE FROM performance_metrics WHERE metric_name LIKE '%test%'",
            ];

            for query in cleanup_queries {
                sqlx::query(query)
                    .execute(&self.pool)
                    .await
                    .map_err(|e| FxtError::DatabaseError(format!("Cleanup failed: {}", e)))?;
            }
        }

        Ok(())
    }
}

/// Test database structure
pub struct TestDatabase {
    manager: TestDatabaseManager,
}

impl TestDatabase {
    /// Create new test database
    pub async fn new() -> FxtResult<Self> {
        let manager = TestDatabaseManager::new().await?;
        Ok(Self { manager })
    }

    /// Verify order was persisted
    pub async fn verify_order_persisted(&self, order_id: &str) -> FxtResult<bool> {
        self.manager.verify_order_persisted(order_id).await
    }
}

/// Test data provider for market data simulation
pub struct TestDataProvider {
    historical_data: HashMap<String, Vec<MarketDataPoint>>,
}

/// Market data point structure
#[derive(Debug, Clone)]
pub struct MarketDataPoint {
    pub timestamp: DateTime<Utc>,
    pub symbol: String,
    pub price: f64,
    pub volume: u64,
    pub bid: Option<f64>,
    pub ask: Option<f64>,
}

impl TestDataProvider {
    /// Create new test data provider
    pub async fn new() -> FxtResult<Self> {
        let mut provider = Self {
            historical_data: HashMap::new(),
        };

        // Generate sample data for common symbols
        provider.generate_sample_data("AAPL", 1000).await;
        provider.generate_sample_data("MSFT", 1000).await;
        provider.generate_sample_data("GOOGL", 1000).await;

        Ok(provider)
    }

    /// Generate sample market data
    async fn generate_sample_data(&mut self, symbol: &str, count: usize) {
        let mut data = Vec::new();
        let mut price = 150.0;
        let start_time = Utc::now() - chrono::Duration::days(30);

        for i in 0..count {
            let timestamp = start_time + chrono::Duration::seconds(i as i64 * 60);

            // Simple random walk
            price += (fastrand::f64() - 0.5) * 2.0;
            price = price.max(100.0).min(200.0); // Keep price in reasonable range

            data.push(MarketDataPoint {
                timestamp,
                symbol: symbol.to_string(),
                price,
                volume: 1000 + fastrand::u64(0..10000),
                bid: Some(price - 0.01),
                ask: Some(price + 0.01),
            });
        }

        self.historical_data.insert(symbol.to_string(), data);
    }

    /// Get historical data for symbol
    pub fn get_historical_data(&self, symbol: &str) -> Option<&Vec<MarketDataPoint>> {
        self.historical_data.get(symbol)
    }
}

/// ML testing infrastructure
pub struct MLTestInfrastructure {
    mock_models: HashMap<String, MockMLModel>,
}

/// Mock ML model
#[derive(Debug, Clone)]
pub struct MockMLModel {
    pub name: String,
    pub inference_latency_ns: u64,
    pub accuracy: f64,
    pub memory_usage_mb: u64,
}

impl MLTestInfrastructure {
    /// Create new ML testing infrastructure
    pub async fn new() -> FxtResult<Self> {
        let mut models = HashMap::new();

        // Add mock models with realistic characteristics
        models.insert("TLOB".to_string(), MockMLModel {
            name: "TLOB".to_string(),
            inference_latency_ns: 15_000, // 15µs
            accuracy: 0.89,
            memory_usage_mb: 256,
        });

        models.insert("MAMBA".to_string(), MockMLModel {
            name: "MAMBA".to_string(),
            inference_latency_ns: 25_000, // 25µs
            accuracy: 0.91,
            memory_usage_mb: 512,
        });

        models.insert("TFT".to_string(), MockMLModel {
            name: "TFT".to_string(),
            inference_latency_ns: 35_000, // 35µs
            accuracy: 0.87,
            memory_usage_mb: 384,
        });

        models.insert("DQN".to_string(), MockMLModel {
            name: "DQN".to_string(),
            inference_latency_ns: 20_000, // 20µs
            accuracy: 0.84,
            memory_usage_mb: 128,
        });

        Ok(Self {
            mock_models: models,
        })
    }

    /// Get mock model
    pub fn get_model(&self, name: &str) -> Option<&MockMLModel> {
        self.mock_models.get(name)
    }

    /// List available models
    pub fn list_models(&self) -> Vec<&str> {
        self.mock_models.keys().map(|s| s.as_str()).collect()
    }
}

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

    #[tokio::test]
    async fn test_mock_trading_service() {
        let mut service = MockTradingService::new().await.unwrap();
        assert!(service.port() > 0);

        service.configure_order_response("test_order", SubmitOrderResponse {
            success: true,
            order_id: "order_123".to_string(),
            message: "Success".to_string(),
            execution_time_ns: 15_000,
        }).await;

        service.stop().await;
    }

    #[tokio::test]
    async fn test_test_data_provider() {
        let provider = TestDataProvider::new().await.unwrap();
        let aapl_data = provider.get_historical_data("AAPL").unwrap();
        assert_eq!(aapl_data.len(), 1000);
        assert!(aapl_data[0].price > 0.0);
    }

    #[tokio::test]
    async fn test_ml_infrastructure() {
        let ml_infra = MLTestInfrastructure::new().await.unwrap();
        let models = ml_infra.list_models();
        assert!(models.contains(&"TLOB"));
        assert!(models.contains(&"MAMBA"));

        let tlob = ml_infra.get_model("TLOB").unwrap();
        assert_eq!(tlob.name, "TLOB");
        assert!(tlob.inference_latency_ns > 0);
    }
}