#![allow(unexpected_cfgs)]
#![cfg(feature = "__trading_service_integration")]
//! TDD Integration Tests for Paper Trading ML Integration
//!
//! Mission: Integrate ML predictions with paper trading executor using strict TDD methodology
//! Phase: RED (Failing Tests) → GREEN (Minimal Implementation) → REFACTOR (Quality)
//!
//! Tests cover:
//! - Paper trading with ML signals
//! - ML signal to order conversion
//! - Position sizing based on confidence
//! - ML prediction tracking in PostgreSQL
//! - Risk limits override ML signals
//! - Fallback to rule-based on ML failure
//! - Performance feedback loop

use ml_core::native_types::NativeDevice;
use common::{CommonError, OrderSide, OrderType};
use sqlx::PgPool;
use std::path::PathBuf;
use uuid::Uuid;

// ============================================================================
// Helper Functions (Test Infrastructure)
// ============================================================================

/// Create test database pool
async fn get_test_db_pool() -> PgPool {
    let database_url = std::env::var("DATABASE_URL").unwrap_or_else(|_| {
        "postgresql://foxhunt:foxhunt_dev_password@localhost:5432/foxhunt".to_string()
    });

    PgPool::connect(&database_url)
        .await
        .expect("Failed to connect to test database")
}

/// Create test ensemble coordinator with 3 models (DQN, PPO, MAMBA2)
fn create_test_ensemble() -> std::sync::Arc<trading_service::EnsembleCoordinator> {
    use std::sync::Arc;

    let coordinator = Arc::new(trading_service::EnsembleCoordinator::new());

    // Note: In real usage, models would be loaded and registered with the coordinator
    // For tests, we create a minimal ensemble coordinator without loaded models

    coordinator
}

/// Create test paper trading executor with ML
async fn create_test_executor_with_ml(pool: PgPool) -> trading_service::PaperTradingExecutor {
    let ensemble = create_test_ensemble();

    trading_service::PaperTradingExecutor::new_with_ml(pool, ensemble)
        .await
        .expect("Failed to create executor with ML")
}

/// Load test OHLCV data (50 bars)
fn load_test_ohlcv_data(_symbol: &str, num_bars: usize) -> Vec<(f64, f64, f64, f64, f64)> {
    // Generate synthetic OHLCV data with realistic pattern
    let mut data = Vec::new();
    let mut base_price = 4500.0; // ES.FUT starting price

    for i in 0..num_bars {
        let trend = (i as f64 * 0.1).sin(); // Add sine wave trend
        let open = base_price + trend * 10.0;
        let high = open + (i as f64 % 5.0) + 5.0;
        let low = open - (i as f64 % 3.0) - 3.0;
        let close = open + trend * 5.0;
        let volume = 1000.0 + (i as f64 * 10.0);

        data.push((open, high, low, close, volume));
        base_price = close; // Next bar starts from previous close
    }

    data
}

/// Trading signal structure
#[derive(Debug, Clone)]
struct TradingSignal {
    action: Option<Action>,
    confidence: f64,
    source: SignalSource,
    model_votes: Option<Vec<(String, usize, f32)>>,
}

/// Action enum
#[derive(Debug, Clone, Copy, PartialEq)]
enum Action {
    Buy,
    Sell,
    Hold,
}

/// Signal source
#[derive(Debug, Clone, Copy, PartialEq)]
enum SignalSource {
    ML,
    RuleBased,
}

/// Order structure for testing
#[derive(Debug, Clone)]
struct Order {
    id: Uuid,
    symbol: String,
    side: OrderSide,
    quantity: i32,
    order_type: OrderType,
    price: Option<i64>,
}

// ============================================================================
// TEST 1: Paper Trading with ML Signals (RED)
// ============================================================================

#[tokio::test]
#[ignore = "RED phase - will fail until implementation exists"]
async fn test_paper_trading_with_ml_signals() {
    // Arrange: Create executor with ML
    let pool = get_test_db_pool().await;
    let mut executor = create_test_executor_with_ml(pool).await;

    // Generate market data
    let market_data = load_test_ohlcv_data("ES.FUT", 50);

    // Act: Generate ML signal
    let result = executor.generate_ml_signal(&market_data).await;

    // Assert: Should generate valid ML signal
    assert!(
        result.is_ok(),
        "ML signal generation failed: {:?}",
        result.err()
    );
    let signal = result.unwrap();

    assert!(signal.action.is_some(), "ML signal should have an action");
    assert_eq!(signal.source, SignalSource::ML, "Source should be ML");
    assert!(
        signal.confidence >= 0.0 && signal.confidence <= 1.0,
        "Confidence should be in [0, 1], got {}",
        signal.confidence
    );
}

// ============================================================================
// TEST 2: ML Signal to Order Conversion (RED)
// ============================================================================

#[tokio::test]
#[ignore = "RED phase - will fail until implementation exists"]
async fn test_ml_signal_to_order_conversion() {
    // Arrange: Create executor and signal
    let pool = get_test_db_pool().await;
    let executor = create_test_executor_with_ml(pool).await;

    let signal = TradingSignal {
        action: Some(Action::Buy),
        confidence: 0.85,
        source: SignalSource::ML,
        model_votes: Some(vec![
            ("DQN".to_string(), 0, 0.9),
            ("PPO".to_string(), 0, 0.8),
        ]),
    };

    // Act: Convert signal to order
    let result = executor.convert_signal_to_order(&signal, "ES.FUT").await;

    // Assert: Order should be created correctly
    assert!(
        result.is_ok(),
        "Signal to order conversion failed: {:?}",
        result.err()
    );
    let order = result.unwrap();

    assert_eq!(order.side, OrderSide::Buy, "Order side should be Buy");
    assert!(order.quantity > 0, "Quantity should be positive");
    assert_eq!(
        order.order_type,
        OrderType::Market,
        "Should be market order"
    );
}

// ============================================================================
// TEST 3: Position Sizing Based on Confidence (RED)
// ============================================================================

#[tokio::test]
#[ignore = "RED phase - will fail until implementation exists"]
async fn test_position_sizing_based_on_confidence() {
    // Arrange: Create executor
    let pool = get_test_db_pool().await;
    let executor = create_test_executor_with_ml(pool).await;

    // High confidence signal (0.9)
    let high_conf_signal = TradingSignal {
        action: Some(Action::Buy),
        confidence: 0.9,
        source: SignalSource::ML,
        model_votes: None,
    };

    // Low confidence signal (0.6)
    let low_conf_signal = TradingSignal {
        action: Some(Action::Buy),
        confidence: 0.6,
        source: SignalSource::ML,
        model_votes: None,
    };

    // Act: Convert both to orders
    let high_conf_order = executor
        .convert_signal_to_order(&high_conf_signal, "ES.FUT")
        .await
        .expect("High confidence order failed");

    let low_conf_order = executor
        .convert_signal_to_order(&low_conf_signal, "ES.FUT")
        .await
        .expect("Low confidence order failed");

    // Assert: Higher confidence should result in larger position
    assert!(
        high_conf_order.quantity > low_conf_order.quantity,
        "High confidence ({}) should have larger position than low confidence ({})",
        high_conf_order.quantity,
        low_conf_order.quantity
    );
}

// ============================================================================
// TEST 4: ML Prediction Tracking in PostgreSQL (RED)
// ============================================================================

#[tokio::test]
#[ignore = "RED phase - will fail until implementation exists"]
async fn test_ml_prediction_tracking() {
    // Arrange: Create executor
    let pool = get_test_db_pool().await;
    let mut executor = create_test_executor_with_ml(pool.clone()).await;

    let signal = TradingSignal {
        action: Some(Action::Buy),
        confidence: 0.85,
        source: SignalSource::ML,
        model_votes: None,
    };

    // Act: Execute ML signal (should track prediction)
    let result = executor.execute_ml_signal(&signal, "ES.FUT").await;

    assert!(
        result.is_ok(),
        "ML signal execution failed: {:?}",
        result.err()
    );
    let order = result.unwrap();

    // Assert: Prediction should be stored in ml_predictions table
    let prediction = sqlx::query!(
        r#"
        SELECT id, predicted_action, confidence, symbol
        FROM ml_predictions
        WHERE order_id = $1
        ORDER BY prediction_timestamp DESC
        LIMIT 1
        "#,
        order.id
    )
    .fetch_optional(&pool)
    .await
    .expect("Failed to query predictions");

    assert!(
        prediction.is_some(),
        "Prediction should be stored in database"
    );

    let pred = prediction.unwrap();
    assert_eq!(
        pred.predicted_action, 0,
        "Predicted action should be 0 (Buy)"
    );
    assert!(
        (pred.confidence - 0.85).abs() < 0.01,
        "Confidence should be 0.85"
    );
    assert_eq!(pred.symbol, "ES.FUT", "Symbol should be ES.FUT");
}

// ============================================================================
// TEST 5: Risk Limits Override ML Signals (RED)
// ============================================================================

#[tokio::test]
#[ignore = "RED phase - will fail until implementation exists"]
async fn test_risk_limits_override_ml_signals() {
    // Arrange: Create executor and set position limit
    let pool = get_test_db_pool().await;
    let mut executor = create_test_executor_with_ml(pool).await;

    // Simulate position limit reached
    executor
        .set_position_limit("ES.FUT", 0)
        .await
        .expect("Failed to set position limit");

    let signal = TradingSignal {
        action: Some(Action::Buy),
        confidence: 0.95, // High confidence, but should be rejected
        source: SignalSource::ML,
        model_votes: None,
    };

    // Act: Try to execute ML signal
    let result = executor.execute_ml_signal(&signal, "ES.FUT").await;

    // Assert: Should reject due to position limit
    assert!(result.is_err(), "Should reject when position limit reached");

    let error = result.unwrap_err();
    let error_msg = error.to_string();
    assert!(
        error_msg.contains("Position limit") || error_msg.contains("position"),
        "Error should mention position limit, got: {}",
        error_msg
    );
}

// ============================================================================
// TEST 6: Fallback to Rule-Based on ML Failure (RED)
// ============================================================================

#[tokio::test]
#[ignore = "RED phase - will fail until implementation exists"]
async fn test_fallback_to_rule_based_on_ml_failure() {
    // Arrange: Create executor
    let pool = get_test_db_pool().await;
    let mut executor = create_test_executor_with_ml(pool).await;

    // Disable ML to simulate failure
    executor.disable_ml().await;

    let market_data = load_test_ohlcv_data("ES.FUT", 50);

    // Act: Generate signal (should fallback to rule-based)
    let result = executor.generate_signal(&market_data).await;

    // Assert: Should fallback successfully
    assert!(
        result.is_ok(),
        "Fallback to rule-based failed: {:?}",
        result.err()
    );
    let signal = result.unwrap();

    assert_eq!(
        signal.source,
        SignalSource::RuleBased,
        "Should fallback to rule-based"
    );
    assert!(
        signal.action.is_some(),
        "Rule-based should still generate signal"
    );
}

// ============================================================================
// TEST 7: ML Performance Feedback Loop (RED)
// ============================================================================

#[tokio::test]
#[ignore = "RED phase - will fail until implementation exists"]
async fn test_ml_performance_feedback_loop() {
    // Arrange: Create executor and execute signal
    let pool = get_test_db_pool().await;
    let mut executor = create_test_executor_with_ml(pool.clone()).await;

    let signal = TradingSignal {
        action: Some(Action::Buy),
        confidence: 0.85,
        source: SignalSource::ML,
        model_votes: None,
    };

    let order = executor
        .execute_ml_signal(&signal, "ES.FUT")
        .await
        .expect("ML signal execution failed");

    // Act: Record outcome (simulate profitable trade)
    let result = executor.record_outcome(order.id, 100.0).await;

    // Assert: Outcome should be recorded
    assert!(
        result.is_ok(),
        "Recording outcome failed: {:?}",
        result.err()
    );

    // Verify outcome in database
    let prediction = sqlx::query!(
        r#"
        SELECT actual_action, pnl, outcome_recorded_at
        FROM ml_predictions
        WHERE order_id = $1
        "#,
        order.id
    )
    .fetch_one(&pool)
    .await
    .expect("Failed to fetch prediction");

    assert!(
        prediction.actual_action.is_some(),
        "Actual action should be recorded"
    );
    assert!(prediction.pnl.is_some(), "PnL should be recorded");
    assert!(
        prediction.outcome_recorded_at.is_some(),
        "Outcome timestamp should be set"
    );
    assert!(
        (prediction.pnl.unwrap() - 100.0).abs() < 0.01,
        "PnL should be $100"
    );
}

// ============================================================================
// TEST 8: Confidence Threshold Filtering (RED)
// ============================================================================

#[tokio::test]
#[ignore = "RED phase - will fail until implementation exists"]
async fn test_confidence_threshold_filtering() {
    // Arrange: Create executor
    let pool = get_test_db_pool().await;
    let executor = create_test_executor_with_ml(pool).await;

    // Very low confidence signal (below trading threshold)
    let low_conf_signal = TradingSignal {
        action: Some(Action::Buy),
        confidence: 0.4, // Below 0.6 threshold
        source: SignalSource::ML,
        model_votes: None,
    };

    // Act: Try to convert to order
    let result = executor
        .convert_signal_to_order(&low_conf_signal, "ES.FUT")
        .await;

    // Assert: Should reject low confidence signals
    assert!(result.is_err(), "Should reject low confidence signals");

    let error = result.unwrap_err();
    assert!(
        error.to_string().contains("Confidence too low")
            || error.to_string().contains("confidence"),
        "Error should mention confidence threshold"
    );
}

// ============================================================================
// TEST 9: Multi-Symbol ML Trading (RED)
// ============================================================================

#[tokio::test]
#[ignore = "RED phase - will fail until implementation exists"]
async fn test_multi_symbol_ml_trading() {
    // Arrange: Create executor
    let pool = get_test_db_pool().await;
    let mut executor = create_test_executor_with_ml(pool).await;

    let symbols = vec!["ES.FUT", "NQ.FUT", "ZN.FUT"];

    // Act: Execute ML signals for multiple symbols
    for symbol in &symbols {
        let signal = TradingSignal {
            action: Some(Action::Buy),
            confidence: 0.8,
            source: SignalSource::ML,
            model_votes: None,
        };

        let result = executor.execute_ml_signal(&signal, symbol).await;
        assert!(result.is_ok(), "ML signal for {} failed", symbol);
    }

    // Assert: All symbols should have executed trades
    let position_summary = executor.get_position_summary().await;

    for symbol in &symbols {
        assert!(
            position_summary.contains_key(*symbol),
            "Position should exist for {}",
            symbol
        );
    }
}

// ============================================================================
// TEST 10: Ensemble Agreement Weighting (RED)
// ============================================================================

#[tokio::test]
#[ignore = "RED phase - will fail until implementation exists"]
async fn test_ensemble_agreement_weighting() {
    // Arrange: Create executor
    let pool = get_test_db_pool().await;
    let mut executor = create_test_executor_with_ml(pool).await;

    let market_data = load_test_ohlcv_data("ES.FUT", 50);

    // Act: Generate signal with ensemble voting
    let result = executor.generate_ml_signal(&market_data).await;

    assert!(result.is_ok());
    let signal = result.unwrap();

    // Assert: Should have model votes with agreement weighting
    assert!(signal.model_votes.is_some(), "Should have model votes");
    let votes = signal.model_votes.unwrap();

    // Check that confidence reflects ensemble agreement
    let agreement_ratio = calculate_agreement_ratio(&votes);

    // If all models agree, confidence should be high
    if agreement_ratio > 0.8 {
        assert!(
            signal.confidence > 0.7,
            "High agreement should result in high confidence, got {}",
            signal.confidence
        );
    }
}

/// Helper: Calculate agreement ratio from model votes
fn calculate_agreement_ratio(votes: &[(String, usize, f32)]) -> f64 {
    if votes.is_empty() {
        return 0.0;
    }

    // Count votes for most common action
    let mut action_counts = std::collections::HashMap::new();
    for (_, action, _) in votes {
        *action_counts.entry(action).or_insert(0) += 1;
    }

    let max_count = action_counts.values().max().copied().unwrap_or(0);
    max_count as f64 / votes.len() as f64
}
