//! Centralized mock implementations for integration testing
//!
//! This module provides reusable mock implementations for all three services
//! (Trading, Backtesting, ML Training) that can be shared across test suites.
//! Mocks are designed to be realistic and maintain behavioral consistency.

use super::*;
use std::collections::HashMap;
use std::sync::Arc;
use std::time::{Duration, Instant};
use tokio::sync::{RwLock, broadcast, mpsc};
use uuid::Uuid;
use serde_json::json;
use tracing::{info, debug, warn};

/// Centralized mock service registry
pub struct MockServiceRegistry {
    trading_service: Arc<MockTradingService>,
    backtesting_service: Arc<MockBacktestingService>,
    ml_training_service: Arc<MockMLTrainingService>,
    tli_client: Arc<MockTLIClient>,
}

impl MockServiceRegistry {
    pub fn new() -> Self {
        Self {
            trading_service: Arc::new(MockTradingService::new()),
            backtesting_service: Arc::new(MockBacktestingService::new()),
            ml_training_service: Arc::new(MockMLTrainingService::new()),
            tli_client: Arc::new(MockTLIClient::new()),
        }
    }

    pub fn trading_service(&self) -> Arc<MockTradingService> {
        self.trading_service.clone()
    }

    pub fn backtesting_service(&self) -> Arc<MockBacktestingService> {
        self.backtesting_service.clone()
    }

    pub fn ml_training_service(&self) -> Arc<MockMLTrainingService> {
        self.ml_training_service.clone()
    }

    pub fn tli_client(&self) -> Arc<MockTLIClient> {
        self.tli_client.clone()
    }

    /// Start all mock services
    pub async fn start_all(&self) -> TestResult<()> {
        self.trading_service.start().await?;
        self.backtesting_service.start().await?;
        self.ml_training_service.start().await?;
        self.tli_client.start().await?;
        Ok(())
    }

    /// Stop all mock services
    pub async fn stop_all(&self) -> TestResult<()> {
        self.trading_service.stop().await?;
        self.backtesting_service.stop().await?;
        self.ml_training_service.stop().await?;
        self.tli_client.stop().await?;
        Ok(())
    }
}

// ============================================================================
// Mock Trading Service
// ============================================================================

/// Mock Trading Service with realistic behavior
pub struct MockTradingService {
    state: Arc<RwLock<TradingServiceState>>,
    orders: Arc<RwLock<HashMap<String, MockOrder>>>,
    positions: Arc<RwLock<HashMap<String, MockPosition>>>,
    market_data_tx: broadcast::Sender<MockMarketData>,
    running: Arc<RwLock<bool>>,
}

#[derive(Debug, Default)]
struct TradingServiceState {
    connected_brokers: Vec<String>,
    risk_limits: RiskLimits,
    trading_enabled: bool,
}

#[derive(Debug, Default)]
struct RiskLimits {
    max_position_size: u64,
    max_order_value: f64,
    daily_loss_limit: f64,
}

#[derive(Debug, Clone)]
pub struct MockOrder {
    pub id: String,
    pub symbol: String,
    pub side: OrderSide,
    pub quantity: u64,
    pub price: f64,
    pub status: OrderStatus,
    pub created_at: Instant,
}

#[derive(Debug, Clone)]
pub struct MockPosition {
    pub symbol: String,
    pub quantity: i64, // Signed for long/short positions
    pub average_price: f64,
    pub unrealized_pnl: f64,
}

#[derive(Debug, Clone)]
pub struct MockMarketData {
    pub symbol: String,
    pub bid: f64,
    pub ask: f64,
    pub last_price: f64,
    pub volume: u64,
    pub timestamp: Instant,
}

#[derive(Debug, Clone)]
// OrderSide now imported from canonical source
use common::OrderSide;

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

impl MockTradingService {
    pub fn new() -> Self {
        let (market_data_tx, _) = broadcast::channel(1000);
        
        Self {
            state: Arc::new(RwLock::new(TradingServiceState {
                connected_brokers: vec!["IBKR".to_string(), "ICMarkets".to_string()],
                risk_limits: RiskLimits {
                    max_position_size: 10000,
                    max_order_value: 100000.0,
                    daily_loss_limit: 50000.0,
                },
                trading_enabled: true,
            })),
            orders: Arc::new(RwLock::new(HashMap::new())),
            positions: Arc::new(RwLock::new(HashMap::new())),
            market_data_tx,
            running: Arc::new(RwLock::new(false)),
        }
    }

    pub async fn start(&self) -> TestResult<()> {
        info!("Starting Mock Trading Service");
        *self.running.write().await = true;
        
        // Start market data generator
        self.start_market_data_generator().await;
        
        Ok(())
    }

    pub async fn stop(&self) -> TestResult<()> {
        info!("Stopping Mock Trading Service");
        *self.running.write().await = false;
        Ok(())
    }

    pub async fn place_order(&self, request: PlaceOrderRequest) -> TestResult<PlaceOrderResponse> {
        let order_id = Uuid::new_v4().to_string();
        
        // Validate order
        if request.quantity == 0 {
            return Ok(PlaceOrderResponse {
                success: false,
                order_id: String::new(),
                error_message: "Invalid quantity".to_string(),
            });
        }

        let state = self.state.read().await;
        if request.quantity > state.risk_limits.max_position_size {
            return Ok(PlaceOrderResponse {
                success: false,
                order_id: String::new(),
                error_message: "Order exceeds position size limit".to_string(),
            });
        }

        // Create order
        let order = MockOrder {
            id: order_id.clone(),
            symbol: request.symbol,
            side: request.side,
            quantity: request.quantity,
            price: request.price,
            status: OrderStatus::Pending,
            created_at: Instant::now(),
        };

        // Store order
        self.orders.write().await.insert(order_id.clone(), order);

        // Simulate order processing
        tokio::time::sleep(Duration::from_millis(10)).await;

        Ok(PlaceOrderResponse {
            success: true,
            order_id,
            error_message: String::new(),
        })
    }

    pub async fn get_order_status(&self, order_id: &str) -> TestResult<OrderStatusResponse> {
        let orders = self.orders.read().await;
        
        if let Some(order) = orders.get(order_id) {
            Ok(OrderStatusResponse {
                order_id: order.id.clone(),
                symbol: order.symbol.clone(),
                side: order.side.clone(),
                quantity: order.quantity,
                price: order.price,
                status: order.status.clone(),
                filled_quantity: match order.status {
                    OrderStatus::Filled => order.quantity,
                    OrderStatus::PartiallyFilled => order.quantity / 2,
                    _ => 0,
                },
            })
        } else {
            Err(TestFrameworkError::CrossServiceIntegrationFailed {
                reason: format!("Order not found: {}", order_id),
            })
        }
    }

    pub async fn subscribe_market_data(&self) -> broadcast::Receiver<MockMarketData> {
        self.market_data_tx.subscribe()
    }

    async fn start_market_data_generator(&self) {
        let tx = self.market_data_tx.clone();
        let running = self.running.clone();
        
        tokio::spawn(async move {
            let symbols = vec!["EURUSD", "GBPUSD", "USDJPY", "AUDUSD"];
            let mut prices: HashMap<String, f64> = symbols
                .iter()
                .map(|s| (s.to_string(), 1.2345))
                .collect();
            
            while *running.read().await {
                for symbol in &symbols {
                    // Generate realistic price movement
                    let current_price = prices.get(symbol).unwrap_or(&1.2345);
                    let change = (rand::random::<f64>() - 0.5) * 0.001; // ±0.1%
                    let new_price = current_price + change;
                    prices.insert(symbol.clone(), new_price);
                    
                    let market_data = MockMarketData {
                        symbol: symbol.clone(),
                        bid: new_price - 0.0002,
                        ask: new_price + 0.0002,
                        last_price: new_price,
                        volume: 1000 + (rand::random::<u64>() % 5000),
                        timestamp: Instant::now(),
                    };
                    
                    let _ = tx.send(market_data);
                }
                
                tokio::time::sleep(Duration::from_millis(100)).await;
            }
        });
    }
}

// ============================================================================
// Mock Backtesting Service  
// ============================================================================

/// Mock Backtesting Service
pub struct MockBacktestingService {
    backtests: Arc<RwLock<HashMap<String, MockBacktest>>>,
    running: Arc<RwLock<bool>>,
}

#[derive(Debug, Clone)]
pub struct MockBacktest {
    pub id: String,
    pub strategy_name: String,
    pub symbols: Vec<String>,
    pub status: BacktestStatus,
    pub progress: f64,
    pub start_time: Instant,
    pub results: Option<BacktestResults>,
}

#[derive(Debug, Clone)]
pub enum BacktestStatus {
    Queued,
    Running,
    Completed,
    Failed,
}

#[derive(Debug, Clone)]
pub struct BacktestResults {
    pub total_return: f64,
    pub sharpe_ratio: f64,
    pub max_drawdown: f64,
    pub total_trades: u64,
}

impl MockBacktestingService {
    pub fn new() -> Self {
        Self {
            backtests: Arc::new(RwLock::new(HashMap::new())),
            running: Arc::new(RwLock::new(false)),
        }
    }

    pub async fn start(&self) -> TestResult<()> {
        info!("Starting Mock Backtesting Service");
        *self.running.write().await = true;
        Ok(())
    }

    pub async fn stop(&self) -> TestResult<()> {
        info!("Stopping Mock Backtesting Service");
        *self.running.write().await = false;
        Ok(())
    }

    pub async fn start_backtest(&self, request: StartBacktestRequest) -> TestResult<StartBacktestResponse> {
        let backtest_id = Uuid::new_v4().to_string();
        
        let backtest = MockBacktest {
            id: backtest_id.clone(),
            strategy_name: request.strategy_name,
            symbols: request.symbols,
            status: BacktestStatus::Queued,
            progress: 0.0,
            start_time: Instant::now(),
            results: None,
        };

        self.backtests.write().await.insert(backtest_id.clone(), backtest);
        
        // Start backtest execution simulation
        let backtests = self.backtests.clone();
        let id = backtest_id.clone();
        tokio::spawn(async move {
            Self::simulate_backtest_execution(backtests, id).await;
        });

        Ok(StartBacktestResponse {
            success: true,
            backtest_id,
            estimated_duration_seconds: 300,
        })
    }

    pub async fn get_backtest_status(&self, backtest_id: &str) -> TestResult<BacktestStatusResponse> {
        let backtests = self.backtests.read().await;
        
        if let Some(backtest) = backtests.get(backtest_id) {
            Ok(BacktestStatusResponse {
                backtest_id: backtest.id.clone(),
                status: backtest.status.clone(),
                progress: backtest.progress,
                estimated_completion: if backtest.progress > 0.0 {
                    Some(backtest.start_time + Duration::from_secs_f64(300.0 / backtest.progress))
                } else {
                    None
                },
            })
        } else {
            Err(TestFrameworkError::CrossServiceIntegrationFailed {
                reason: format!("Backtest not found: {}", backtest_id),
            })
        }
    }

    async fn simulate_backtest_execution(
        backtests: Arc<RwLock<HashMap<String, MockBacktest>>>,
        backtest_id: String,
    ) {
        // Simulate backtest progression
        for progress in (10..=100).step_by(10) {
            tokio::time::sleep(Duration::from_millis(200)).await;
            
            let mut backtests = backtests.write().await;
            if let Some(backtest) = backtests.get_mut(&backtest_id) {
                backtest.progress = progress as f64;
                backtest.status = if progress == 100 {
                    BacktestStatus::Completed
                } else {
                    BacktestStatus::Running
                };
                
                if progress == 100 {
                    backtest.results = Some(BacktestResults {
                        total_return: 0.15, // 15% return
                        sharpe_ratio: 1.8,
                        max_drawdown: 0.08,
                        total_trades: 245,
                    });
                }
            }
        }
    }
}

// ============================================================================
// Mock ML Training Service
// ============================================================================

/// Mock ML Training Service
pub struct MockMLTrainingService {
    training_jobs: Arc<RwLock<HashMap<String, MockTrainingJob>>>,
    models: Arc<RwLock<HashMap<String, MockModel>>>,
    running: Arc<RwLock<bool>>,
}

#[derive(Debug, Clone)]
pub struct MockTrainingJob {
    pub id: String,
    pub model_name: String,
    pub model_type: String,
    pub status: TrainingStatus,
    pub progress: f64,
    pub start_time: Instant,
}

#[derive(Debug, Clone)]
pub struct MockModel {
    pub name: String,
    pub model_type: String,
    pub version: String,
    pub s3_path: String,
    pub performance_metrics: serde_json::Value,
}

#[derive(Debug, Clone)]
pub enum TrainingStatus {
    Queued,
    Training,
    Completed,
    Failed,
}

impl MockMLTrainingService {
    pub fn new() -> Self {
        Self {
            training_jobs: Arc::new(RwLock::new(HashMap::new())),
            models: Arc::new(RwLock::new(HashMap::new())),
            running: Arc::new(RwLock::new(false)),
        }
    }

    pub async fn start(&self) -> TestResult<()> {
        info!("Starting Mock ML Training Service");
        *self.running.write().await = true;
        Ok(())
    }

    pub async fn stop(&self) -> TestResult<()> {
        info!("Stopping Mock ML Training Service");
        *self.running.write().await = false;
        Ok(())
    }

    pub async fn start_training(&self, request: StartTrainingRequest) -> TestResult<StartTrainingResponse> {
        let job_id = Uuid::new_v4().to_string();
        
        let training_job = MockTrainingJob {
            id: job_id.clone(),
            model_name: request.model_name,
            model_type: request.model_type,
            status: TrainingStatus::Queued,
            progress: 0.0,
            start_time: Instant::now(),
        };

        self.training_jobs.write().await.insert(job_id.clone(), training_job);
        
        // Start training simulation
        let training_jobs = self.training_jobs.clone();
        let models = self.models.clone();
        let id = job_id.clone();
        let req = request.clone();
        tokio::spawn(async move {
            Self::simulate_training_execution(training_jobs, models, id, req).await;
        });

        Ok(StartTrainingResponse {
            success: true,
            job_id,
            estimated_duration_minutes: 120,
        })
    }

    pub async fn get_training_status(&self, job_id: &str) -> TestResult<TrainingStatusResponse> {
        let training_jobs = self.training_jobs.read().await;
        
        if let Some(job) = training_jobs.get(job_id) {
            Ok(TrainingStatusResponse {
                job_id: job.id.clone(),
                status: job.status.clone(),
                progress: job.progress,
                current_epoch: (job.progress * 100.0) as u32,
                loss: 0.001 + (1.0 - job.progress) * 0.1, // Decreasing loss
            })
        } else {
            Err(TestFrameworkError::CrossServiceIntegrationFailed {
                reason: format!("Training job not found: {}", job_id),
            })
        }
    }

    pub async fn get_model_prediction(&self, request: PredictionRequest) -> TestResult<PredictionResponse> {
        // Simulate ML inference
        let inference_start = Instant::now();
        tokio::time::sleep(Duration::from_millis(20)).await; // 20ms inference time
        let inference_latency = inference_start.elapsed();

        Ok(PredictionResponse {
            success: true,
            predictions: vec![0.75, 0.25], // Buy probability, Sell probability
            confidence: 0.85,
            inference_time_ms: inference_latency.as_millis() as u64,
        })
    }

    async fn simulate_training_execution(
        training_jobs: Arc<RwLock<HashMap<String, MockTrainingJob>>>,
        models: Arc<RwLock<HashMap<String, MockModel>>>,
        job_id: String,
        request: StartTrainingRequest,
    ) {
        // Simulate training progression
        for progress in (5..=100).step_by(5) {
            tokio::time::sleep(Duration::from_millis(100)).await;
            
            let mut jobs = training_jobs.write().await;
            if let Some(job) = jobs.get_mut(&job_id) {
                job.progress = progress as f64 / 100.0;
                job.status = if progress == 100 {
                    TrainingStatus::Completed
                } else {
                    TrainingStatus::Training
                };
                
                if progress == 100 {
                    // Create trained model
                    let model = MockModel {
                        name: request.model_name.clone(),
                        model_type: request.model_type.clone(),
                        version: "v1.0".to_string(),
                        s3_path: format!("s3://foxhunt-models/{}/v1.0/model.safetensors", request.model_name),
                        performance_metrics: json!({
                            "accuracy": 0.94,
                            "precision": 0.91,
                            "recall": 0.89,
                            "f1_score": 0.90
                        }),
                    };
                    
                    models.write().await.insert(request.model_name.clone(), model);
                }
            }
        }
    }
}

// ============================================================================
// Mock TLI Client
// ============================================================================

/// Mock TLI (Terminal Line Interface) Client
pub struct MockTLIClient {
    connected_services: Arc<RwLock<Vec<String>>>,
    command_history: Arc<RwLock<Vec<String>>>,
    running: Arc<RwLock<bool>>,
}

impl MockTLIClient {
    pub fn new() -> Self {
        Self {
            connected_services: Arc::new(RwLock::new(Vec::new())),
            command_history: Arc::new(RwLock::new(Vec::new())),
            running: Arc::new(RwLock::new(false)),
        }
    }

    pub async fn start(&self) -> TestResult<()> {
        info!("Starting Mock TLI Client");
        *self.running.write().await = true;
        Ok(())
    }

    pub async fn stop(&self) -> TestResult<()> {
        info!("Stopping Mock TLI Client");
        *self.running.write().await = false;
        Ok(())
    }

    pub async fn connect_to_service(&self, service_name: &str, endpoint: &str) -> TestResult<()> {
        debug!("TLI connecting to service: {} at {}", service_name, endpoint);
        
        // Simulate connection
        tokio::time::sleep(Duration::from_millis(50)).await;
        
        self.connected_services.write().await.push(service_name.to_string());
        
        Ok(())
    }

    pub async fn execute_command(&self, command: &str) -> TestResult<String> {
        debug!("TLI executing command: {}", command);
        
        // Store command in history
        self.command_history.write().await.push(command.to_string());
        
        // Simulate command execution
        tokio::time::sleep(Duration::from_millis(20)).await;
        
        match command {
            "health" => Ok("All services healthy".to_string()),
            "status" => Ok("System operational".to_string()),
            cmd if cmd.starts_with("place_order") => Ok("Order placed successfully".to_string()),
            cmd if cmd.starts_with("cancel_order") => Ok("Order cancelled".to_string()),
            _ => Ok(format!("Command executed: {}", command)),
        }
    }

    pub async fn get_connected_services(&self) -> Vec<String> {
        self.connected_services.read().await.clone()
    }
}

// ============================================================================
// Request/Response Types
// ============================================================================

#[derive(Debug, Clone)]
pub struct PlaceOrderRequest {
    pub symbol: String,
    pub side: OrderSide,
    pub quantity: u64,
    pub price: f64,
}

#[derive(Debug, Clone)]
pub struct PlaceOrderResponse {
    pub success: bool,
    pub order_id: String,
    pub error_message: String,
}

#[derive(Debug, Clone)]
pub struct OrderStatusResponse {
    pub order_id: String,
    pub symbol: String,
    pub side: OrderSide,
    pub quantity: u64,
    pub price: f64,
    pub status: OrderStatus,
    pub filled_quantity: u64,
}

#[derive(Debug, Clone)]
pub struct StartBacktestRequest {
    pub strategy_name: String,
    pub symbols: Vec<String>,
}

#[derive(Debug, Clone)]
pub struct StartBacktestResponse {
    pub success: bool,
    pub backtest_id: String,
    pub estimated_duration_seconds: u64,
}

#[derive(Debug, Clone)]
pub struct BacktestStatusResponse {
    pub backtest_id: String,
    pub status: BacktestStatus,
    pub progress: f64,
    pub estimated_completion: Option<Instant>,
}

#[derive(Debug, Clone)]
pub struct StartTrainingRequest {
    pub model_name: String,
    pub model_type: String,
}

#[derive(Debug, Clone)]
pub struct StartTrainingResponse {
    pub success: bool,
    pub job_id: String,
    pub estimated_duration_minutes: u64,
}

#[derive(Debug, Clone)]
pub struct TrainingStatusResponse {
    pub job_id: String,
    pub status: TrainingStatus,
    pub progress: f64,
    pub current_epoch: u32,
    pub loss: f64,
}

#[derive(Debug, Clone)]
pub struct PredictionRequest {
    pub model_name: String,
    pub features: Vec<f64>,
}

#[derive(Debug, Clone)]
pub struct PredictionResponse {
    pub success: bool,
    pub predictions: Vec<f64>,
    pub confidence: f64,
    pub inference_time_ms: u64,
}