#![allow(unexpected_cfgs)]
#![cfg(feature = "__trading_service_integration")]
//! ML Performance Metrics Tests - TDD Implementation
//!
//! Following strict RED-GREEN-REFACTOR methodology

use chrono::Utc;
use sqlx::PgPool;
use std::env;

/// Get test database pool
async fn get_test_db_pool() -> PgPool {
    let database_url = 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")
}

/// Helper to create test prediction
fn create_test_prediction() -> trading_service::ml_performance_metrics::MLPrediction {
    trading_service::ml_performance_metrics::MLPrediction {
        model_name: "DQN".to_string(),
        features: vec![0.5; 26],
        predicted_action: 0, // Buy
        confidence: 0.85,
        symbol: "ES.FUT".to_string(),
        timestamp: Utc::now(),
    }
}

#[tokio::test]
async fn test_ml_predictions_table_exists() {
    // RED: ml_predictions table doesn't exist yet
    let pool = get_test_db_pool().await;

    let result = sqlx::query("SELECT * FROM ml_predictions LIMIT 1")
        .fetch_optional(&pool)
        .await;

    assert!(result.is_ok(), "ml_predictions table should exist");
}

#[tokio::test]
async fn test_insert_ml_prediction() {
    // RED: MLMetricsStore doesn't exist yet
    let pool = get_test_db_pool().await;
    let store = trading_service::ml_performance_metrics::MLMetricsStore::new(pool.clone());

    let prediction = create_test_prediction();

    let prediction_id = store
        .insert_prediction(&prediction)
        .await
        .expect("Failed to insert prediction");
    assert!(prediction_id > 0, "Prediction ID should be positive");
}

#[tokio::test]
async fn test_record_outcome() {
    // RED: Test recording actual outcome
    let pool = get_test_db_pool().await;
    let store = trading_service::ml_performance_metrics::MLMetricsStore::new(pool.clone());

    let prediction = create_test_prediction();
    let prediction_id = store
        .insert_prediction(&prediction)
        .await
        .expect("Failed to insert prediction");

    // Record outcome after 5 minutes
    let outcome = trading_service::ml_performance_metrics::PredictionOutcome {
        prediction_id,
        actual_action: 0, // Actual was Buy (correct)
        pnl: 250.0,       // Profit
        timestamp: Utc::now(),
    };

    store
        .record_outcome(&outcome)
        .await
        .expect("Failed to record outcome");

    let stats = store
        .get_accuracy_stats("DQN")
        .await
        .expect("Failed to get accuracy stats");
    assert_eq!(stats.total_predictions, 1, "Total predictions should be 1");
    assert_eq!(
        stats.correct_predictions, 1,
        "Correct predictions should be 1"
    );
    assert!(
        (stats.accuracy - 1.0).abs() < 0.01,
        "Accuracy should be 1.0"
    );
}

#[tokio::test]
async fn test_model_accuracy_calculation() {
    // RED: Test accuracy calculation across multiple predictions
    let pool = get_test_db_pool().await;
    let store = trading_service::ml_performance_metrics::MLMetricsStore::new(pool.clone());

    // Clean up test data
    let model_name = format!("TEST_DQN_{}", Utc::now().timestamp_millis());

    // Insert 10 predictions
    for i in 0..10 {
        let pred = trading_service::ml_performance_metrics::MLPrediction {
            model_name: model_name.clone(),
            features: vec![0.5; 26],
            predicted_action: (i % 3) as i16, // Vary actions
            confidence: 0.8,
            symbol: "ES.FUT".to_string(),
            timestamp: Utc::now(),
        };

        let pred_id = store
            .insert_prediction(&pred)
            .await
            .expect("Failed to insert prediction");

        // Record outcomes (7/10 correct)
        let outcome = trading_service::ml_performance_metrics::PredictionOutcome {
            prediction_id: pred_id,
            actual_action: if i < 7 {
                pred.predicted_action
            } else {
                ((pred.predicted_action + 1) % 3)
            },
            pnl: if i < 7 { 100.0 } else { -50.0 },
            timestamp: Utc::now(),
        };

        store
            .record_outcome(&outcome)
            .await
            .expect("Failed to record outcome");
    }

    let stats = store
        .get_accuracy_stats(&model_name)
        .await
        .expect("Failed to get accuracy stats");
    assert_eq!(
        stats.total_predictions, 10,
        "Total predictions should be 10"
    );
    assert_eq!(
        stats.correct_predictions, 7,
        "Correct predictions should be 7"
    );
    assert!(
        (stats.accuracy - 0.7).abs() < 0.01,
        "Accuracy should be 0.7"
    );
}

#[tokio::test]
async fn test_sharpe_ratio_calculation() {
    // RED: Test Sharpe ratio calculation
    let pool = get_test_db_pool().await;
    let store = trading_service::ml_performance_metrics::MLMetricsStore::new(pool.clone());

    let model_name = format!("TEST_SHARPE_{}", Utc::now().timestamp_millis());

    // Insert predictions with PnL outcomes
    for pnl in vec![100.0, -50.0, 200.0, -30.0, 150.0] {
        let pred = trading_service::ml_performance_metrics::MLPrediction {
            model_name: model_name.clone(),
            features: vec![0.5; 26],
            predicted_action: 0,
            confidence: 0.8,
            symbol: "ES.FUT".to_string(),
            timestamp: Utc::now(),
        };

        let pred_id = store
            .insert_prediction(&pred)
            .await
            .expect("Failed to insert prediction");

        let outcome = trading_service::ml_performance_metrics::PredictionOutcome {
            prediction_id: pred_id,
            actual_action: pred.predicted_action,
            pnl,
            timestamp: Utc::now(),
        };

        store
            .record_outcome(&outcome)
            .await
            .expect("Failed to record outcome");
    }

    let sharpe = store
        .calculate_sharpe_ratio(&model_name)
        .await
        .expect("Failed to calculate Sharpe");
    assert!(sharpe > 0.0, "Sharpe ratio should be positive (profitable)");
}

#[tokio::test]
async fn test_ensemble_vs_individual_accuracy() {
    // RED: Test comparing ensemble accuracy vs individual models
    let pool = get_test_db_pool().await;
    let store = trading_service::ml_performance_metrics::MLMetricsStore::new(pool.clone());

    let timestamp = Utc::now().timestamp_millis();

    // Insert predictions for each model
    for model in &["DQN", "PPO", "MAMBA2", "TFT"] {
        let model_name = format!("TEST_{}_{}", model, timestamp);

        for _ in 0..5 {
            let pred = trading_service::ml_performance_metrics::MLPrediction {
                model_name: model_name.clone(),
                features: vec![0.5; 26],
                predicted_action: 0,
                confidence: 0.8,
                symbol: "ES.FUT".to_string(),
                timestamp: Utc::now(),
            };

            let pred_id = store
                .insert_prediction(&pred)
                .await
                .expect("Failed to insert prediction");

            let outcome = trading_service::ml_performance_metrics::PredictionOutcome {
                prediction_id: pred_id,
                actual_action: 0,
                pnl: 100.0,
                timestamp: Utc::now(),
            };

            store
                .record_outcome(&outcome)
                .await
                .expect("Failed to record outcome");
        }
    }

    let comparison = store
        .compare_model_accuracy()
        .await
        .expect("Failed to compare models");
    assert!(
        comparison.len() >= 4,
        "Should have at least 4 models in comparison"
    );

    for (model, accuracy) in comparison {
        assert!(!model.is_empty(), "Model name should not be empty");
        assert!(
            accuracy >= 0.0 && accuracy <= 1.0,
            "Accuracy should be between 0 and 1"
        );
    }
}
