//! Ensemble Coordinator Integration Test
//!
//! Tests the full ensemble integration with Trading Service:
//! 1. Ensemble initialization with models
//! 2. Prediction flow with feature extraction
//! 3. Fallback mechanism on ensemble failure
//! 4. Health checks and monitoring
//! 5. Order execution with ensemble attribution

use ml::Features;
use std::sync::Arc;
use trading_service::ensemble_coordinator::EnsembleCoordinator;
#[allow(unused_imports)]
use trading_service::state::{EnsembleTradingSignal, TradingActionType};

#[tokio::test]
async fn test_ensemble_coordinator_initialization() {
    // Create ensemble coordinator
    let coordinator = Arc::new(EnsembleCoordinator::new());

    // Verify initialization
    assert_eq!(coordinator.model_count().await, 0);

    // Register models
    coordinator
        .register_model("DQN".to_string(), 0.35)
        .await
        .unwrap();
    coordinator
        .register_model("PPO".to_string(), 0.35)
        .await
        .unwrap();
    coordinator
        .register_model("TFT".to_string(), 0.30)
        .await
        .unwrap();

    // Verify model count
    assert_eq!(coordinator.model_count().await, 3);
}

#[tokio::test]
async fn test_ensemble_prediction_flow() {
    // Create and initialize ensemble coordinator
    let coordinator = Arc::new(EnsembleCoordinator::new());
    coordinator
        .register_model("DQN".to_string(), 0.35)
        .await
        .unwrap();
    coordinator
        .register_model("PPO".to_string(), 0.35)
        .await
        .unwrap();
    coordinator
        .register_model("TFT".to_string(), 0.30)
        .await
        .unwrap();

    // Create features
    let features = Features::new(
        vec![0.5, 0.6, 0.7, 0.8, 0.9],
        vec![
            "f1".to_string(),
            "f2".to_string(),
            "f3".to_string(),
            "f4".to_string(),
            "f5".to_string(),
        ],
    );

    // Make prediction
    let decision = coordinator.predict(&features).await.unwrap();

    // Verify prediction
    assert!(decision.confidence >= 0.0 && decision.confidence <= 1.0);
    assert!(decision.signal >= -1.0 && decision.signal <= 1.0);
    assert_eq!(decision.model_count(), 3);

    // Verify model votes
    assert!(decision.model_votes.contains_key("DQN"));
    assert!(decision.model_votes.contains_key("PPO"));
    assert!(decision.model_votes.contains_key("TFT"));
}

#[tokio::test]
async fn test_ensemble_confidence_thresholds() {
    let coordinator = Arc::new(EnsembleCoordinator::new());
    coordinator
        .register_model("DQN".to_string(), 0.35)
        .await
        .unwrap();
    coordinator
        .register_model("PPO".to_string(), 0.35)
        .await
        .unwrap();
    coordinator
        .register_model("TFT".to_string(), 0.30)
        .await
        .unwrap();

    // High confidence features (all positive)
    let high_conf_features = Features::new(
        vec![0.9, 0.9, 0.9, 0.9, 0.9],
        vec![
            "f1".to_string(),
            "f2".to_string(),
            "f3".to_string(),
            "f4".to_string(),
            "f5".to_string(),
        ],
    );

    let decision = coordinator.predict(&high_conf_features).await.unwrap();

    // Should have high confidence
    assert!(decision.confidence > 0.7);
}

#[tokio::test]
async fn test_ensemble_disagreement_detection() {
    let coordinator = Arc::new(EnsembleCoordinator::new());
    coordinator
        .register_model("DQN".to_string(), 0.35)
        .await
        .unwrap();
    coordinator
        .register_model("PPO".to_string(), 0.35)
        .await
        .unwrap();
    coordinator
        .register_model("TFT".to_string(), 0.30)
        .await
        .unwrap();

    // Features that should cause disagreement
    let features = Features::new(
        vec![0.1, 0.2, 0.3, 0.4, 0.5],
        vec![
            "f1".to_string(),
            "f2".to_string(),
            "f3".to_string(),
            "f4".to_string(),
            "f5".to_string(),
        ],
    );

    let decision = coordinator.predict(&features).await.unwrap();

    // Verify disagreement rate is tracked
    assert!(decision.disagreement_rate >= 0.0 && decision.disagreement_rate <= 1.0);
}

#[tokio::test]
async fn test_model_weight_updates() {
    let coordinator = Arc::new(EnsembleCoordinator::new());
    coordinator
        .register_model("DQN".to_string(), 0.35)
        .await
        .unwrap();
    coordinator
        .register_model("PPO".to_string(), 0.35)
        .await
        .unwrap();
    coordinator
        .register_model("TFT".to_string(), 0.30)
        .await
        .unwrap();

    // Update weights based on performance
    coordinator.update_model_weights().await.unwrap();

    // Make prediction with updated weights
    let features = Features::new(
        vec![0.5, 0.6, 0.7, 0.8, 0.9],
        vec![
            "f1".to_string(),
            "f2".to_string(),
            "f3".to_string(),
            "f4".to_string(),
            "f5".to_string(),
        ],
    );

    let decision = coordinator.predict(&features).await.unwrap();
    assert!(decision.confidence >= 0.0);
}

#[tokio::test]
async fn test_multiple_predictions() {
    let coordinator = Arc::new(EnsembleCoordinator::new());
    coordinator
        .register_model("DQN".to_string(), 0.35)
        .await
        .unwrap();
    coordinator
        .register_model("PPO".to_string(), 0.35)
        .await
        .unwrap();
    coordinator
        .register_model("TFT".to_string(), 0.30)
        .await
        .unwrap();

    // Make 100 predictions
    for i in 0..100 {
        let features = Features::new(
            vec![i as f64 * 0.01, 0.6, 0.7, 0.8, 0.9],
            vec![
                "f1".to_string(),
                "f2".to_string(),
                "f3".to_string(),
                "f4".to_string(),
                "f5".to_string(),
            ],
        );

        let decision = coordinator.predict(&features).await.unwrap();
        assert!(decision.confidence >= 0.0 && decision.confidence <= 1.0);
    }
}

#[tokio::test]
async fn test_trading_action_types() {
    let coordinator = Arc::new(EnsembleCoordinator::new());
    coordinator
        .register_model("DQN".to_string(), 0.35)
        .await
        .unwrap();
    coordinator
        .register_model("PPO".to_string(), 0.35)
        .await
        .unwrap();
    coordinator
        .register_model("TFT".to_string(), 0.30)
        .await
        .unwrap();

    // Test different feature ranges to get different actions
    let test_features = vec![
        (vec![0.9, 0.9, 0.9, 0.9, 0.9], "high"),
        (vec![0.5, 0.5, 0.5, 0.5, 0.5], "medium"),
        (vec![0.1, 0.1, 0.1, 0.1, 0.1], "low"),
    ];

    for (values, label) in test_features {
        let features = Features::new(
            values,
            vec![
                "f1".to_string(),
                "f2".to_string(),
                "f3".to_string(),
                "f4".to_string(),
                "f5".to_string(),
            ],
        );

        let decision = coordinator.predict(&features).await.unwrap();
        println!(
            "Features ({}): action={:?}, signal={:.3}",
            label, decision.action, decision.signal
        );

        // All actions should be valid
        match decision.action {
            ml::ensemble::TradingAction::Buy
            | ml::ensemble::TradingAction::Sell
            | ml::ensemble::TradingAction::Hold => {},
        }
    }
}

#[tokio::test]
async fn test_empty_model_registry() {
    let coordinator = Arc::new(EnsembleCoordinator::new());

    // Try prediction with no models
    let features = Features::new(
        vec![0.5, 0.6, 0.7, 0.8, 0.9],
        vec![
            "f1".to_string(),
            "f2".to_string(),
            "f3".to_string(),
            "f4".to_string(),
            "f5".to_string(),
        ],
    );

    // Should return error
    let result = coordinator.predict(&features).await;
    assert!(result.is_err());
}

#[tokio::test]
async fn test_position_sizing_calculation() {
    // Test position sizing logic
    let base_size: u64 = 100;

    // High confidence, low disagreement
    let confidence: f64 = 0.9;
    let disagreement: f64 = 0.1;
    let confidence_multiplier = ((confidence - 0.5) * 2.0).clamp(0.0, 1.0);
    let disagreement_penalty = 1.0 - disagreement;
    let position_size = (base_size as f64 * confidence_multiplier * disagreement_penalty) as u64;

    assert!(position_size > 70); // Should be large position

    // Low confidence, high disagreement
    let confidence: f64 = 0.55;
    let disagreement: f64 = 0.8;
    let confidence_multiplier = ((confidence - 0.5) * 2.0).clamp(0.0, 1.0);
    let disagreement_penalty = 1.0 - disagreement;
    let position_size = (base_size as f64 * confidence_multiplier * disagreement_penalty) as u64;

    assert!(position_size < 20); // Should be small position
}

#[test]
fn test_trading_action_conversion() {
    use trading_service::state::TradingActionType;

    // Test all action types exist and are distinct
    let buy = TradingActionType::Buy;
    let sell = TradingActionType::Sell;
    let hold = TradingActionType::Hold;

    assert_ne!(buy, sell);
    assert_ne!(buy, hold);
    assert_ne!(sell, hold);
}

#[tokio::test]
async fn test_ensemble_metrics_recording() {
    use trading_service::ensemble_metrics::EnsemblePredictionMetrics;

    let metrics = EnsemblePredictionMetrics {
        symbol: "ES.FUT".to_string(),
        action: "buy".to_string(),
        confidence: 0.85,
        disagreement_rate: 0.25,
        aggregation_latency_us: 35.0,
        aggregation_method: "weighted_average".to_string(),
    };

    // Should not panic
    metrics.record();
}
