//! Integration tests for API Gateway metrics system
//!
//! Verifies that all metrics are correctly registered and recorded

use api::metrics::{AuthMetrics, ConfigMetrics, GatewayMetrics, ProxyMetrics};
use prometheus::Registry;
use std::sync::Arc;

#[test]
fn test_gateway_metrics_initialization() {
    let metrics = GatewayMetrics::new().expect("Failed to create metrics");

    // Verify all subsystems initialized
    assert!(Arc::strong_count(&metrics.auth) >= 1);
    assert!(Arc::strong_count(&metrics.proxy) >= 1);
    assert!(Arc::strong_count(&metrics.config) >= 1);
    assert!(Arc::strong_count(&metrics.registry) >= 1);
}

#[test]
fn test_auth_metrics_registration() {
    let registry = Registry::new();
    let auth_metrics = AuthMetrics::new(&registry).expect("Failed to create auth metrics");

    // Record some events
    auth_metrics.auth_requests_total.inc();
    auth_metrics.record_success(5.5);
    auth_metrics.record_failure("expired_jwt", Some("test_user"));
    auth_metrics.record_user_request("test_user");
    auth_metrics.record_rate_limit("rate_limited_user");

    // Verify metrics can be gathered
    let metrics = registry.gather();
    assert!(!metrics.is_empty(), "No metrics gathered");

    // Check specific metrics exist
    let metric_names: Vec<String> = metrics.iter().map(|m| m.name().to_string()).collect();

    assert!(
        metric_names.contains(&"api_auth_requests_total".to_string()),
        "Missing auth_requests_total metric"
    );
    assert!(
        metric_names.contains(&"api_auth_requests_success".to_string()),
        "Missing auth_requests_success metric"
    );
    assert!(
        metric_names.contains(&"api_auth_sla_met".to_string()),
        "Missing auth_sla_met metric"
    );
}

#[test]
fn test_proxy_metrics_registration() {
    let registry = Registry::new();
    let proxy_metrics = ProxyMetrics::new(&registry).expect("Failed to create proxy metrics");

    // Record backend events
    proxy_metrics.record_backend_success("trading", "ExecuteTrade", 15.5);
    proxy_metrics.record_backend_failure("backtesting", "timeout");
    proxy_metrics.update_circuit_breaker_state("trading", 0); // closed
    proxy_metrics.update_health_status("trading", true);
    proxy_metrics.update_connection_pool("trading", 10, 5, 50);

    // Verify metrics can be gathered
    let metrics = registry.gather();
    assert!(!metrics.is_empty(), "No metrics gathered");

    let metric_names: Vec<String> = metrics.iter().map(|m| m.name().to_string()).collect();

    assert!(
        metric_names.contains(&"api_backend_requests_total".to_string()),
        "Missing backend_requests_total metric"
    );
    assert!(
        metric_names.contains(&"api_circuit_breaker_state".to_string()),
        "Missing circuit_breaker_state metric"
    );
    assert!(
        metric_names.contains(&"api_health_status".to_string()),
        "Missing health_status metric"
    );
}

#[test]
fn test_config_metrics_registration() {
    let registry = Registry::new();
    let config_metrics = ConfigMetrics::new(&registry).expect("Failed to create config metrics");

    // Record config events
    config_metrics.record_notify_event(true);
    config_metrics.record_config_reload("auth", 25.0);
    config_metrics.update_listener_status(true);
    config_metrics.record_reconnection();

    // Verify metrics can be gathered
    let metrics = registry.gather();
    assert!(!metrics.is_empty(), "No metrics gathered");

    let metric_names: Vec<String> = metrics.iter().map(|m| m.name().to_string()).collect();

    assert!(
        metric_names.contains(&"api_notify_events_total".to_string()),
        "Missing notify_events_total metric"
    );
    assert!(
        metric_names.contains(&"api_config_reload_duration_milliseconds".to_string()),
        "Missing config_reload_duration metric"
    );
    assert!(
        metric_names.contains(&"api_notify_listener_connected".to_string()),
        "Missing notify_listener_connected metric"
    );
}

#[test]
fn test_auth_sla_tracking() {
    let registry = Registry::new();
    let auth_metrics = AuthMetrics::new(&registry).expect("Failed to create auth metrics");

    // Record requests meeting SLA (<10μs)
    auth_metrics.record_success(5.0);
    auth_metrics.record_success(8.5);
    auth_metrics.record_success(9.9);

    // Record requests exceeding SLA (>10μs)
    auth_metrics.record_success(15.0);
    auth_metrics.record_success(25.5);

    let metrics = registry.gather();
    let sla_met = metrics
        .iter()
        .find(|m| m.name() == "api_auth_sla_met")
        .expect("SLA met metric not found");

    let sla_exceeded = metrics
        .iter()
        .find(|m| m.name() == "api_auth_sla_exceeded")
        .expect("SLA exceeded metric not found");

    // Verify counts (3 met, 2 exceeded)
    assert_eq!(sla_met.metric[0].counter.value, Some(3.0));
    assert_eq!(sla_exceeded.metric[0].counter.value, Some(2.0));
}

#[test]
fn test_cache_hit_rate_tracking() {
    let registry = Registry::new();
    let auth_metrics = AuthMetrics::new(&registry).expect("Failed to create auth metrics");

    // Simulate 95% cache hit rate
    auth_metrics.jwt_cache_hits.inc_by(95.0);
    auth_metrics.jwt_cache_misses.inc_by(5.0);
    auth_metrics.rbac_cache_hits.inc_by(98.0);
    auth_metrics.rbac_cache_misses.inc_by(2.0);

    let metrics = registry.gather();

    let jwt_hits = metrics
        .iter()
        .find(|m| m.name() == "api_jwt_cache_hits")
        .expect("JWT cache hits not found");

    let jwt_misses = metrics
        .iter()
        .find(|m| m.name() == "api_jwt_cache_misses")
        .expect("JWT cache misses not found");

    assert_eq!(jwt_hits.metric[0].counter.value, Some(95.0));
    assert_eq!(jwt_misses.metric[0].counter.value, Some(5.0));

    // Verify hit rate calculation
    let hit_rate = 100.0 * 95.0 / (95.0 + 5.0);
    assert_eq!(hit_rate, 95.0, "JWT cache hit rate should be 95%");
}

#[test]
fn test_circuit_breaker_state_transitions() {
    let registry = Registry::new();
    let proxy_metrics = ProxyMetrics::new(&registry).expect("Failed to create proxy metrics");

    // Initial state: closed (0)
    proxy_metrics.update_circuit_breaker_state("trading", 0);

    // Trip circuit breaker (open = 2)
    proxy_metrics.record_circuit_breaker_trip("trading");

    let metrics = registry.gather();
    let cb_state = metrics
        .iter()
        .find(|m| m.name() == "api_circuit_breaker_state")
        .expect("Circuit breaker state not found");

    // Verify state is open (2)
    let trading_state = cb_state
        .metric
        .iter()
        .find(|m| {
            m.label
                .iter()
                .any(|l| l.name() == "service" && l.value() == "trading")
        })
        .expect("Trading service state not found");

    assert_eq!(trading_state.gauge.value, Some(2.0));

    // Recover circuit breaker (closed = 0)
    proxy_metrics.record_circuit_breaker_recovery("trading");

    let metrics = registry.gather();
    let cb_state = metrics
        .iter()
        .find(|m| m.name() == "api_circuit_breaker_state")
        .expect("Circuit breaker state not found");

    let trading_state = cb_state
        .metric
        .iter()
        .find(|m| {
            m.label
                .iter()
                .any(|l| l.name() == "service" && l.value() == "trading")
        })
        .expect("Trading service state not found");

    assert_eq!(trading_state.gauge.value, Some(0.0));
}

#[test]
fn test_per_user_metrics() {
    let registry = Registry::new();
    let auth_metrics = AuthMetrics::new(&registry).expect("Failed to create auth metrics");

    // Record requests for different users
    auth_metrics.record_user_request("user_1");
    auth_metrics.record_user_request("user_1");
    auth_metrics.record_user_request("user_2");

    // Record failures per user
    auth_metrics.record_failure("expired_jwt", Some("user_1"));
    auth_metrics.record_failure("permission_denied", Some("user_2"));

    // Record rate limits
    auth_metrics.record_rate_limit("user_3");
    auth_metrics.record_rate_limit("user_3");

    let metrics = registry.gather();

    // Verify per-user request counts
    let requests_by_user = metrics
        .iter()
        .find(|m| m.name() == "api_requests_by_user")
        .expect("Requests by user not found");

    assert_eq!(requests_by_user.metric.len(), 2); // user_1 and user_2

    // Verify per-user rate limits
    let rate_limits_by_user = metrics
        .iter()
        .find(|m| m.name() == "api_rate_limits_by_user")
        .expect("Rate limits by user not found");

    let user_3_limits = rate_limits_by_user
        .metric
        .iter()
        .find(|m| {
            m.label
                .iter()
                .any(|l| l.name() == "user_id" && l.value() == "user_3")
        })
        .expect("User 3 rate limits not found");

    assert_eq!(user_3_limits.counter.value, Some(2.0));
}

#[test]
fn test_metrics_exporter_format() {
    use api::metrics::PrometheusExporter;

    let metrics = GatewayMetrics::new().expect("Failed to create metrics");

    // Record some events
    metrics.auth.record_success(5.0);
    metrics
        .proxy
        .record_backend_success("trading", "ExecuteTrade", 15.0);
    metrics.config.record_notify_event(true);

    let exporter = PrometheusExporter::new(metrics.registry());
    let output = exporter.gather_metrics().expect("Failed to gather metrics");

    // Verify Prometheus text format
    assert!(output.contains("# HELP"));
    assert!(output.contains("# TYPE"));
    assert!(output.contains("api_auth_requests_success"));
    assert!(output.contains("api_backend_requests_success"));
    assert!(output.contains("api_notify_events_success"));
}
