#![allow(unexpected_cfgs)]
#![cfg(feature = "__backtesting_integration")]
//! Wave D Regime Backtesting Integration Test - TDD RED Phase
//!
//! This test validates regime-adaptive strategy backtesting:
//! 1. Load ES.FUT data (5000 bars, full trading day)
//! 2. Initialize backtesting engine with Wave D features enabled
//! 3. Run regime-adaptive strategy with position sizing and stop-loss adjustments
//! 4. Track regime-conditioned performance (Sharpe, PnL, win rate by regime)
//! 5. Compare vs. baseline (no regime adaptation)
//!
//! **Expected Improvement**: +25-50% Sharpe, -15-30% drawdown
//!
//! TDD Workflow:
//! - RED: This test will fail initially (missing regime feature integration)
//! - GREEN: Implement minimal code to pass
//! - REFACTOR: Optimize and clean up

use anyhow::Result;
use backtesting_service::ml_strategy_engine::MLStrategyEngine;
use num_traits::ToPrimitive;
use backtesting_service::service::{BacktestContext, BacktestStatus};
use backtesting_service::storage::StorageManager;
use rust_decimal::Decimal;
use std::collections::HashMap;
use std::sync::Arc;

// Import fixtures for real ES.FUT data
mod fixtures;
use fixtures::{get_es_fut_bars, get_regime_sample, RegimeType};

/// Helper to create backtest context with custom parameters
fn create_backtest_context(
    strategy_name: &str,
    symbol: &str,
    start_nanos: i64,
    end_nanos: i64,
    parameters: HashMap<String, String>,
) -> BacktestContext {
    BacktestContext {
        id: uuid::Uuid::new_v4().to_string(),
        status: BacktestStatus::Pending,
        progress: 0.0,
        current_date: String::new(),
        trades_executed: 0,
        current_pnl: 0.0,
        started_at: start_nanos,
        completed_at: Some(end_nanos),
        error_message: None,
        strategy_name: strategy_name.to_string(),
        symbols: vec![symbol.to_string()],
        initial_capital: 100000.0,
        parameters,
    }
}

/// Helper to calculate Sharpe ratio from PnL series
fn calculate_sharpe_ratio(pnl_series: &[f64]) -> f64 {
    if pnl_series.len() < 2 {
        return 0.0;
    }

    let mean = pnl_series.iter().sum::<f64>() / pnl_series.len() as f64;
    let variance =
        pnl_series.iter().map(|x| (x - mean).powi(2)).sum::<f64>() / pnl_series.len() as f64;
    let std_dev = variance.sqrt();

    if std_dev == 0.0 {
        return 0.0;
    }

    // Annualized Sharpe ratio (assuming 252 trading days)
    mean / std_dev * (252.0_f64).sqrt()
}

/// Helper to calculate maximum drawdown
fn calculate_max_drawdown(equity_curve: &[f64]) -> f64 {
    if equity_curve.is_empty() {
        return 0.0;
    }

    let mut max_equity = equity_curve[0];
    let mut max_drawdown = 0.0;

    for &equity in equity_curve.iter() {
        if equity > max_equity {
            max_equity = equity;
        }
        let drawdown = (max_equity - equity) / max_equity;
        if drawdown > max_drawdown {
            max_drawdown = drawdown;
        }
    }

    max_drawdown
}

/// Helper to calculate win rate from trades
fn calculate_win_rate(pnl_series: &[f64]) -> f64 {
    if pnl_series.is_empty() {
        return 0.0;
    }

    let winning_trades = pnl_series.iter().filter(|&&pnl| pnl > 0.0).count();
    winning_trades as f64 / pnl_series.len() as f64
}

#[tokio::test]
async fn test_red_regime_adaptive_backtest_basic() -> Result<()> {
    // RED: This test will fail because regime feature integration doesn't exist yet
    println!("\n🔴 RED Phase: Testing basic regime-adaptive backtest");

    // Load ES.FUT data (5000 bars)
    let market_data = get_es_fut_bars().await?;
    assert!(
        market_data.len() >= 5000,
        "Need at least 5000 bars for regime detection"
    );
    println!("✅ Loaded {} ES.FUT bars", market_data.len());

    // Create storage manager and ML strategy engine
    let storage_manager = Arc::new(
        StorageManager::new(&config::structures::BacktestingDatabaseConfig::default()).await?,
    );
    let config = config::structures::BacktestingStrategyConfig::default();
    let mut ml_engine = MLStrategyEngine::new(&config, storage_manager).await?;

    // Create backtest context with Wave D regime features enabled
    let start_nanos = market_data[0].timestamp.timestamp_nanos_opt().unwrap();
    let end_nanos = market_data
        .last()
        .unwrap()
        .timestamp
        .timestamp_nanos_opt()
        .unwrap();

    let mut parameters = HashMap::new();
    parameters.insert("enable_regime_features".to_string(), "true".to_string());
    parameters.insert("regime_position_sizing".to_string(), "true".to_string());
    parameters.insert("regime_stop_loss".to_string(), "true".to_string());
    parameters.insert("trending_multiplier".to_string(), "1.5".to_string());
    parameters.insert("volatile_multiplier".to_string(), "0.5".to_string());
    parameters.insert("crisis_multiplier".to_string(), "0.2".to_string());

    let context =
        create_backtest_context("ml_ensemble", "ES.FUT", start_nanos, end_nanos, parameters);

    // Execute backtest with regime adaptation
    let (trades, model_performance) = ml_engine.execute_ml_backtest(&context).await?;

    // Verify trades were executed
    assert!(!trades.is_empty(), "Should have executed trades");
    println!("✅ Executed {} trades", trades.len());

    // Verify model performance includes regime metrics
    assert!(
        !model_performance.is_empty(),
        "Should have model performance metrics"
    );
    println!(
        "✅ Tracked performance for {} models",
        model_performance.len()
    );

    // Calculate basic metrics
    let pnl_series: Vec<f64> = trades
        .iter()
        .map(|t| t.pnl.to_f64().unwrap_or(0.0))
        .collect();

    let sharpe = calculate_sharpe_ratio(&pnl_series);
    let win_rate = calculate_win_rate(&pnl_series);

    println!("\n📊 Regime-Adaptive Backtest Results:");
    println!("   Total Trades: {}", trades.len());
    println!("   Sharpe Ratio: {:.3}", sharpe);
    println!("   Win Rate: {:.2}%", win_rate * 100.0);

    // Basic validation (strict requirements in next test)
    assert!(sharpe > 0.0, "Sharpe ratio should be positive");
    assert!(win_rate > 0.4, "Win rate should be >40%");

    Ok(())
}

#[tokio::test]
async fn test_red_regime_vs_baseline_comparison() -> Result<()> {
    // RED: This test will fail because regime performance comparison doesn't exist yet
    println!("\n🔴 RED Phase: Testing regime-adaptive vs baseline comparison");

    // Load ES.FUT data
    let market_data = get_es_fut_bars().await?;
    assert!(market_data.len() >= 5000, "Need at least 5000 bars");

    let start_nanos = market_data[0].timestamp.timestamp_nanos_opt().unwrap();
    let end_nanos = market_data
        .last()
        .unwrap()
        .timestamp
        .timestamp_nanos_opt()
        .unwrap();

    // Create ML strategy engine
    let storage_manager = Arc::new(
        StorageManager::new(&config::structures::BacktestingDatabaseConfig::default()).await?,
    );
    let config = config::structures::BacktestingStrategyConfig::default();
    let mut ml_engine = MLStrategyEngine::new(&config, storage_manager.clone()).await?;

    // Run BASELINE backtest (NO regime adaptation)
    let mut baseline_params = HashMap::new();
    baseline_params.insert("enable_regime_features".to_string(), "false".to_string());

    let baseline_context = create_backtest_context(
        "ml_ensemble",
        "ES.FUT",
        start_nanos,
        end_nanos,
        baseline_params,
    );

    let (baseline_trades, _) = ml_engine.execute_ml_backtest(&baseline_context).await?;

    // Calculate baseline metrics
    let baseline_pnl: Vec<f64> = baseline_trades
        .iter()
        .map(|t| t.pnl.to_f64().unwrap_or(0.0))
        .collect();
    let baseline_sharpe = calculate_sharpe_ratio(&baseline_pnl);
    let baseline_win_rate = calculate_win_rate(&baseline_pnl);

    // Build equity curve for drawdown
    let mut baseline_equity = vec![100000.0];
    for pnl in &baseline_pnl {
        let new_equity = baseline_equity.last().expect("INVARIANT: Collection should be non-empty") + pnl;
        baseline_equity.push(new_equity);
    }
    let baseline_drawdown = calculate_max_drawdown(&baseline_equity);

    println!("\n📉 Baseline (No Regime Adaptation):");
    println!("   Trades: {}", baseline_trades.len());
    println!("   Sharpe: {:.3}", baseline_sharpe);
    println!("   Win Rate: {:.2}%", baseline_win_rate * 100.0);
    println!("   Max Drawdown: {:.2}%", baseline_drawdown * 100.0);

    // Run REGIME-ADAPTIVE backtest
    let mut ml_engine2 = MLStrategyEngine::new(&config, storage_manager).await?;
    let mut regime_params = HashMap::new();
    regime_params.insert("enable_regime_features".to_string(), "true".to_string());
    regime_params.insert("regime_position_sizing".to_string(), "true".to_string());
    regime_params.insert("regime_stop_loss".to_string(), "true".to_string());
    regime_params.insert("trending_multiplier".to_string(), "1.5".to_string());
    regime_params.insert("volatile_multiplier".to_string(), "0.5".to_string());
    regime_params.insert("crisis_multiplier".to_string(), "0.2".to_string());

    let regime_context = create_backtest_context(
        "ml_ensemble",
        "ES.FUT",
        start_nanos,
        end_nanos,
        regime_params,
    );

    let (regime_trades, _) = ml_engine2.execute_ml_backtest(&regime_context).await?;

    // Calculate regime-adaptive metrics
    let regime_pnl: Vec<f64> = regime_trades
        .iter()
        .map(|t| t.pnl.to_f64().unwrap_or(0.0))
        .collect();
    let regime_sharpe = calculate_sharpe_ratio(&regime_pnl);
    let regime_win_rate = calculate_win_rate(&regime_pnl);

    let mut regime_equity = vec![100000.0];
    for pnl in &regime_pnl {
        let new_equity = regime_equity.last().expect("INVARIANT: Collection should be non-empty") + pnl;
        regime_equity.push(new_equity);
    }
    let regime_drawdown = calculate_max_drawdown(&regime_equity);

    println!("\n📈 Regime-Adaptive Strategy:");
    println!("   Trades: {}", regime_trades.len());
    println!("   Sharpe: {:.3}", regime_sharpe);
    println!("   Win Rate: {:.2}%", regime_win_rate * 100.0);
    println!("   Max Drawdown: {:.2}%", regime_drawdown * 100.0);

    // Calculate improvement
    let sharpe_improvement = ((regime_sharpe - baseline_sharpe) / baseline_sharpe.abs()) * 100.0;
    let drawdown_improvement =
        ((baseline_drawdown - regime_drawdown) / baseline_drawdown.abs()) * 100.0;

    println!("\n🎯 Improvement vs Baseline:");
    println!("   Sharpe: {:+.1}%", sharpe_improvement);
    println!("   Drawdown: {:+.1}%", drawdown_improvement);

    // Verify improvement targets (Wave D goals: +25-50% Sharpe, -15-30% drawdown)
    assert!(
        regime_sharpe >= baseline_sharpe,
        "Regime-adaptive should match or beat baseline Sharpe"
    );
    assert!(
        regime_drawdown <= baseline_drawdown,
        "Regime-adaptive should have lower drawdown"
    );

    // Aspirational targets (may not hit immediately with untrained models)
    if sharpe_improvement >= 25.0 {
        println!("   ✅ ACHIEVED Sharpe improvement target (+25%)!");
    }
    if drawdown_improvement >= 15.0 {
        println!("   ✅ ACHIEVED Drawdown improvement target (-15%)!");
    }

    Ok(())
}

#[tokio::test]
async fn test_red_regime_conditioned_performance() -> Result<()> {
    // RED: This test will fail because per-regime performance tracking doesn't exist yet
    println!("\n🔴 RED Phase: Testing regime-conditioned performance tracking");

    // Load regime-specific samples
    let trending_bars = get_regime_sample(RegimeType::Trending).await?;
    let volatile_bars = get_regime_sample(RegimeType::Volatile).await?;
    let ranging_bars = get_regime_sample(RegimeType::Ranging).await?;

    println!("✅ Loaded regime samples:");
    println!("   Trending: {} bars", trending_bars.len());
    println!("   Volatile: {} bars", volatile_bars.len());
    println!("   Ranging: {} bars", ranging_bars.len());

    // Create ML strategy engine
    let storage_manager = Arc::new(
        StorageManager::new(&config::structures::BacktestingDatabaseConfig::default()).await?,
    );
    let config = config::structures::BacktestingStrategyConfig::default();
    let mut ml_engine = MLStrategyEngine::new(&config, storage_manager).await?;

    // Run backtest on TRENDING regime
    let trending_start = trending_bars[0].timestamp.timestamp_nanos_opt().unwrap();
    let trending_end = trending_bars
        .last()
        .unwrap()
        .timestamp
        .timestamp_nanos_opt()
        .unwrap();

    let mut trending_params = HashMap::new();
    trending_params.insert("enable_regime_features".to_string(), "true".to_string());
    trending_params.insert("regime_position_sizing".to_string(), "true".to_string());
    trending_params.insert("trending_multiplier".to_string(), "1.5".to_string());

    let trending_context = create_backtest_context(
        "ml_ensemble",
        "ES.FUT",
        trending_start,
        trending_end,
        trending_params,
    );

    let (trending_trades, _) = ml_engine.execute_ml_backtest(&trending_context).await?;

    // Calculate trending regime metrics
    let trending_pnl: Vec<f64> = trending_trades
        .iter()
        .map(|t| t.pnl.to_f64().unwrap_or(0.0))
        .collect();
    let trending_sharpe = calculate_sharpe_ratio(&trending_pnl);
    let trending_win_rate = calculate_win_rate(&trending_pnl);

    println!("\n📊 Trending Regime Performance:");
    println!("   Trades: {}", trending_trades.len());
    println!("   Sharpe: {:.3}", trending_sharpe);
    println!("   Win Rate: {:.2}%", trending_win_rate * 100.0);

    // Trending regime should benefit from 1.5x position multiplier
    assert!(
        trending_sharpe > 0.0,
        "Trending regime should be profitable"
    );
    assert!(
        trending_trades.len() > 0,
        "Should execute trades in trending regime"
    );

    // Run backtest on VOLATILE regime (reduced position sizing)
    let volatile_start = volatile_bars[0].timestamp.timestamp_nanos_opt().unwrap();
    let volatile_end = volatile_bars
        .last()
        .unwrap()
        .timestamp
        .timestamp_nanos_opt()
        .unwrap();

    let mut volatile_params = HashMap::new();
    volatile_params.insert("enable_regime_features".to_string(), "true".to_string());
    volatile_params.insert("regime_position_sizing".to_string(), "true".to_string());
    volatile_params.insert("volatile_multiplier".to_string(), "0.5".to_string());

    let volatile_context = create_backtest_context(
        "ml_ensemble",
        "ES.FUT",
        volatile_start,
        volatile_end,
        volatile_params,
    );

    let mut ml_engine2 = MLStrategyEngine::new(&config, storage_manager.clone()).await?;
    let (volatile_trades, _) = ml_engine2.execute_ml_backtest(&volatile_context).await?;

    // Calculate volatile regime metrics
    let volatile_pnl: Vec<f64> = volatile_trades
        .iter()
        .map(|t| t.pnl.to_f64().unwrap_or(0.0))
        .collect();
    let volatile_sharpe = calculate_sharpe_ratio(&volatile_pnl);
    let volatile_win_rate = calculate_win_rate(&volatile_pnl);

    println!("\n📊 Volatile Regime Performance:");
    println!("   Trades: {}", volatile_trades.len());
    println!("   Sharpe: {:.3}", volatile_sharpe);
    println!("   Win Rate: {:.2}%", volatile_win_rate * 100.0);

    // Volatile regime should have reduced drawdown due to 0.5x position multiplier
    assert!(
        volatile_trades.len() > 0,
        "Should execute trades in volatile regime"
    );

    // Verify regime-specific metrics tracked
    println!("\n✅ Regime-conditioned performance tracking validated");

    Ok(())
}

#[tokio::test]
async fn test_red_regime_attribution_analysis() -> Result<()> {
    // RED: This test will fail because PnL attribution by regime doesn't exist yet
    println!("\n🔴 RED Phase: Testing PnL attribution by regime");

    // Load full ES.FUT dataset
    let market_data = get_es_fut_bars().await?;
    let start_nanos = market_data[0].timestamp.timestamp_nanos_opt().unwrap();
    let end_nanos = market_data
        .last()
        .unwrap()
        .timestamp
        .timestamp_nanos_opt()
        .unwrap();

    // Create ML strategy engine with regime tracking
    let storage_manager = Arc::new(
        StorageManager::new(&config::structures::BacktestingDatabaseConfig::default()).await?,
    );
    let config = config::structures::BacktestingStrategyConfig::default();
    let mut ml_engine = MLStrategyEngine::new(&config, storage_manager).await?;

    let mut params = HashMap::new();
    params.insert("enable_regime_features".to_string(), "true".to_string());
    params.insert("regime_attribution".to_string(), "true".to_string());
    params.insert("regime_position_sizing".to_string(), "true".to_string());

    let context = create_backtest_context("ml_ensemble", "ES.FUT", start_nanos, end_nanos, params);

    let (trades, _) = ml_engine.execute_ml_backtest(&context).await?;

    // Aggregate PnL by regime (this will require regime detection integration)
    // For now, we validate the structure exists
    assert!(!trades.is_empty(), "Should have executed trades");

    // Future: Extract regime_type from trade metadata
    // let mut pnl_by_regime: HashMap<String, Vec<f64>> = HashMap::new();
    // for trade in &trades {
    //     let regime = trade.metadata.get("regime_type").unwrap_or(&"Unknown".to_string());
    //     pnl_by_regime.entry(regime.clone()).or_default()
    //         .push(trade.pnl.to_string().parse::<f64>().unwrap_or(0.0));
    // }

    println!("\n📊 PnL Attribution by Regime:");
    println!("   (Implementation pending - requires regime metadata in trades)");
    println!("   Total Trades: {}", trades.len());

    // Verify basic structure
    assert!(
        trades.len() > 100,
        "Should have sufficient trades for attribution analysis"
    );

    Ok(())
}

#[tokio::test]
async fn test_red_regime_performance_targets() -> Result<()> {
    // RED: This test validates production targets are met
    println!("\n🔴 RED Phase: Testing regime-adaptive performance targets");

    let market_data = get_es_fut_bars().await?;
    let start_nanos = market_data[0].timestamp.timestamp_nanos_opt().unwrap();
    let end_nanos = market_data
        .last()
        .unwrap()
        .timestamp
        .timestamp_nanos_opt()
        .unwrap();

    let storage_manager = Arc::new(
        StorageManager::new(&config::structures::BacktestingDatabaseConfig::default()).await?,
    );
    let config = config::structures::BacktestingStrategyConfig::default();
    let mut ml_engine = MLStrategyEngine::new(&config, storage_manager).await?;

    let mut params = HashMap::new();
    params.insert("enable_regime_features".to_string(), "true".to_string());
    params.insert("regime_position_sizing".to_string(), "true".to_string());
    params.insert("regime_stop_loss".to_string(), "true".to_string());

    let context = create_backtest_context("ml_ensemble", "ES.FUT", start_nanos, end_nanos, params);

    let (trades, model_performance) = ml_engine.execute_ml_backtest(&context).await?;

    // Calculate final metrics
    let pnl_series: Vec<f64> = trades
        .iter()
        .map(|t| t.pnl.to_f64().unwrap_or(0.0))
        .collect();
    let sharpe = calculate_sharpe_ratio(&pnl_series);
    let win_rate = calculate_win_rate(&pnl_series);

    let mut equity_curve = vec![100000.0];
    for pnl in &pnl_series {
        equity_curve.push(equity_curve.last().expect("INVARIANT: Collection should be non-empty") + pnl);
    }
    let max_drawdown = calculate_max_drawdown(&equity_curve);

    println!("\n🎯 Production Performance Targets:");
    println!("   Sharpe Ratio: {:.3} (target: >1.5)", sharpe);
    println!("   Win Rate: {:.2}% (target: >55%)", win_rate * 100.0);
    println!(
        "   Max Drawdown: {:.2}% (target: <20%)",
        max_drawdown * 100.0
    );
    println!("   Total Trades: {} (target: >100)", trades.len());

    // Validate minimum performance
    assert!(sharpe > 0.0, "Sharpe ratio should be positive");
    assert!(win_rate > 0.4, "Win rate should be >40%");
    assert!(max_drawdown < 0.5, "Max drawdown should be <50%");
    assert!(trades.len() > 50, "Should execute >50 trades");

    // Check if production targets achieved
    let mut targets_met = 0;
    let mut total_targets = 4;

    if sharpe > 1.5 {
        println!("   ✅ Sharpe target MET");
        targets_met += 1;
    }
    if win_rate > 0.55 {
        println!("   ✅ Win rate target MET");
        targets_met += 1;
    }
    if max_drawdown < 0.20 {
        println!("   ✅ Drawdown target MET");
        targets_met += 1;
    }
    if trades.len() > 100 {
        println!("   ✅ Trade count target MET");
        targets_met += 1;
    }

    println!("\n📈 Targets Achieved: {}/{}", targets_met, total_targets);

    // Verify model performance tracking
    assert!(
        !model_performance.is_empty(),
        "Should track model performance"
    );
    for (model_id, perf) in &model_performance {
        println!("\nModel: {}", model_id);
        println!("   Sharpe: {:.3}", perf.sharpe_ratio);
        println!("   Accuracy: {:.2}%", perf.accuracy_percentage);
    }

    Ok(())
}
