//! Integration tests for Backtesting Service
//!
//! Wave 120 - Agent 9: Comprehensive integration tests for backtesting service
//! Tests Parquet replay, model loading, performance analytics, multi-strategy comparison,
//! parameter optimization, walk-forward analysis, and Monte Carlo simulation.

#![allow(dead_code, unused_variables)]
#![cfg(test)]

mod mock_repositories;

use anyhow::Result;
use backtesting_service::foxhunt::tli::BacktestStatus;
use backtesting_service::performance::PerformanceAnalyzer;
use backtesting_service::repositories::BacktestingRepositories;
use backtesting_service::service::{BacktestContext, BacktestingServiceImpl};
use backtesting_service::strategy_engine::{BacktestTrade, MarketData, StrategyEngine, TradeSide};
use chrono::Utc;
use mock_repositories::*;
use rand::Rng;
use rust_decimal::Decimal;
use std::collections::HashMap;
use std::sync::Arc;
// Model loader integration is tested via model_loader crate tests

// ==================== Test Setup Helpers ====================

/// Create a test backtesting service with mock repositories (no model cache for simplicity)
async fn setup_test_service() -> Result<BacktestingServiceImpl> {
    let market_data_repo = Box::new(MockMarketDataRepository::with_data(
        generate_sample_market_data("BTC_USDT", 100, 50000.0, 0.02),
    ));

    let trading_repo = Box::new(MockTradingRepository::new());
    let news_repo = Box::new(MockNewsRepository::with_events(
        generate_sample_news_events(&["BTC_USDT".to_string()], 20),
    ));

    let repositories = Arc::new(MockBacktestingRepositories::new(
        market_data_repo,
        trading_repo,
        news_repo,
    )) as Arc<dyn BacktestingRepositories>;

    // Create service without model cache for integration tests
    // Model loading is tested separately with dedicated model loader tests
    BacktestingServiceImpl::new(repositories).await
}

/// Create a test backtest context
fn create_test_context(
    strategy_name: &str,
    symbols: Vec<String>,
    initial_capital: f64,
    parameters: HashMap<String, String>,
) -> BacktestContext {
    let now = Utc::now();
    // Align with generate_sample_market_data which uses 100 days of history
    let start = now - chrono::Duration::days(101);
    // Set end time to cover the entire generated market data range
    let end = now + chrono::Duration::days(1);

    BacktestContext {
        id: uuid::Uuid::new_v4().to_string(),
        status: BacktestStatus::Queued,
        progress: 0.0,
        current_date: start.format("%Y-%m-%d").to_string(),
        trades_executed: 0,
        current_pnl: 0.0,
        started_at: start.timestamp_nanos_opt().unwrap_or(0),
        completed_at: Some(end.timestamp_nanos_opt().unwrap_or(0)),
        error_message: None,
        strategy_name: strategy_name.to_string(),
        symbols,
        initial_capital,
        parameters,
    }
}

// ==================== Parquet Replay Tests ====================

#[tokio::test]
async fn test_parquet_replay_with_strategy() -> Result<()> {
    let service = setup_test_service().await?;

    // Create test context
    let mut params = HashMap::new();
    // Set trigger at midpoint (50000) so sine wave oscillation will cross it
    params.insert("trigger_price".to_string(), "50000".to_string());

    let context = create_test_context(
        "moving_average_crossover",
        vec!["BTC_USDT".to_string()],
        100000.0,
        params,
    );

    // Create strategy engine with deterministic sine wave data
    // Price oscillates around 50000 ± 2% (49000-51000), crossing trigger at 50000
    let repos = Arc::new(MockBacktestingRepositories::new(
        Box::new(MockMarketDataRepository::with_data(
            generate_sample_market_data("BTC_USDT", 100, 50000.0, 0.02),
        )),
        Box::new(MockTradingRepository::new()),
        Box::new(MockNewsRepository::new()),
    )) as Arc<dyn BacktestingRepositories>;

    let config = config::structures::BacktestingStrategyConfig::default();
    let engine = StrategyEngine::new(&config, repos).await?;

    // Execute backtest
    let trades = engine.execute_backtest(&context).await?;

    // Verify trades were generated
    assert!(!trades.is_empty(), "Expected trades to be generated");

    Ok(())
}

#[tokio::test]
async fn test_parquet_replay_multiple_symbols() -> Result<()> {
    let symbols = vec!["BTC_USDT".to_string(), "ETH_USDT".to_string()];

    let mut market_data = generate_sample_market_data("BTC_USDT", 50, 50000.0, 0.02);
    market_data.extend(generate_sample_market_data("ETH_USDT", 50, 3000.0, 0.03));

    let repos = Arc::new(MockBacktestingRepositories::new(
        Box::new(MockMarketDataRepository::with_data(market_data)),
        Box::new(MockTradingRepository::new()),
        Box::new(MockNewsRepository::new()),
    )) as Arc<dyn BacktestingRepositories>;

    let config = config::structures::BacktestingStrategyConfig::default();
    let engine = StrategyEngine::new(&config, repos).await?;

    // Use moving_average_crossover strategy with very low trigger
    // Set trigger below typical prices to ensure entry signals
    let mut params = HashMap::new();
    params.insert("trigger_price".to_string(), "30000".to_string()); // Well below BTC/ETH prices

    let context = create_test_context(
        "moving_average_crossover",
        symbols.clone(),
        200000.0, // Increased capital to handle multiple symbols
        params,
    );

    // Execute backtest - this tests that the engine can handle multiple symbols
    let result = engine.execute_backtest(&context).await;
    assert!(
        result.is_ok(),
        "Backtest with multiple symbols should succeed"
    );

    let trades = result.unwrap();
    // With trigger=30000, all BTC prices (~50000) will generate entry signals
    // Strategy will buy BTC first, then potentially ETH
    // We just verify the system handles multiple symbols without errors
    // (reaching this point means backtest completed without errors)

    Ok(())
}

#[tokio::test]
async fn test_parquet_replay_with_gaps() -> Result<()> {
    // Create market data with gaps
    let mut market_data = generate_sample_market_data("BTC_USDT", 30, 50000.0, 0.02);
    let gap_data = generate_sample_market_data("BTC_USDT", 20, 52000.0, 0.02);

    // Shift gap data timestamps to create a gap
    let gap_start = Utc::now();
    let shifted_gap_data: Vec<MarketData> = gap_data
        .into_iter()
        .map(|mut d| {
            d.timestamp = gap_start + chrono::Duration::days(50);
            d
        })
        .collect();

    market_data.extend(shifted_gap_data);

    let repos = Arc::new(MockBacktestingRepositories::new(
        Box::new(MockMarketDataRepository::with_data(market_data)),
        Box::new(MockTradingRepository::new()),
        Box::new(MockNewsRepository::new()),
    )) as Arc<dyn BacktestingRepositories>;

    let config = config::structures::BacktestingStrategyConfig::default();
    let engine = StrategyEngine::new(&config, repos).await?;

    let context = create_test_context(
        "buy_and_hold",
        vec!["BTC_USDT".to_string()],
        100000.0,
        HashMap::new(),
    );

    // Should handle gaps gracefully
    let result = engine.execute_backtest(&context).await;
    assert!(result.is_ok(), "Expected backtest to handle data gaps");

    Ok(())
}

// ==================== Model Loading Tests ====================
// Note: Model loading tests are handled by the model_loader crate tests
// The backtesting service integrates with Agent 1's model_loader which has its own test suite

#[tokio::test]
async fn test_service_initialization() -> Result<()> {
    let service = setup_test_service().await?;

    // Verify service was created successfully (reaching this point means no error)
    let _ = &service;

    Ok(())
}

// ==================== Performance Analytics Tests ====================

#[tokio::test]
async fn test_sharpe_ratio_calculation() -> Result<()> {
    let config = config::structures::BacktestingPerformanceConfig::default();
    let analyzer = PerformanceAnalyzer::new(&config)?;

    // Generate sample trades with positive returns
    let trades = generate_profitable_trades(50, 100000.0);
    let metrics = analyzer.calculate_metrics(&trades, 100000.0);

    assert!(
        metrics.sharpe_ratio > 0.0,
        "Expected positive Sharpe ratio, got {}",
        metrics.sharpe_ratio
    );
    assert!(metrics.total_return > 0.0, "Expected positive returns");

    Ok(())
}

#[tokio::test]
async fn test_max_drawdown_calculation() -> Result<()> {
    let config = config::structures::BacktestingPerformanceConfig::default();
    let analyzer = PerformanceAnalyzer::new(&config)?;

    // Generate trades with a drawdown period
    let trades = generate_trades_with_drawdown(100, 100000.0, 0.3);
    let metrics = analyzer.calculate_metrics(&trades, 100000.0);

    assert!(
        metrics.max_drawdown > 0.0,
        "Expected drawdown to be measured"
    );
    assert!(metrics.max_drawdown <= 100.0, "Drawdown should be <= 100%");

    Ok(())
}

#[tokio::test]
async fn test_win_rate_calculation() -> Result<()> {
    let config = config::structures::BacktestingPerformanceConfig::default();
    let analyzer = PerformanceAnalyzer::new(&config)?;

    // Generate trades with known win rate (60%)
    let trades = generate_trades_with_win_rate(100, 100000.0, 0.6);
    let metrics = analyzer.calculate_metrics(&trades, 100000.0);

    assert!(
        metrics.win_rate >= 55.0 && metrics.win_rate <= 65.0,
        "Expected win rate around 60%, got {}",
        metrics.win_rate
    );

    Ok(())
}

#[tokio::test]
async fn test_sortino_ratio() -> Result<()> {
    let config = config::structures::BacktestingPerformanceConfig::default();
    let analyzer = PerformanceAnalyzer::new(&config)?;

    // Use trades with some losses to generate downside deviation for Sortino
    let trades = generate_trades_with_win_rate(100, 100000.0, 0.7);
    let metrics = analyzer.calculate_metrics(&trades, 100000.0);

    assert!(
        metrics.sortino_ratio != 0.0,
        "Expected Sortino ratio to be calculated, got {}",
        metrics.sortino_ratio
    );

    Ok(())
}

#[tokio::test]
async fn test_calmar_ratio() -> Result<()> {
    let config = config::structures::BacktestingPerformanceConfig::default();
    let analyzer = PerformanceAnalyzer::new(&config)?;

    let trades = generate_profitable_trades(50, 100000.0);
    let metrics = analyzer.calculate_metrics(&trades, 100000.0);

    assert!(
        metrics.calmar_ratio >= 0.0,
        "Expected non-negative Calmar ratio"
    );

    Ok(())
}

#[tokio::test]
async fn test_var_calculation() -> Result<()> {
    let config = config::structures::BacktestingPerformanceConfig::default();
    let analyzer = PerformanceAnalyzer::new(&config)?;

    let trades = generate_trades_with_drawdown(100, 100000.0, 0.2);
    let metrics = analyzer.calculate_metrics(&trades, 100000.0);

    assert!(metrics.var_95.is_some(), "Expected VaR to be calculated");

    Ok(())
}

#[tokio::test]
async fn test_expected_shortfall() -> Result<()> {
    let config = config::structures::BacktestingPerformanceConfig::default();
    let analyzer = PerformanceAnalyzer::new(&config)?;

    let trades = generate_trades_with_drawdown(100, 100000.0, 0.2);
    let metrics = analyzer.calculate_metrics(&trades, 100000.0);

    assert!(
        metrics.expected_shortfall.is_some(),
        "Expected ES to be calculated"
    );

    Ok(())
}

#[tokio::test]
async fn test_equity_curve_generation() -> Result<()> {
    let config = config::structures::BacktestingPerformanceConfig::default();
    let analyzer = PerformanceAnalyzer::new(&config)?;

    let trades = generate_profitable_trades(100, 100000.0);
    let equity_curve = analyzer.generate_equity_curve(&trades, 100000.0);

    assert!(!equity_curve.is_empty(), "Expected equity curve points");
    assert_eq!(
        equity_curve[0].equity, 100000.0,
        "First point should be initial capital"
    );

    Ok(())
}

#[tokio::test]
async fn test_drawdown_periods() -> Result<()> {
    let config = config::structures::BacktestingPerformanceConfig::default();
    let analyzer = PerformanceAnalyzer::new(&config)?;

    let trades = generate_trades_with_drawdown(100, 100000.0, 0.3);
    let equity_curve = analyzer.generate_equity_curve(&trades, 100000.0);
    let drawdown_periods = analyzer.identify_drawdown_periods(&equity_curve);

    assert!(
        !drawdown_periods.is_empty(),
        "Expected drawdown periods to be identified"
    );

    Ok(())
}

// test_rolling_metrics removed: calculate_rolling_metrics was removed from PerformanceAnalyzer

// ==================== Multi-Strategy Comparison Tests ====================

#[tokio::test]
async fn test_compare_buy_and_hold_vs_ma_crossover() -> Result<()> {
    let market_data = generate_sample_market_data("BTC_USDT", 100, 50000.0, 0.02);

    // Test buy and hold
    let repos1 = Arc::new(MockBacktestingRepositories::new(
        Box::new(MockMarketDataRepository::with_data(market_data.clone())),
        Box::new(MockTradingRepository::new()),
        Box::new(MockNewsRepository::new()),
    )) as Arc<dyn BacktestingRepositories>;

    let config = config::structures::BacktestingStrategyConfig::default();
    let engine1 = StrategyEngine::new(&config, repos1).await?;

    let context1 = create_test_context(
        "buy_and_hold",
        vec!["BTC_USDT".to_string()],
        100000.0,
        HashMap::new(),
    );

    let trades1 = engine1.execute_backtest(&context1).await?;

    // Test MA crossover
    let repos2 = Arc::new(MockBacktestingRepositories::new(
        Box::new(MockMarketDataRepository::with_data(market_data)),
        Box::new(MockTradingRepository::new()),
        Box::new(MockNewsRepository::new()),
    )) as Arc<dyn BacktestingRepositories>;

    let engine2 = StrategyEngine::new(&config, repos2).await?;

    let mut params = HashMap::new();
    params.insert("trigger_price".to_string(), "48000".to_string());

    let context2 = create_test_context(
        "moving_average_crossover",
        vec!["BTC_USDT".to_string()],
        100000.0,
        params,
    );

    let trades2 = engine2.execute_backtest(&context2).await?;

    // Compare results
    let perf_config = config::structures::BacktestingPerformanceConfig::default();
    let analyzer = PerformanceAnalyzer::new(&perf_config)?;

    let metrics1 = analyzer.calculate_metrics(&trades1, 100000.0);
    let metrics2 = analyzer.calculate_metrics(&trades2, 100000.0);

    println!(
        "Buy and Hold - Total Return: {}%, Sharpe: {}",
        metrics1.total_return, metrics1.sharpe_ratio
    );
    println!(
        "MA Crossover - Total Return: {}%, Sharpe: {}",
        metrics2.total_return, metrics2.sharpe_ratio
    );

    // Strategy comparison completed (reaching this point means no error)

    Ok(())
}

#[tokio::test]
async fn test_news_aware_strategy() -> Result<()> {
    let market_data = generate_sample_market_data("BTC_USDT", 50, 50000.0, 0.02);
    let news_events = generate_sample_news_events(&["BTC_USDT".to_string()], 20);

    let repos = Arc::new(MockBacktestingRepositories::new(
        Box::new(MockMarketDataRepository::with_data(market_data)),
        Box::new(MockTradingRepository::new()),
        Box::new(MockNewsRepository::with_events(news_events)),
    )) as Arc<dyn BacktestingRepositories>;

    let config = config::structures::BacktestingStrategyConfig::default();
    let engine = StrategyEngine::new(&config, repos).await?;

    let mut params = HashMap::new();
    params.insert("sentiment_threshold".to_string(), "0.3".to_string());
    params.insert("max_position_size".to_string(), "0.1".to_string());

    let context = create_test_context(
        "news_aware_strategy",
        vec!["BTC_USDT".to_string()],
        100000.0,
        params,
    );

    let trades = engine.execute_backtest(&context).await?;

    // News-aware strategy should generate signals
    // (reaching this point means news-aware strategy executed successfully)

    Ok(())
}

// ==================== Parameter Optimization Tests ====================

#[tokio::test]
async fn test_parameter_grid_search() -> Result<()> {
    let market_data = generate_sample_market_data("BTC_USDT", 100, 50000.0, 0.02);

    let trigger_prices = vec!["45000", "48000", "50000", "52000"];
    let mut best_sharpe = f64::MIN;
    let mut best_params = HashMap::new();

    for trigger_price in trigger_prices {
        let repos = Arc::new(MockBacktestingRepositories::new(
            Box::new(MockMarketDataRepository::with_data(market_data.clone())),
            Box::new(MockTradingRepository::new()),
            Box::new(MockNewsRepository::new()),
        )) as Arc<dyn BacktestingRepositories>;

        let config = config::structures::BacktestingStrategyConfig::default();
        let engine = StrategyEngine::new(&config, repos).await?;

        let mut params = HashMap::new();
        params.insert("trigger_price".to_string(), trigger_price.to_string());

        let context = create_test_context(
            "moving_average_crossover",
            vec!["BTC_USDT".to_string()],
            100000.0,
            params.clone(),
        );

        let trades = engine.execute_backtest(&context).await?;

        let perf_config = config::structures::BacktestingPerformanceConfig::default();
        let analyzer = PerformanceAnalyzer::new(&perf_config)?;
        let metrics = analyzer.calculate_metrics(&trades, 100000.0);

        if metrics.sharpe_ratio > best_sharpe {
            best_sharpe = metrics.sharpe_ratio;
            best_params = params;
        }
    }

    println!(
        "Best parameters: {:?}, Sharpe: {}",
        best_params, best_sharpe
    );
    assert!(!best_params.is_empty(), "Expected to find best parameters");

    Ok(())
}

#[tokio::test]
async fn test_allocation_optimization() -> Result<()> {
    let market_data = generate_sample_market_data("BTC_USDT", 50, 50000.0, 0.02);

    let allocations = vec!["0.25", "0.5", "0.75", "1.0"];
    let mut results = Vec::new();

    for allocation in allocations {
        let repos = Arc::new(MockBacktestingRepositories::new(
            Box::new(MockMarketDataRepository::with_data(market_data.clone())),
            Box::new(MockTradingRepository::new()),
            Box::new(MockNewsRepository::new()),
        )) as Arc<dyn BacktestingRepositories>;

        let config = config::structures::BacktestingStrategyConfig::default();
        let engine = StrategyEngine::new(&config, repos).await?;

        let mut params = HashMap::new();
        params.insert("allocation".to_string(), allocation.to_string());

        let context = create_test_context(
            "buy_and_hold",
            vec!["BTC_USDT".to_string()],
            100000.0,
            params,
        );

        let trades = engine.execute_backtest(&context).await?;

        let perf_config = config::structures::BacktestingPerformanceConfig::default();
        let analyzer = PerformanceAnalyzer::new(&perf_config)?;
        let metrics = analyzer.calculate_metrics(&trades, 100000.0);

        results.push((allocation, metrics.total_return, metrics.sharpe_ratio));
    }

    // Find optimal allocation
    results.sort_by(|a, b| b.2.partial_cmp(&a.2).unwrap_or(std::cmp::Ordering::Equal));
    println!(
        "Best allocation: {}% (Sharpe: {})",
        results[0].0, results[0].2
    );

    assert!(!results.is_empty(), "Expected optimization results");

    Ok(())
}

// ==================== Walk-Forward Analysis Tests ====================

#[tokio::test]
async fn test_walk_forward_analysis() -> Result<()> {
    // Generate 6 months of data
    let all_data = generate_sample_market_data("BTC_USDT", 180, 50000.0, 0.02);

    // Split into train (first 120 days) and test (last 60 days)
    let train_data = all_data[0..120].to_vec();
    let test_data = all_data[120..].to_vec();

    // Train phase: optimize parameters on training data
    let train_repos = Arc::new(MockBacktestingRepositories::new(
        Box::new(MockMarketDataRepository::with_data(train_data)),
        Box::new(MockTradingRepository::new()),
        Box::new(MockNewsRepository::new()),
    )) as Arc<dyn BacktestingRepositories>;

    let config = config::structures::BacktestingStrategyConfig::default();
    let train_engine = StrategyEngine::new(&config, train_repos).await?;

    let mut params = HashMap::new();
    params.insert("trigger_price".to_string(), "48000".to_string());

    let train_context = create_test_context(
        "moving_average_crossover",
        vec!["BTC_USDT".to_string()],
        100000.0,
        params.clone(),
    );

    let train_trades = train_engine.execute_backtest(&train_context).await?;

    // Test phase: apply optimized parameters to test data
    let test_repos = Arc::new(MockBacktestingRepositories::new(
        Box::new(MockMarketDataRepository::with_data(test_data)),
        Box::new(MockTradingRepository::new()),
        Box::new(MockNewsRepository::new()),
    )) as Arc<dyn BacktestingRepositories>;

    let test_engine = StrategyEngine::new(&config, test_repos).await?;

    let test_context = create_test_context(
        "moving_average_crossover",
        vec!["BTC_USDT".to_string()],
        100000.0,
        params,
    );

    let test_trades = test_engine.execute_backtest(&test_context).await?;

    // Compare train vs test performance
    let perf_config = config::structures::BacktestingPerformanceConfig::default();
    let analyzer = PerformanceAnalyzer::new(&perf_config)?;

    let train_metrics = analyzer.calculate_metrics(&train_trades, 100000.0);
    let test_metrics = analyzer.calculate_metrics(&test_trades, 100000.0);

    println!(
        "Train Sharpe: {}, Test Sharpe: {}",
        train_metrics.sharpe_ratio, test_metrics.sharpe_ratio
    );

    // Walk-forward analysis completed (reaching this point means no error)

    Ok(())
}

#[tokio::test]
async fn test_rolling_walk_forward() -> Result<()> {
    // Generate 12 months of data
    let all_data = generate_sample_market_data("BTC_USDT", 365, 50000.0, 0.02);

    // Perform rolling walk-forward with 60-day train, 30-day test windows
    let train_window = 60;
    let test_window = 30;
    let num_windows = (all_data.len() - train_window) / test_window;

    let mut test_results = Vec::new();

    for i in 0..num_windows.min(3) {
        // Limit to 3 windows for test speed
        let train_start = i * test_window;
        let train_end = train_start + train_window;
        let test_end = train_end + test_window;

        if test_end > all_data.len() {
            break;
        }

        let train_data = all_data[train_start..train_end].to_vec();
        let test_data = all_data[train_end..test_end].to_vec();

        // Test on window
        let repos = Arc::new(MockBacktestingRepositories::new(
            Box::new(MockMarketDataRepository::with_data(test_data)),
            Box::new(MockTradingRepository::new()),
            Box::new(MockNewsRepository::new()),
        )) as Arc<dyn BacktestingRepositories>;

        let config = config::structures::BacktestingStrategyConfig::default();
        let engine = StrategyEngine::new(&config, repos).await?;

        let context = create_test_context(
            "buy_and_hold",
            vec!["BTC_USDT".to_string()],
            100000.0,
            HashMap::new(),
        );

        let trades = engine.execute_backtest(&context).await?;

        let perf_config = config::structures::BacktestingPerformanceConfig::default();
        let analyzer = PerformanceAnalyzer::new(&perf_config)?;
        let metrics = analyzer.calculate_metrics(&trades, 100000.0);

        test_results.push(metrics.sharpe_ratio);
    }

    let avg_sharpe: f64 = test_results.iter().sum::<f64>() / test_results.len() as f64;
    println!(
        "Average Sharpe across {} windows: {}",
        test_results.len(),
        avg_sharpe
    );

    assert!(!test_results.is_empty(), "Expected rolling window results");

    Ok(())
}

// ==================== Monte Carlo Simulation Tests ====================

#[tokio::test]
async fn test_monte_carlo_returns() -> Result<()> {
    // Run strategy 50 times with different random seeds
    let num_simulations = 50;
    let mut returns = Vec::new();

    for _ in 0..num_simulations {
        let market_data = generate_sample_market_data("BTC_USDT", 100, 50000.0, 0.02);

        let repos = Arc::new(MockBacktestingRepositories::new(
            Box::new(MockMarketDataRepository::with_data(market_data)),
            Box::new(MockTradingRepository::new()),
            Box::new(MockNewsRepository::new()),
        )) as Arc<dyn BacktestingRepositories>;

        let config = config::structures::BacktestingStrategyConfig::default();
        let engine = StrategyEngine::new(&config, repos).await?;

        let context = create_test_context(
            "buy_and_hold",
            vec!["BTC_USDT".to_string()],
            100000.0,
            HashMap::new(),
        );

        let trades = engine.execute_backtest(&context).await?;

        let perf_config = config::structures::BacktestingPerformanceConfig::default();
        let analyzer = PerformanceAnalyzer::new(&perf_config)?;
        let metrics = analyzer.calculate_metrics(&trades, 100000.0);

        returns.push(metrics.total_return);
    }

    // Calculate Monte Carlo statistics
    let mean_return: f64 = returns.iter().sum::<f64>() / returns.len() as f64;
    let variance: f64 = returns
        .iter()
        .map(|r| (r - mean_return).powi(2))
        .sum::<f64>()
        / returns.len() as f64;
    let std_dev = variance.sqrt();

    println!(
        "Monte Carlo - Mean: {:.2}%, Std Dev: {:.2}%",
        mean_return, std_dev
    );

    assert!(
        returns.len() == num_simulations,
        "Expected all simulations to complete"
    );

    Ok(())
}

#[tokio::test]
async fn test_monte_carlo_confidence_intervals() -> Result<()> {
    let num_simulations = 100;
    let mut sharpe_ratios = Vec::new();

    for _ in 0..num_simulations {
        let market_data = generate_sample_market_data("BTC_USDT", 50, 50000.0, 0.03);

        let repos = Arc::new(MockBacktestingRepositories::new(
            Box::new(MockMarketDataRepository::with_data(market_data)),
            Box::new(MockTradingRepository::new()),
            Box::new(MockNewsRepository::new()),
        )) as Arc<dyn BacktestingRepositories>;

        let config = config::structures::BacktestingStrategyConfig::default();
        let engine = StrategyEngine::new(&config, repos).await?;

        let context = create_test_context(
            "buy_and_hold",
            vec!["BTC_USDT".to_string()],
            100000.0,
            HashMap::new(),
        );

        let trades = engine.execute_backtest(&context).await?;

        let perf_config = config::structures::BacktestingPerformanceConfig::default();
        let analyzer = PerformanceAnalyzer::new(&perf_config)?;
        let metrics = analyzer.calculate_metrics(&trades, 100000.0);

        sharpe_ratios.push(metrics.sharpe_ratio);
    }

    // Calculate 95% confidence interval
    sharpe_ratios.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
    let p5_idx = (num_simulations as f64 * 0.05) as usize;
    let p95_idx = (num_simulations as f64 * 0.95) as usize;

    let ci_lower = sharpe_ratios[p5_idx];
    let ci_upper = sharpe_ratios[p95_idx];

    println!(
        "95% Confidence Interval for Sharpe: [{:.2}, {:.2}]",
        ci_lower, ci_upper
    );

    assert!(ci_upper >= ci_lower, "Expected valid confidence interval");

    Ok(())
}

#[tokio::test]
async fn test_monte_carlo_risk_analysis() -> Result<()> {
    let num_simulations = 50;
    let mut max_drawdowns = Vec::new();

    for _ in 0..num_simulations {
        let market_data = generate_sample_market_data("BTC_USDT", 100, 50000.0, 0.025);

        let repos = Arc::new(MockBacktestingRepositories::new(
            Box::new(MockMarketDataRepository::with_data(market_data)),
            Box::new(MockTradingRepository::new()),
            Box::new(MockNewsRepository::new()),
        )) as Arc<dyn BacktestingRepositories>;

        let config = config::structures::BacktestingStrategyConfig::default();
        let engine = StrategyEngine::new(&config, repos).await?;

        let context = create_test_context(
            "buy_and_hold",
            vec!["BTC_USDT".to_string()],
            100000.0,
            HashMap::new(),
        );

        let trades = engine.execute_backtest(&context).await?;

        let perf_config = config::structures::BacktestingPerformanceConfig::default();
        let analyzer = PerformanceAnalyzer::new(&perf_config)?;
        let metrics = analyzer.calculate_metrics(&trades, 100000.0);

        max_drawdowns.push(metrics.max_drawdown);
    }

    // Calculate worst-case drawdown (95th percentile)
    max_drawdowns.sort_by(|a, b| b.partial_cmp(a).unwrap_or(std::cmp::Ordering::Equal));
    let worst_case_idx = (num_simulations as f64 * 0.05) as usize;
    let worst_case_dd = max_drawdowns[worst_case_idx];

    println!("95th percentile worst-case drawdown: {:.2}%", worst_case_dd);

    assert!(worst_case_dd >= 0.0, "Expected non-negative drawdown");

    Ok(())
}

// ==================== Helper Functions for Trade Generation ====================

fn generate_profitable_trades(count: usize, _initial_capital: f64) -> Vec<BacktestTrade> {
    let mut trades = Vec::new();
    let mut rng = rand::thread_rng();
    let base_time = Utc::now() - chrono::Duration::days(count as i64);

    for i in 0..count {
        let entry_price = Decimal::from_f64_retain(50000.0 + rng.gen_range(-1000.0..1000.0))
            .unwrap_or(Decimal::ZERO);
        // Variable profit: 1-3% to create volatility for Sharpe ratio calculation
        let profit_pct = 1.0 + rng.gen_range(0.01..0.03);
        let exit_price = entry_price * Decimal::from_f64_retain(profit_pct).unwrap();
        let quantity = Decimal::from_f64_retain(0.1).unwrap();
        let pnl = (exit_price - entry_price) * quantity;

        trades.push(BacktestTrade {
            trade_id: format!("trade_{}", i),
            symbol: "BTC_USDT".to_string(),
            side: TradeSide::Buy,
            quantity,
            entry_price,
            exit_price,
            entry_time: base_time + chrono::Duration::days(i as i64),
            exit_time: base_time + chrono::Duration::days(i as i64 + 1),
            pnl,
            return_percent: pnl / (entry_price * quantity),
            entry_signal: "buy".to_string(),
            exit_signal: "sell".to_string(),
        });
    }

    trades
}

fn generate_trades_with_drawdown(
    count: usize,
    _initial_capital: f64,
    max_dd: f64,
) -> Vec<BacktestTrade> {
    let mut trades = Vec::new();
    let mut rng = rand::thread_rng();
    let base_time = Utc::now() - chrono::Duration::days(count as i64);

    let drawdown_start = count / 3;
    let drawdown_end = 2 * count / 3;

    for i in 0..count {
        let entry_price = Decimal::from_f64_retain(50000.0).unwrap();

        // Create drawdown in the middle section
        let return_pct = if i >= drawdown_start && i < drawdown_end {
            -max_dd / (drawdown_end - drawdown_start) as f64
        } else {
            rng.gen_range(0.0..0.02)
        };

        let exit_price = entry_price * Decimal::from_f64_retain(1.0 + return_pct).unwrap();
        let quantity = Decimal::from_f64_retain(0.1).unwrap();
        let pnl = (exit_price - entry_price) * quantity;

        trades.push(BacktestTrade {
            trade_id: format!("trade_{}", i),
            symbol: "BTC_USDT".to_string(),
            side: TradeSide::Buy,
            quantity,
            entry_price,
            exit_price,
            entry_time: base_time + chrono::Duration::days(i as i64),
            exit_time: base_time + chrono::Duration::days(i as i64 + 1),
            pnl,
            return_percent: pnl / (entry_price * quantity),
            entry_signal: "buy".to_string(),
            exit_signal: "sell".to_string(),
        });
    }

    trades
}

fn generate_trades_with_win_rate(
    count: usize,
    _initial_capital: f64,
    win_rate: f64,
) -> Vec<BacktestTrade> {
    let mut trades = Vec::new();
    let mut rng = rand::thread_rng();
    let base_time = Utc::now() - chrono::Duration::days(count as i64);

    for i in 0..count {
        let entry_price = Decimal::from_f64_retain(50000.0).unwrap();

        // Determine if this trade wins
        let is_winner = rng.gen::<f64>() < win_rate;
        let return_pct = if is_winner { 0.02 } else { -0.01 };

        let exit_price = entry_price * Decimal::from_f64_retain(1.0 + return_pct).unwrap();
        let quantity = Decimal::from_f64_retain(0.1).unwrap();
        let pnl = (exit_price - entry_price) * quantity;

        trades.push(BacktestTrade {
            trade_id: format!("trade_{}", i),
            symbol: "BTC_USDT".to_string(),
            side: if is_winner {
                TradeSide::Buy
            } else {
                TradeSide::Sell
            },
            quantity,
            entry_price,
            exit_price,
            entry_time: base_time + chrono::Duration::days(i as i64),
            exit_time: base_time + chrono::Duration::days(i as i64 + 1),
            pnl,
            return_percent: pnl / (entry_price * quantity),
            entry_signal: "buy".to_string(),
            exit_signal: "sell".to_string(),
        });
    }

    trades
}
