//! Circuit breaker tests
//!
//! Tests for the circuit breaker pattern implementation.

use crate::resilience::circuit_breaker::{CircuitBreaker, CircuitBreakerConfig, CircuitBreakerState};
use std::time::Duration;
use tokio::time::sleep;

#[tokio::test]
async fn test_circuit_breaker_starts_closed() {
    let config = CircuitBreakerConfig::default();
    let cb = CircuitBreaker::new("test", config);

    let state = cb.state().await;
    assert_eq!(state, CircuitBreakerState::Closed);
}

#[tokio::test]
async fn test_circuit_breaker_opens_after_threshold() {
    let config = CircuitBreakerConfig {
        failure_threshold: 3,
        timeout: Duration::from_millis(100),
        success_threshold: 2,
    };
    let cb = CircuitBreaker::new("test", config);

    // Record 3 failures
    cb.record_failure().await;
    cb.record_failure().await;
    cb.record_failure().await;

    let state = cb.state().await;
    assert_eq!(state, CircuitBreakerState::Open);
}

#[tokio::test]
async fn test_circuit_breaker_transitions_to_half_open() {
    let config = CircuitBreakerConfig {
        failure_threshold: 2,
        timeout: Duration::from_millis(50),
        success_threshold: 2,
    };
    let cb = CircuitBreaker::new("test", config);

    // Record failures to open circuit
    cb.record_failure().await;
    cb.record_failure().await;

    assert_eq!(cb.state().await, CircuitBreakerState::Open);

    // Wait for timeout
    sleep(Duration::from_millis(60)).await;

    // Check should transition to HalfOpen
    let can_execute = cb.can_execute().await;
    assert!(can_execute, "Should allow execution in HalfOpen state");

    let state = cb.state().await;
    assert_eq!(state, CircuitBreakerState::HalfOpen);
}

#[tokio::test]
async fn test_circuit_breaker_closes_after_successes() {
    let config = CircuitBreakerConfig {
        failure_threshold: 2,
        timeout: Duration::from_millis(50),
        success_threshold: 2,
    };
    let cb = CircuitBreaker::new("test", config);

    // Open the circuit
    cb.record_failure().await;
    cb.record_failure().await;
    assert_eq!(cb.state().await, CircuitBreakerState::Open);

    // Wait for timeout to transition to HalfOpen
    sleep(Duration::from_millis(60)).await;
    let _ = cb.can_execute().await; // Trigger HalfOpen transition

    // Record successes to close circuit
    cb.record_success().await;
    cb.record_success().await;

    let state = cb.state().await;
    assert_eq!(state, CircuitBreakerState::Closed);
}

#[tokio::test]
async fn test_circuit_breaker_reopens_on_half_open_failure() {
    let config = CircuitBreakerConfig {
        failure_threshold: 2,
        timeout: Duration::from_millis(50),
        success_threshold: 2,
    };
    let cb = CircuitBreaker::new("test", config);

    // Open the circuit
    cb.record_failure().await;
    cb.record_failure().await;

    // Wait for HalfOpen
    sleep(Duration::from_millis(60)).await;
    let _ = cb.can_execute().await;

    // Record a failure in HalfOpen - should reopen
    cb.record_failure().await;

    let state = cb.state().await;
    assert_eq!(state, CircuitBreakerState::Open);
}

#[tokio::test]
async fn test_circuit_breaker_rejects_when_open() {
    let config = CircuitBreakerConfig {
        failure_threshold: 2,
        timeout: Duration::from_millis(1000),
        success_threshold: 2,
    };
    let cb = CircuitBreaker::new("test", config);

    // Open the circuit
    cb.record_failure().await;
    cb.record_failure().await;

    // Should reject execution
    let can_execute = cb.can_execute().await;
    assert!(!can_execute, "Circuit breaker should reject when Open");
}

#[tokio::test]
async fn test_circuit_breaker_success_resets_counter() {
    let config = CircuitBreakerConfig {
        failure_threshold: 3,
        timeout: Duration::from_millis(100),
        success_threshold: 2,
    };
    let cb = CircuitBreaker::new("test", config);

    // Record some failures
    cb.record_failure().await;
    cb.record_failure().await;

    // Record success - should reset counter
    cb.record_success().await;

    // Record one more failure - should not open (counter was reset)
    cb.record_failure().await;

    let state = cb.state().await;
    assert_eq!(state, CircuitBreakerState::Closed);
}

#[tokio::test]
async fn test_circuit_breaker_metrics() {
    let config = CircuitBreakerConfig::default();
    let cb = CircuitBreaker::new("test", config);

    cb.record_success().await;
    cb.record_success().await;
    cb.record_failure().await;

    let metrics = cb.metrics().await;
    assert_eq!(metrics.total_successes, 2);
    assert_eq!(metrics.total_failures, 1);
    assert_eq!(metrics.total_requests, 3);
}

#[tokio::test]
async fn test_multiple_circuit_breakers_independent() {
    let config = CircuitBreakerConfig {
        failure_threshold: 2,
        timeout: Duration::from_millis(100),
        success_threshold: 2,
    };

    let cb1 = CircuitBreaker::new("service1", config.clone());
    let cb2 = CircuitBreaker::new("service2", config);

    // Open first circuit
    cb1.record_failure().await;
    cb1.record_failure().await;

    // Second circuit should still be closed
    assert_eq!(cb1.state().await, CircuitBreakerState::Open);
    assert_eq!(cb2.state().await, CircuitBreakerState::Closed);
}
