"""
Tests for CAAE Circuit Breaker.

Validates circuit breaker behavior against Experiment 5 results.
"""

import time
import pytest
from caae.circuit_breaker import CircuitBreaker, CircuitState


class TestCircuitBreaker:
    """Test suite for CircuitBreaker class."""

    @pytest.fixture
    def circuit_breaker(self):
        """Create a circuit breaker with test-friendly settings."""
        return CircuitBreaker(
            threshold_ms=5.0, cooldown_ms=100.0  # Short cooldown for testing
        )

    # =========================================================================
    # State Transition Tests
    # =========================================================================

    def test_initial_state_is_closed(self, circuit_breaker):
        """Test: Circuit breaker starts in CLOSED state."""
        assert circuit_breaker.state == CircuitState.CLOSED
        assert not circuit_breaker.is_active

    def test_trips_on_high_queue_depth(self, circuit_breaker):
        """Test: Circuit trips when queue depth exceeds threshold."""
        # First failure
        circuit_breaker.check(6.0)  # > 5.0ms threshold
        assert circuit_breaker.state == CircuitState.CLOSED

        # Second failure - should trip
        circuit_breaker.check(6.0)
        assert circuit_breaker.state == CircuitState.OPEN
        assert circuit_breaker.is_active

    def test_stays_closed_on_low_queue_depth(self, circuit_breaker):
        """Test: Circuit stays closed when queue depth is low."""
        for _ in range(10):
            result = circuit_breaker.check(2.0)  # < 5.0ms threshold
            assert result is False

        assert circuit_breaker.state == CircuitState.CLOSED

    def test_returns_true_when_active(self, circuit_breaker):
        """Test: check() returns True when circuit is active."""
        # Trip the circuit
        circuit_breaker.check(10.0)
        circuit_breaker.check(10.0)

        result = circuit_breaker.check(10.0)
        assert result is True

    # =========================================================================
    # Recovery Tests
    # =========================================================================

    def test_enters_half_open_after_cooldown(self, circuit_breaker):
        """Test: Circuit enters HALF_OPEN after cooldown period."""
        # Trip the circuit
        circuit_breaker.check(10.0)
        circuit_breaker.check(10.0)
        assert circuit_breaker.state == CircuitState.OPEN

        # Wait for cooldown
        time.sleep(0.15)  # 150ms > 100ms cooldown

        # Next check should transition to HALF_OPEN
        circuit_breaker.check(1.0)  # Low queue depth
        assert circuit_breaker.state in (CircuitState.HALF_OPEN, CircuitState.CLOSED)

    def test_recovers_after_consecutive_successes(self, circuit_breaker):
        """Test: Circuit recovers after RECOVERY_THRESHOLD successes."""
        # Trip the circuit
        circuit_breaker.check(10.0)
        circuit_breaker.check(10.0)

        # Wait for cooldown
        time.sleep(0.15)

        # Successful checks
        for _ in range(CircuitBreaker.RECOVERY_THRESHOLD + 1):
            circuit_breaker.check(1.0)

        assert circuit_breaker.state == CircuitState.CLOSED

    # =========================================================================
    # Stats Tests
    # =========================================================================

    def test_stats_track_trips(self, circuit_breaker):
        """Test: Statistics correctly track circuit trips."""
        initial_stats = circuit_breaker.get_stats()
        assert initial_stats.trips == 0

        # Trip the circuit
        circuit_breaker.check(10.0)
        circuit_breaker.check(10.0)

        stats = circuit_breaker.get_stats()
        assert stats.trips == 1

    def test_stats_track_total_checks(self, circuit_breaker):
        """Test: Statistics track total checks."""
        for _ in range(5):
            circuit_breaker.check(1.0)

        stats = circuit_breaker.get_stats()
        assert stats.total_checks == 5

    # =========================================================================
    # Force Methods Tests
    # =========================================================================

    def test_force_open(self, circuit_breaker):
        """Test: force_open() immediately trips the circuit."""
        assert circuit_breaker.state == CircuitState.CLOSED

        circuit_breaker.force_open()

        assert circuit_breaker.state == CircuitState.OPEN
        assert circuit_breaker.is_active

    def test_force_close(self, circuit_breaker):
        """Test: force_close() immediately recovers the circuit."""
        # Trip first
        circuit_breaker.force_open()
        assert circuit_breaker.is_active

        circuit_breaker.force_close()

        assert circuit_breaker.state == CircuitState.CLOSED
        assert not circuit_breaker.is_active


class TestCircuitBreakerExperiment5:
    """
    Tests validating circuit breaker against Experiment 5 results.

    From Experiment 5 Phase 4:
    - At 40 RPS with queue depth > 5ms: 170 circuit breaker triggers
    - At 10 RPS with queue depth < 5ms: 2 triggers
    """

    def test_high_load_triggers_circuit(self):
        """
        Simulate high load scenario from Experiment 5.

        At 40 RPS with queue congestion, circuit should trigger frequently.
        """
        cb = CircuitBreaker(threshold_ms=5.0, cooldown_ms=10.0)

        # Simulate 100 requests with queue depth oscillating
        triggers = 0
        for i in range(100):
            # Simulate queue depth varying between 3-8ms
            queue_depth = 5.5 + 2.5 * (i % 2)  # Alternates 5.5, 8.0
            if cb.check(queue_depth):
                triggers += 1

        # Should have triggered multiple times
        assert triggers > 10, f"Expected many triggers, got {triggers}"

    def test_low_load_minimal_triggers(self):
        """
        Simulate low load scenario from Experiment 5.

        At 10 RPS with low queue depth, circuit should rarely trigger.
        """
        cb = CircuitBreaker(threshold_ms=5.0, cooldown_ms=1000.0)

        # Simulate 100 requests with low queue depth
        triggers = 0
        for _ in range(100):
            if cb.check(2.0):  # Always below threshold
                triggers += 1

        # Should have minimal triggers
        assert triggers == 0, f"Expected no triggers, got {triggers}"


if __name__ == "__main__":
    pytest.main([__file__, "-v"])
