#![allow(
    dead_code,
    unused_variables,
    clippy::manual_range_contains,
    clippy::needless_update
)]
//! TDD Tests for A/B Testing Pipeline
//!
//! This test suite validates the automated A/B testing pipeline for model deployment decisions.
//!
//! ## Test Coverage
//! 1. A/B test creation on model deployment
//! 2. Traffic splitting (50/50 control vs treatment)
//! 3. Metrics collection (Sharpe, win rate, drawdown)
//! 4. Statistical testing (t-test, p < 0.05)
//! 5. Deployment decision logic
//! 6. Rollback on failure

use anyhow::Result;
use sqlx::PgPool;
use uuid::Uuid;

use trading_service::ab_testing_pipeline::{
    ABTestingConfig, ABTestingPipeline, DeploymentDecision, ModelPerformanceMetrics,
};

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

    let pool = PgPool::connect(&database_url).await?;
    Ok(pool)
}

/// Test helper: Clean up test data
async fn cleanup_test_data(pool: &PgPool, test_id: &str) -> Result<()> {
    sqlx::query("DELETE FROM ab_test_results WHERE test_id LIKE $1")
        .bind(format!("{}%", test_id))
        .execute(pool)
        .await?;
    Ok(())
}

// ============================================================================
// TEST HELPERS MODULE
// ============================================================================

/// Test helper module for A/B testing pipeline tests
mod test_helpers {
    use super::*;

    /// Create test A/B configuration with 50/50 split
    ///
    /// # Arguments
    /// * `prefix` - Test ID prefix for namespacing
    ///
    /// # Returns
    /// ABTestingConfig with test-friendly defaults (min_sample_size=100)
    ///
    /// # Example
    /// ```
    /// let config = test_helpers::create_test_ab_config("my_test");
    /// assert_eq!(config.traffic_split, 0.5); // 50/50 split
    /// assert_eq!(config.min_sample_size, 100); // Fast for tests
    /// ```
    pub fn create_test_ab_config(prefix: &str) -> ABTestingConfig {
        ABTestingConfig {
            test_prefix: prefix.to_string(),
            min_sample_size: 100,     // Lower for faster tests
            traffic_split: 0.5,       // 50/50 control vs treatment
            significance_level: 0.05, // p < 0.05
            max_duration_hours: 24,   // 1 day
        }
    }

    /// Create test A/B configuration with custom parameters
    ///
    /// # Arguments
    /// * `prefix` - Test ID prefix
    /// * `min_sample_size` - Minimum samples per group
    /// * `traffic_split` - Traffic split ratio (0.0 to 1.0)
    ///
    /// # Example
    /// ```
    /// let config = test_helpers::create_custom_ab_config("test", 200, 0.7);
    /// assert_eq!(config.traffic_split, 0.7); // 70/30 split
    /// ```
    pub fn create_custom_ab_config(
        prefix: &str,
        min_sample_size: usize,
        traffic_split: f64,
    ) -> ABTestingConfig {
        ABTestingConfig {
            test_prefix: prefix.to_string(),
            min_sample_size,
            traffic_split,
            significance_level: 0.05,
            max_duration_hours: 24,
        }
    }

    /// Generate realistic mock metrics for A/B testing
    ///
    /// # Arguments
    /// * `predictions` - Total number of predictions
    /// * `win_rate` - Win rate (0.0 to 1.0)
    /// * `sharpe_ratio` - Annualized Sharpe ratio
    ///
    /// # Returns
    /// ModelPerformanceMetrics with calculated PnL and drawdown
    ///
    /// # Example
    /// ```
    /// let metrics = test_helpers::generate_mock_metrics(1000, 0.55, 1.2);
    /// assert_eq!(metrics.predictions, 1000);
    /// assert_eq!(metrics.win_rate, 0.55);
    /// assert_eq!(metrics.sharpe_ratio, 1.2);
    /// ```
    pub fn generate_mock_metrics(
        predictions: u64,
        win_rate: f64,
        sharpe_ratio: f64,
    ) -> ModelPerformanceMetrics {
        let correct_predictions = (predictions as f64 * win_rate) as u64;
        let avg_pnl = sharpe_ratio * 100.0; // Rough estimate
        let total_pnl = avg_pnl * predictions as f64;
        let max_drawdown = if sharpe_ratio < 0.0 {
            total_pnl.abs() * 0.3
        } else {
            0.0
        };

        ModelPerformanceMetrics {
            predictions,
            correct_predictions,
            win_rate,
            total_pnl,
            avg_pnl,
            sharpe_ratio,
            max_drawdown,
            avg_latency_us: 50.0, // Default latency
        }
    }

    /// Builder pattern for mock metrics with fine-grained control
    ///
    /// # Example
    /// ```
    /// let metrics = MockMetricsBuilder::new(1000)
    ///     .with_win_rate(0.65)
    ///     .with_sharpe(1.8)
    ///     .with_latency(35.0)
    ///     .build();
    /// ```
    pub struct MockMetricsBuilder {
        predictions: u64,
        correct_predictions: u64,
        win_rate: f64,
        total_pnl: f64,
        avg_pnl: f64,
        sharpe_ratio: f64,
        max_drawdown: f64,
        avg_latency_us: f64,
    }

    impl MockMetricsBuilder {
        pub fn new(predictions: u64) -> Self {
            Self {
                predictions,
                correct_predictions: 0,
                win_rate: 0.5,
                total_pnl: 0.0,
                avg_pnl: 0.0,
                sharpe_ratio: 0.0,
                max_drawdown: 0.0,
                avg_latency_us: 50.0,
            }
        }

        pub fn with_win_rate(mut self, win_rate: f64) -> Self {
            self.win_rate = win_rate;
            self.correct_predictions = (self.predictions as f64 * win_rate) as u64;
            self
        }

        pub fn with_sharpe(mut self, sharpe_ratio: f64) -> Self {
            self.sharpe_ratio = sharpe_ratio;
            self.avg_pnl = sharpe_ratio * 100.0;
            self.total_pnl = self.avg_pnl * self.predictions as f64;
            self
        }

        pub fn with_latency(mut self, latency_us: f64) -> Self {
            self.avg_latency_us = latency_us;
            self
        }

        pub fn with_max_drawdown(mut self, max_drawdown: f64) -> Self {
            self.max_drawdown = max_drawdown;
            self
        }

        pub fn build(self) -> ModelPerformanceMetrics {
            ModelPerformanceMetrics {
                predictions: self.predictions,
                correct_predictions: self.correct_predictions,
                win_rate: self.win_rate,
                total_pnl: self.total_pnl,
                avg_pnl: self.avg_pnl,
                sharpe_ratio: self.sharpe_ratio,
                max_drawdown: self.max_drawdown,
                avg_latency_us: self.avg_latency_us,
            }
        }
    }

    /// Assert that deployment decision is to rollout treatment
    ///
    /// # Example
    /// ```
    /// let decision = pipeline.make_deployment_decision(&test_id).await.unwrap();
    /// test_helpers::assert_rollout_decision(&decision, 0.2);
    /// ```
    #[track_caller]
    pub fn assert_rollout_decision(decision: &DeploymentDecision, min_improvement: f64) {
        match decision {
            DeploymentDecision::RolloutTreatment {
                sharpe_improvement, ..
            } => {
                assert!(
                    *sharpe_improvement >= min_improvement,
                    "Sharpe improvement {} below threshold {}",
                    sharpe_improvement,
                    min_improvement
                );
            },
            _ => panic!("Expected RolloutTreatment, got {:?}", decision),
        }
    }

    /// Assert that deployment decision is to revert to control
    #[track_caller]
    pub fn assert_revert_decision(decision: &DeploymentDecision) {
        match decision {
            DeploymentDecision::RevertToControl {
                sharpe_degradation, ..
            } => {
                assert!(
                    *sharpe_degradation < 0.0,
                    "Expected negative Sharpe degradation"
                );
            },
            _ => panic!("Expected RevertToControl, got {:?}", decision),
        }
    }

    /// Assert that deployment decision is neutral
    #[track_caller]
    pub fn assert_neutral_decision(decision: &DeploymentDecision) {
        assert!(
            matches!(decision, DeploymentDecision::Neutral { .. }),
            "Expected Neutral decision, got {:?}",
            decision
        );
    }

    /// Assert that deployment decision is inconclusive
    ///
    /// # Arguments
    /// * `decision` - The deployment decision to check
    /// * `expected_reason` - Substring expected in the reason message
    #[track_caller]
    pub fn assert_inconclusive_decision(decision: &DeploymentDecision, expected_reason: &str) {
        match decision {
            DeploymentDecision::Inconclusive { reason, .. } => {
                assert!(
                    reason.contains(expected_reason),
                    "Expected reason containing '{}', got '{}'",
                    expected_reason,
                    reason
                );
            },
            _ => panic!("Expected Inconclusive, got {:?}", decision),
        }
    }
}

// ============================================================================
// EXISTING TESTS
// ============================================================================

/// Test 1: Create A/B test on model deployment
#[tokio::test]
async fn test_create_ab_test_on_deployment() {
    let pool = create_test_pool()
        .await
        .expect("Failed to create test pool");
    let test_id = format!("test_create_{}", Uuid::new_v4());

    // Create pipeline
    let config = ABTestingConfig {
        test_prefix: test_id.clone(),
        min_sample_size: 100,
        traffic_split: 0.5,
        significance_level: 0.05,
        max_duration_hours: 24,
        ..Default::default()
    };

    let pipeline = ABTestingPipeline::new(pool.clone(), config);

    // Simulate model deployment event
    let control_model_id = "DQN_v1.0.0";
    let treatment_model_id = "DQN_v2.0.0";

    let result = pipeline
        .create_ab_test(control_model_id, treatment_model_id, "ES.FUT")
        .await;

    // Should create test successfully
    assert!(
        result.is_ok(),
        "Failed to create A/B test: {:?}",
        result.err()
    );

    let test_state = result.unwrap();
    assert_eq!(test_state.control_model, control_model_id);
    assert_eq!(test_state.treatment_model, treatment_model_id);
    assert_eq!(test_state.status, "running");

    // Cleanup
    cleanup_test_data(&pool, &test_id).await.unwrap();
}

/// Test 2: Traffic splitting (50/50 control vs treatment)
#[tokio::test]
async fn test_traffic_splitting_50_50() {
    let pool = create_test_pool()
        .await
        .expect("Failed to create test pool");
    let test_id = format!("test_split_{}", Uuid::new_v4());

    let config = ABTestingConfig {
        test_prefix: test_id.clone(),
        traffic_split: 0.5, // 50/50
        ..Default::default()
    };

    let pipeline = ABTestingPipeline::new(pool.clone(), config);

    // Create test
    let test_state = pipeline
        .create_ab_test("DQN_v1.0.0", "DQN_v2.0.0", "ES.FUT")
        .await
        .unwrap();

    // Simulate 1000 predictions with deterministic assignment
    let mut control_count = 0;
    let mut treatment_count = 0;

    for i in 0..1000 {
        let user_id = format!("user_{}", i);
        let group = pipeline
            .assign_traffic_group(&test_state.test_id, &user_id)
            .await
            .unwrap();

        match group.as_str() {
            "control" => control_count += 1,
            "treatment" => treatment_count += 1,
            _ => panic!("Unknown group: {}", group),
        }
    }

    // Should be close to 50/50 (within 10% tolerance)
    let control_ratio = control_count as f64 / 1000.0;
    assert!(
        control_ratio >= 0.40 && control_ratio <= 0.60,
        "Traffic split not balanced: {}% control",
        control_ratio * 100.0
    );

    cleanup_test_data(&pool, &test_id).await.unwrap();
}

/// Test 3: Metrics collection (Sharpe, win rate, drawdown)
#[tokio::test]
async fn test_metrics_collection() {
    let pool = create_test_pool()
        .await
        .expect("Failed to create test pool");
    let test_id = format!("test_metrics_{}", Uuid::new_v4());

    let config = ABTestingConfig {
        test_prefix: test_id.clone(),
        ..Default::default()
    };

    let pipeline = ABTestingPipeline::new(pool.clone(), config);

    let test_state = pipeline
        .create_ab_test("DQN_v1.0.0", "DQN_v2.0.0", "ES.FUT")
        .await
        .unwrap();

    // Record prediction outcomes for control group
    for i in 0..150 {
        let pnl = if i % 2 == 0 { 100.0 } else { -50.0 }; // 50% win rate, positive PnL
        let return_pct = pnl / 10000.0;

        pipeline
            .record_prediction_outcome(
                &test_state.test_id,
                "control",
                i % 2 == 0, // correct
                pnl,
                return_pct,
                50, // latency_us
            )
            .await
            .unwrap();
    }

    // Record prediction outcomes for treatment group (better performance)
    for i in 0..150 {
        let pnl = if i % 3 != 0 { 120.0 } else { -40.0 }; // 66% win rate, higher PnL
        let return_pct = pnl / 10000.0;

        pipeline
            .record_prediction_outcome(
                &test_state.test_id,
                "treatment",
                i % 3 != 0, // correct
                pnl,
                return_pct,
                45, // latency_us (faster)
            )
            .await
            .unwrap();
    }

    // Get metrics
    let metrics = pipeline
        .get_ab_test_metrics(&test_state.test_id)
        .await
        .unwrap();

    // Validate control metrics
    assert_eq!(metrics.control.predictions, 150);
    assert!(metrics.control.win_rate >= 0.48 && metrics.control.win_rate <= 0.52); // ~50%
    assert!(metrics.control.total_pnl > 0.0); // Positive PnL
    assert!(metrics.control.sharpe_ratio > 0.0); // Positive Sharpe

    // Validate treatment metrics (should be better)
    assert_eq!(metrics.treatment.predictions, 150);
    assert!(metrics.treatment.win_rate >= 0.64 && metrics.treatment.win_rate <= 0.68); // ~66%
    assert!(metrics.treatment.total_pnl > metrics.control.total_pnl); // Higher PnL
    assert!(metrics.treatment.sharpe_ratio > metrics.control.sharpe_ratio); // Higher Sharpe

    cleanup_test_data(&pool, &test_id).await.unwrap();
}

/// Test 4: Statistical testing (t-test, p < 0.05)
#[tokio::test]
async fn test_statistical_significance_testing() {
    let pool = create_test_pool()
        .await
        .expect("Failed to create test pool");
    let test_id = format!("test_stats_{}", Uuid::new_v4());

    let config = ABTestingConfig {
        test_prefix: test_id.clone(),
        min_sample_size: 100,
        significance_level: 0.05,
        ..Default::default()
    };

    let pipeline = ABTestingPipeline::new(pool.clone(), config);

    let test_state = pipeline
        .create_ab_test("DQN_v1.0.0", "DQN_v2.0.0", "ES.FUT")
        .await
        .unwrap();

    // Record control group (baseline performance)
    for i in 0..120 {
        let return_pct = 0.001; // Low return
        pipeline
            .record_prediction_outcome(
                &test_state.test_id,
                "control",
                i % 2 == 0,
                return_pct * 10000.0,
                return_pct,
                50,
            )
            .await
            .unwrap();
    }

    // Record treatment group (significantly better)
    for i in 0..120 {
        let return_pct = 0.003; // 3x higher return
        pipeline
            .record_prediction_outcome(
                &test_state.test_id,
                "treatment",
                i % 3 != 0,
                return_pct * 10000.0,
                return_pct,
                45,
            )
            .await
            .unwrap();
    }

    // Run statistical tests
    let test_result = pipeline
        .run_statistical_tests(&test_state.test_id)
        .await
        .unwrap();

    // Should detect significant difference
    assert!(
        test_result.sharpe_test.is_significant,
        "Sharpe difference not significant"
    );
    assert!(
        test_result.sharpe_test.p_value < 0.05,
        "P-value too high: {}",
        test_result.sharpe_test.p_value
    );

    // Should have positive effect
    assert!(
        test_result.sharpe_diff > 0.0,
        "Treatment not better than control"
    );

    cleanup_test_data(&pool, &test_id).await.unwrap();
}

/// Test 5: Deployment decision logic (rollout on success)
#[tokio::test]
async fn test_deployment_decision_rollout() {
    let pool = create_test_pool()
        .await
        .expect("Failed to create test pool");
    let test_id = format!("test_deploy_{}", Uuid::new_v4());

    let config = ABTestingConfig {
        test_prefix: test_id.clone(),
        min_sample_size: 100,
        ..Default::default()
    };

    let pipeline = ABTestingPipeline::new(pool.clone(), config);

    let test_state = pipeline
        .create_ab_test("DQN_v1.0.0", "DQN_v2.0.0", "ES.FUT")
        .await
        .unwrap();

    // Control: baseline
    for i in 0..120 {
        pipeline
            .record_prediction_outcome(
                &test_state.test_id,
                "control",
                i % 2 == 0,
                0.001 * 10000.0,
                0.001,
                50,
            )
            .await
            .unwrap();
    }

    // Treatment: significantly better
    for i in 0..120 {
        pipeline
            .record_prediction_outcome(
                &test_state.test_id,
                "treatment",
                i % 3 != 0,
                0.004 * 10000.0,
                0.004,
                40,
            )
            .await
            .unwrap();
    }

    // Make deployment decision
    let decision = pipeline
        .make_deployment_decision(&test_state.test_id)
        .await
        .unwrap();

    // Should recommend rollout
    match decision {
        DeploymentDecision::RolloutTreatment { reason, .. } => {
            assert!(
                reason.contains("outperforms"),
                "Unexpected reason: {}",
                reason
            );
        },
        _ => panic!("Expected RolloutTreatment, got {:?}", decision),
    }

    cleanup_test_data(&pool, &test_id).await.unwrap();
}

/// Test 6: Deployment decision logic (rollback on failure)
#[tokio::test]
async fn test_deployment_decision_rollback() {
    let pool = create_test_pool()
        .await
        .expect("Failed to create test pool");
    let test_id = format!("test_rollback_{}", Uuid::new_v4());

    let config = ABTestingConfig {
        test_prefix: test_id.clone(),
        min_sample_size: 100,
        ..Default::default()
    };

    let pipeline = ABTestingPipeline::new(pool.clone(), config);

    let test_state = pipeline
        .create_ab_test("DQN_v1.0.0", "DQN_v2.0.0", "ES.FUT")
        .await
        .unwrap();

    // Control: good baseline
    for i in 0..120 {
        pipeline
            .record_prediction_outcome(
                &test_state.test_id,
                "control",
                i % 2 != 0, // 50% win rate
                0.003 * 10000.0,
                0.003,
                50,
            )
            .await
            .unwrap();
    }

    // Treatment: significantly worse
    for i in 0..120 {
        pipeline
            .record_prediction_outcome(
                &test_state.test_id,
                "treatment",
                i % 3 == 0,       // 33% win rate
                -0.001 * 10000.0, // Negative PnL
                -0.001,
                60, // Slower
            )
            .await
            .unwrap();
    }

    // Make deployment decision
    let decision = pipeline
        .make_deployment_decision(&test_state.test_id)
        .await
        .unwrap();

    // Should recommend rollback
    match decision {
        DeploymentDecision::RevertToControl { reason, .. } => {
            assert!(
                reason.contains("underperforms"),
                "Unexpected reason: {}",
                reason
            );
        },
        _ => panic!("Expected RevertToControl, got {:?}", decision),
    }

    cleanup_test_data(&pool, &test_id).await.unwrap();
}

/// Test 7: Deployment decision logic (neutral - continue testing)
#[tokio::test]
async fn test_deployment_decision_neutral() {
    let pool = create_test_pool()
        .await
        .expect("Failed to create test pool");
    let test_id = format!("test_neutral_{}", Uuid::new_v4());

    let config = ABTestingConfig {
        test_prefix: test_id.clone(),
        min_sample_size: 100,
        ..Default::default()
    };

    let pipeline = ABTestingPipeline::new(pool.clone(), config);

    let test_state = pipeline
        .create_ab_test("DQN_v1.0.0", "DQN_v2.0.0", "ES.FUT")
        .await
        .unwrap();

    // Both groups have identical performance
    for i in 0..120 {
        pipeline
            .record_prediction_outcome(
                &test_state.test_id,
                "control",
                i % 2 == 0,
                0.002 * 10000.0,
                0.002,
                50,
            )
            .await
            .unwrap();

        pipeline
            .record_prediction_outcome(
                &test_state.test_id,
                "treatment",
                i % 2 == 0,
                0.002 * 10000.0,
                0.002,
                50,
            )
            .await
            .unwrap();
    }

    // Make deployment decision
    let decision = pipeline
        .make_deployment_decision(&test_state.test_id)
        .await
        .unwrap();

    // Should be neutral or inconclusive
    match decision {
        DeploymentDecision::Neutral { .. } | DeploymentDecision::Inconclusive { .. } => {
            // Expected
        },
        _ => panic!("Expected Neutral or Inconclusive, got {:?}", decision),
    }

    cleanup_test_data(&pool, &test_id).await.unwrap();
}

/// Test 8: Insufficient samples handling
#[tokio::test]
async fn test_insufficient_samples() {
    let pool = create_test_pool()
        .await
        .expect("Failed to create test pool");
    let test_id = format!("test_insufficient_{}", Uuid::new_v4());

    let config = ABTestingConfig {
        test_prefix: test_id.clone(),
        min_sample_size: 100,
        ..Default::default()
    };

    let pipeline = ABTestingPipeline::new(pool.clone(), config);

    let test_state = pipeline
        .create_ab_test("DQN_v1.0.0", "DQN_v2.0.0", "ES.FUT")
        .await
        .unwrap();

    // Record only 50 samples (below minimum)
    for i in 0..50 {
        pipeline
            .record_prediction_outcome(
                &test_state.test_id,
                "control",
                i % 2 == 0,
                0.002 * 10000.0,
                0.002,
                50,
            )
            .await
            .unwrap();
    }

    // Try to make decision with insufficient samples
    let result = pipeline.make_deployment_decision(&test_state.test_id).await;

    // Should return Inconclusive due to insufficient samples
    match result {
        Ok(DeploymentDecision::Inconclusive {
            reason,
            control_samples,
            treatment_samples,
            required_samples,
        }) => {
            assert!(
                reason.contains("insufficient") || reason.contains("samples"),
                "Unexpected reason: {}",
                reason
            );
            assert!(
                control_samples < required_samples || treatment_samples < required_samples,
                "Expected insufficient samples, but got control={}, treatment={}, required={}",
                control_samples,
                treatment_samples,
                required_samples
            );
        },
        other => panic!("Expected Inconclusive error, got {:?}", other),
    }

    cleanup_test_data(&pool, &test_id).await.unwrap();
}

/// Test 9: Deterministic traffic assignment (same user always gets same group)
#[tokio::test]
async fn test_deterministic_traffic_assignment() {
    let pool = create_test_pool()
        .await
        .expect("Failed to create test pool");
    let test_id = format!("test_deterministic_{}", Uuid::new_v4());

    let config = ABTestingConfig {
        test_prefix: test_id.clone(),
        ..Default::default()
    };

    let pipeline = ABTestingPipeline::new(pool.clone(), config);

    let test_state = pipeline
        .create_ab_test("DQN_v1.0.0", "DQN_v2.0.0", "ES.FUT")
        .await
        .unwrap();

    // Same user should always get same group
    let user_id = "user_123";
    let group1 = pipeline
        .assign_traffic_group(&test_state.test_id, user_id)
        .await
        .unwrap();
    let group2 = pipeline
        .assign_traffic_group(&test_state.test_id, user_id)
        .await
        .unwrap();
    let group3 = pipeline
        .assign_traffic_group(&test_state.test_id, user_id)
        .await
        .unwrap();

    assert_eq!(group1, group2);
    assert_eq!(group2, group3);

    cleanup_test_data(&pool, &test_id).await.unwrap();
}

/// Test 10: Integration with ensemble predictions
#[tokio::test]
async fn test_integration_with_ensemble_predictions() {
    let pool = create_test_pool()
        .await
        .expect("Failed to create test pool");
    let test_id = format!("test_integration_{}", Uuid::new_v4());

    let config = ABTestingConfig {
        test_prefix: test_id.clone(),
        ..Default::default()
    };

    let pipeline = ABTestingPipeline::new(pool.clone(), config);

    let test_state = pipeline
        .create_ab_test("DQN_v1.0.0", "DQN_v2.0.0", "ES.FUT")
        .await
        .unwrap();

    // Insert mock ensemble prediction
    let prediction_id = Uuid::new_v4();
    sqlx::query(
        r#"
        INSERT INTO ensemble_predictions (
            id, symbol, ensemble_action, ensemble_signal, ensemble_confidence,
            disagreement_rate, timestamp
        ) VALUES ($1, $2, $3, $4, $5, $6, NOW())
        "#,
    )
    .bind(prediction_id)
    .bind("ES.FUT")
    .bind("BUY")
    .bind(0.8)
    .bind(0.75)
    .bind(0.1)
    .execute(&pool)
    .await
    .unwrap();

    // Assign traffic group for this prediction
    let user_id = prediction_id.to_string();
    let group = pipeline
        .assign_traffic_group(&test_state.test_id, &user_id)
        .await
        .unwrap();

    // Record outcome
    pipeline
        .record_prediction_outcome(&test_state.test_id, &group, true, 150.0, 0.0015, 42)
        .await
        .unwrap();

    // Verify metrics updated
    let metrics = pipeline
        .get_ab_test_metrics(&test_state.test_id)
        .await
        .unwrap();

    if group == "control" {
        assert_eq!(metrics.control.predictions, 1);
    } else {
        assert_eq!(metrics.treatment.predictions, 1);
    }

    // Cleanup
    sqlx::query("DELETE FROM ensemble_predictions WHERE id = $1")
        .bind(prediction_id)
        .execute(&pool)
        .await
        .unwrap();

    cleanup_test_data(&pool, &test_id).await.unwrap();
}

// ============================================================================
// EXAMPLE TESTS DEMONSTRATING TEST HELPERS
// ============================================================================

/// Test 11: Example using all test helpers
#[tokio::test]
async fn test_example_using_all_helpers() {
    let pool = create_test_pool()
        .await
        .expect("Failed to create test pool");
    let test_id = format!("test_helpers_example_{}", Uuid::new_v4());

    // Use configuration factory for 50/50 split
    let config = test_helpers::create_test_ab_config(&test_id);
    assert_eq!(config.traffic_split, 0.5);
    assert_eq!(config.min_sample_size, 100);

    let pipeline = ABTestingPipeline::new(pool.clone(), config);

    let test_state = pipeline
        .create_ab_test("DQN_v1.0.0", "DQN_v2.0.0", "ES.FUT")
        .await
        .unwrap();

    // Simulate control group with baseline metrics
    for i in 0..120 {
        pipeline
            .record_prediction_outcome(
                &test_state.test_id,
                "control",
                i % 2 == 0,
                0.001 * 10000.0,
                0.001,
                50,
            )
            .await
            .unwrap();
    }

    // Simulate treatment group with better metrics
    for i in 0..120 {
        pipeline
            .record_prediction_outcome(
                &test_state.test_id,
                "treatment",
                i % 3 != 0,
                0.003 * 10000.0,
                0.003,
                45,
            )
            .await
            .unwrap();
    }

    // Make deployment decision
    let decision = pipeline
        .make_deployment_decision(&test_state.test_id)
        .await
        .unwrap();

    // Use assertion helper to validate rollout decision
    test_helpers::assert_rollout_decision(&decision, 0.0);

    cleanup_test_data(&pool, &test_id).await.unwrap();
}

/// Test 12: Using MockMetricsBuilder for fine-grained control
#[tokio::test]
async fn test_mock_metrics_builder() {
    // Use builder pattern for complex metrics
    let control_metrics = test_helpers::MockMetricsBuilder::new(1000)
        .with_win_rate(0.52)
        .with_sharpe(1.0)
        .with_latency(50.0)
        .build();

    let treatment_metrics = test_helpers::MockMetricsBuilder::new(1000)
        .with_win_rate(0.68)
        .with_sharpe(1.8)
        .with_latency(42.0)
        .with_max_drawdown(0.05)
        .build();

    // Validate metrics
    assert_eq!(control_metrics.predictions, 1000);
    assert_eq!(control_metrics.win_rate, 0.52);
    assert_eq!(control_metrics.sharpe_ratio, 1.0);
    assert_eq!(control_metrics.avg_latency_us, 50.0);

    assert_eq!(treatment_metrics.predictions, 1000);
    assert_eq!(treatment_metrics.win_rate, 0.68);
    assert_eq!(treatment_metrics.sharpe_ratio, 1.8);
    assert_eq!(treatment_metrics.avg_latency_us, 42.0);
    assert_eq!(treatment_metrics.max_drawdown, 0.05);

    // Treatment should be better
    assert!(treatment_metrics.sharpe_ratio > control_metrics.sharpe_ratio);
    assert!(treatment_metrics.win_rate > control_metrics.win_rate);
}

/// Test 13: Using generate_mock_metrics for quick setup
#[tokio::test]
async fn test_generate_mock_metrics_quick() {
    // Quick metrics generation
    let baseline = test_helpers::generate_mock_metrics(500, 0.50, 0.8);
    let improved = test_helpers::generate_mock_metrics(500, 0.65, 1.5);

    assert_eq!(baseline.predictions, 500);
    assert_eq!(baseline.win_rate, 0.50);
    assert_eq!(baseline.sharpe_ratio, 0.8);

    assert_eq!(improved.predictions, 500);
    assert_eq!(improved.win_rate, 0.65);
    assert_eq!(improved.sharpe_ratio, 1.5);

    // Improved model should have better metrics
    assert!(improved.total_pnl > baseline.total_pnl);
}

/// Test 14: Custom configuration with different traffic split
#[tokio::test]
async fn test_custom_config_70_30_split() {
    let config = test_helpers::create_custom_ab_config("test_70_30", 200, 0.7);
    assert_eq!(config.traffic_split, 0.7); // 70% control, 30% treatment
    assert_eq!(config.min_sample_size, 200);
}
