#![allow(
    unused_variables,
    clippy::assertions_on_constants,
    clippy::len_zero
)]
//! Unit Tests for ML Metrics Module
//!
//! This test suite validates Prometheus metrics registration and helper functions
//! for ML model performance monitoring.

use prometheus::core::Collector;
use trading_service::ml_metrics::*;

#[test]
fn test_ml_inference_latency_metric_exists() {
    // Verify histogram is registered with correct labels
    let metric = &*ML_INFERENCE_LATENCY_US;
    let desc = metric.desc();

    assert!(
        desc.len() > 0,
        "ML inference latency histogram should have descriptors"
    );

    // Test that we can observe values
    metric.with_label_values(&["DQN"]).observe(100.0);
    metric.with_label_values(&["PPO"]).observe(250.0);
    metric.with_label_values(&["TFT"]).observe(500.0);

    // Verify histogram buckets are defined (should have 7 buckets)
    let collected = metric.collect();
    assert!(!collected.is_empty(), "Should have collected metrics");
}

#[test]
fn test_ml_model_accuracy_metric_exists() {
    let metric = &*ML_MODEL_ACCURACY;
    let desc = metric.desc();

    assert!(
        desc.len() > 0,
        "ML model accuracy gauge should have descriptors"
    );

    // Test setting accuracy values (0-100%)
    metric.with_label_values(&["DQN"]).set(87.5);
    metric.with_label_values(&["PPO"]).set(92.3);
    metric.with_label_values(&["TFT"]).set(89.1);

    let collected = metric.collect();
    assert!(!collected.is_empty(), "Should have collected metrics");
}

#[test]
fn test_ml_model_health_metric_exists() {
    let metric = &*ML_MODEL_HEALTH;
    let desc = metric.desc();

    assert!(
        desc.len() > 0,
        "ML model health gauge should have descriptors"
    );

    // Test health status values (0=Healthy, 1=Degraded, 2=Unhealthy, 3=Failed, 4=Offline)
    metric.with_label_values(&["DQN"]).set(0.0); // Healthy
    metric.with_label_values(&["PPO"]).set(1.0); // Degraded
    metric.with_label_values(&["TFT"]).set(2.0); // Unhealthy

    let collected = metric.collect();
    assert!(!collected.is_empty(), "Should have collected metrics");
}

#[test]
fn test_ml_fallback_counter_exists() {
    let metric = &*ML_FALLBACK_TOTAL;
    let desc = metric.desc();

    assert!(
        desc.len() > 0,
        "ML fallback counter should have descriptors"
    );

    // Test fallback events with 3 labels (from_model, to_model, reason)
    metric
        .with_label_values(&["DQN", "PPO", "high_latency"])
        .inc();
    metric
        .with_label_values(&["PPO", "TFT", "prediction_error"])
        .inc();
    metric
        .with_label_values(&["TFT", "DQN", "model_failure"])
        .inc();

    let collected = metric.collect();
    assert!(!collected.is_empty(), "Should have collected metrics");
}

#[test]
fn test_ml_predictions_counter_exists() {
    let metric = &*ML_PREDICTIONS_TOTAL;
    let desc = metric.desc();

    assert!(
        desc.len() > 0,
        "ML predictions counter should have descriptors"
    );

    // Test prediction types (buy/sell/hold)
    metric.with_label_values(&["DQN", "buy"]).inc();
    metric.with_label_values(&["DQN", "sell"]).inc();
    metric.with_label_values(&["DQN", "hold"]).inc();
    metric.with_label_values(&["PPO", "buy"]).inc_by(5.0);

    let collected = metric.collect();
    assert!(!collected.is_empty(), "Should have collected metrics");
}

#[test]
fn test_ml_prediction_errors_counter_exists() {
    let metric = &*ML_PREDICTION_ERRORS_TOTAL;
    let desc = metric.desc();

    assert!(
        desc.len() > 0,
        "ML prediction errors counter should have descriptors"
    );

    // Test error types
    metric
        .with_label_values(&["DQN", "inference_timeout"])
        .inc();
    metric.with_label_values(&["PPO", "invalid_input"]).inc();
    metric.with_label_values(&["TFT", "model_not_loaded"]).inc();

    let collected = metric.collect();
    assert!(!collected.is_empty(), "Should have collected metrics");
}

#[test]
fn test_ml_alerts_counter_exists() {
    let metric = &*ML_ALERTS_TOTAL;
    let desc = metric.desc();

    assert!(desc.len() > 0, "ML alerts counter should have descriptors");

    // Test alerts with 3 labels (model_id, alert_type, severity)
    metric
        .with_label_values(&["DQN", "high_latency", "warning"])
        .inc();
    metric
        .with_label_values(&["PPO", "low_accuracy", "critical"])
        .inc();
    metric
        .with_label_values(&["TFT", "model_drift", "emergency"])
        .inc();

    let collected = metric.collect();
    assert!(!collected.is_empty(), "Should have collected metrics");
}

#[test]
fn test_ml_model_drift_score_metric_exists() {
    let metric = &*ML_MODEL_DRIFT_SCORE;
    let desc = metric.desc();

    assert!(
        desc.len() > 0,
        "ML model drift score gauge should have descriptors"
    );

    // Test drift scores (percentage change)
    metric.with_label_values(&["DQN"]).set(2.5); // 2.5% drift
    metric.with_label_values(&["PPO"]).set(5.1); // 5.1% drift
    metric.with_label_values(&["TFT"]).set(10.8); // 10.8% drift

    let collected = metric.collect();
    assert!(!collected.is_empty(), "Should have collected metrics");
}

#[test]
fn test_ml_model_confidence_metric_exists() {
    let metric = &*ML_MODEL_CONFIDENCE;
    let desc = metric.desc();

    assert!(
        desc.len() > 0,
        "ML model confidence gauge should have descriptors"
    );

    // Test confidence scores (0-1)
    metric.with_label_values(&["DQN"]).set(0.85);
    metric.with_label_values(&["PPO"]).set(0.92);
    metric.with_label_values(&["TFT"]).set(0.78);

    let collected = metric.collect();
    assert!(!collected.is_empty(), "Should have collected metrics");
}

#[test]
fn test_ml_model_memory_metric_exists() {
    let metric = &*ML_MODEL_MEMORY_MB;
    let desc = metric.desc();

    assert!(
        desc.len() > 0,
        "ML model memory gauge should have descriptors"
    );

    // Test memory usage in MB
    metric.with_label_values(&["DQN"]).set(6.0); // 6 MB
    metric.with_label_values(&["PPO"]).set(145.0); // 145 MB
    metric.with_label_values(&["TFT"]).set(125.0); // 125 MB
    metric.with_label_values(&["MAMBA2"]).set(164.0); // 164 MB

    let collected = metric.collect();
    assert!(!collected.is_empty(), "Should have collected metrics");
}

#[test]
fn test_ml_model_cpu_metric_exists() {
    let metric = &*ML_MODEL_CPU_PERCENT;
    let desc = metric.desc();

    assert!(desc.len() > 0, "ML model CPU gauge should have descriptors");

    // Test CPU utilization (0-100%)
    metric.with_label_values(&["DQN"]).set(15.5);
    metric.with_label_values(&["PPO"]).set(28.3);
    metric.with_label_values(&["TFT"]).set(45.7);

    let collected = metric.collect();
    assert!(!collected.is_empty(), "Should have collected metrics");
}

#[test]
fn test_ml_circuit_breaker_transitions_metric_exists() {
    let metric = &*ML_CIRCUIT_BREAKER_TRANSITIONS;
    let desc = metric.desc();

    assert!(
        desc.len() > 0,
        "ML circuit breaker transitions counter should have descriptors"
    );

    // Test state transitions (from_state, to_state)
    metric.with_label_values(&["DQN", "closed", "open"]).inc();
    metric
        .with_label_values(&["PPO", "open", "half_open"])
        .inc();
    metric
        .with_label_values(&["TFT", "half_open", "closed"])
        .inc();

    let collected = metric.collect();
    assert!(!collected.is_empty(), "Should have collected metrics");
}

#[test]
fn test_multiple_labels_per_metric() {
    // Verify we can track multiple models independently
    ML_MODEL_ACCURACY.with_label_values(&["model_1"]).set(85.0);
    ML_MODEL_ACCURACY.with_label_values(&["model_2"]).set(90.0);
    ML_MODEL_ACCURACY.with_label_values(&["model_3"]).set(87.5);

    ML_INFERENCE_LATENCY_US
        .with_label_values(&["model_1"])
        .observe(100.0);
    ML_INFERENCE_LATENCY_US
        .with_label_values(&["model_2"])
        .observe(200.0);
    ML_INFERENCE_LATENCY_US
        .with_label_values(&["model_3"])
        .observe(150.0);

    // Each model should have independent metrics
    let accuracy_collected = ML_MODEL_ACCURACY.collect();
    let latency_collected = ML_INFERENCE_LATENCY_US.collect();

    assert!(!accuracy_collected.is_empty());
    assert!(!latency_collected.is_empty());
}

#[test]
fn test_metric_increments() {
    // Test that counters can be incremented multiple times
    let initial_count = ML_PREDICTIONS_TOTAL
        .with_label_values(&["test_model", "buy"])
        .get();

    ML_PREDICTIONS_TOTAL
        .with_label_values(&["test_model", "buy"])
        .inc();
    ML_PREDICTIONS_TOTAL
        .with_label_values(&["test_model", "buy"])
        .inc();
    ML_PREDICTIONS_TOTAL
        .with_label_values(&["test_model", "buy"])
        .inc_by(3.0);

    // Counter should have increased (we can't easily check exact value due to other tests)
    let collected = ML_PREDICTIONS_TOTAL.collect();
    assert!(!collected.is_empty());
}

#[test]
fn test_histogram_buckets() {
    // Verify histogram has correct bucket configuration
    // Buckets: [10.0, 50.0, 100.0, 500.0, 1000.0, 5000.0, 10000.0]

    // Test values in different buckets
    ML_INFERENCE_LATENCY_US
        .with_label_values(&["bucket_test"])
        .observe(5.0); // < 10
    ML_INFERENCE_LATENCY_US
        .with_label_values(&["bucket_test"])
        .observe(75.0); // 50-100
    ML_INFERENCE_LATENCY_US
        .with_label_values(&["bucket_test"])
        .observe(750.0); // 500-1000
    ML_INFERENCE_LATENCY_US
        .with_label_values(&["bucket_test"])
        .observe(5500.0); // 5000-10000
    ML_INFERENCE_LATENCY_US
        .with_label_values(&["bucket_test"])
        .observe(15000.0); // > 10000

    let collected = ML_INFERENCE_LATENCY_US.collect();
    assert!(
        !collected.is_empty(),
        "Should have collected histogram metrics"
    );
}

#[test]
fn test_gauge_set_operations() {
    // Test that gauges can be set to arbitrary values
    ML_MODEL_HEALTH.with_label_values(&["gauge_test"]).set(0.0);
    ML_MODEL_HEALTH.with_label_values(&["gauge_test"]).set(1.0);
    ML_MODEL_HEALTH.with_label_values(&["gauge_test"]).set(2.0);
    ML_MODEL_HEALTH.with_label_values(&["gauge_test"]).set(3.0);
    ML_MODEL_HEALTH.with_label_values(&["gauge_test"]).set(4.0);

    // Verify latest value is set (should be 4.0)
    let collected = ML_MODEL_HEALTH.collect();
    assert!(!collected.is_empty());
}

#[test]
fn test_all_metrics_are_registered() {
    // Verify all static metrics are successfully registered (no panics during initialization)
    let _ = &*ML_INFERENCE_LATENCY_US;
    let _ = &*ML_MODEL_ACCURACY;
    let _ = &*ML_MODEL_HEALTH;
    let _ = &*ML_FALLBACK_TOTAL;
    let _ = &*ML_PREDICTIONS_TOTAL;
    let _ = &*ML_PREDICTION_ERRORS_TOTAL;
    let _ = &*ML_ALERTS_TOTAL;
    let _ = &*ML_MODEL_DRIFT_SCORE;
    let _ = &*ML_MODEL_CONFIDENCE;
    let _ = &*ML_MODEL_MEMORY_MB;
    let _ = &*ML_MODEL_CPU_PERCENT;
    let _ = &*ML_CIRCUIT_BREAKER_TRANSITIONS;

    // If we got here without panicking, all metrics are registered successfully
    assert!(true, "All metrics initialized successfully");
}
