"""Tests for CircuitBreaker."""

import time
from agentic_security.executor.circuit_breaker import CircuitBreaker


class TestCircuitBreaker:
    """Test CircuitBreaker functionality."""

    def test_initialization(self):
        """Test circuit breaker initialization."""
        breaker = CircuitBreaker(failure_threshold=0.5, recovery_timeout=30)

        assert breaker.failure_threshold == 0.5
        assert breaker.recovery_timeout == 30
        assert breaker.state == "closed"
        assert breaker.failures == 0
        assert breaker.successes == 0

    def test_record_success(self):
        """Test recording successful requests."""
        breaker = CircuitBreaker()

        breaker.record_success()
        assert breaker.successes == 1
        assert breaker.failures == 0
        assert breaker.state == "closed"

    def test_record_failure(self):
        """Test recording failed requests."""
        breaker = CircuitBreaker()

        breaker.record_failure()
        assert breaker.failures == 1
        assert breaker.successes == 0
        assert breaker.last_failure_time is not None

    def test_circuit_opens_on_failure_threshold(self):
        """Test that circuit opens when failure threshold is exceeded."""
        breaker = CircuitBreaker(failure_threshold=0.5, recovery_timeout=30)

        # Record 10 requests: 6 failures, 4 successes (60% failure rate)
        for _ in range(4):
            breaker.record_success()
        for _ in range(6):
            breaker.record_failure()

        # Circuit should be open (60% > 50% threshold)
        assert breaker.state == "open"
        assert breaker.is_open() is True

    def test_circuit_stays_closed_below_threshold(self):
        """Test that circuit stays closed when below threshold."""
        breaker = CircuitBreaker(failure_threshold=0.5, recovery_timeout=30)

        # Record 10 requests: 4 failures, 6 successes (40% failure rate)
        for _ in range(6):
            breaker.record_success()
        for _ in range(4):
            breaker.record_failure()

        # Circuit should stay closed (40% < 50% threshold)
        assert breaker.state == "closed"
        assert breaker.is_open() is False

    def test_minimum_sample_size_required(self):
        """Test that minimum sample size is required before opening."""
        breaker = CircuitBreaker(failure_threshold=0.5)

        # Only 5 failures (below minimum of 10 total requests)
        for _ in range(5):
            breaker.record_failure()

        # Circuit should stay closed (not enough samples)
        assert breaker.state == "closed"
        assert breaker.is_open() is False

    def test_circuit_recovery_after_timeout(self):
        """Test that circuit enters half-open state after recovery timeout."""
        breaker = CircuitBreaker(failure_threshold=0.5, recovery_timeout=1)

        # Open the circuit
        for _ in range(4):
            breaker.record_success()
        for _ in range(6):
            breaker.record_failure()

        assert breaker.state == "open"

        # Wait for recovery timeout
        time.sleep(1.1)

        # Check if circuit moves to half-open
        is_open = breaker.is_open()
        assert is_open is False
        assert breaker.state == "half_open"

    def test_half_open_to_closed_on_successes(self):
        """Test that circuit closes from half-open after enough successes."""
        breaker = CircuitBreaker(failure_threshold=0.5, recovery_timeout=1)

        # Open the circuit
        for _ in range(4):
            breaker.record_success()
        for _ in range(6):
            breaker.record_failure()

        # Wait for recovery
        time.sleep(1.1)
        breaker.is_open()  # Triggers transition to half-open

        assert breaker.state == "half_open"

        # Record 3 successes
        breaker.record_success()
        breaker.record_success()
        breaker.record_success()

        # Should transition to closed
        assert breaker.state == "closed"

    def test_get_state(self):
        """Test get_state method."""
        breaker = CircuitBreaker()

        assert breaker.get_state() == "closed"

        # Open the circuit
        for _ in range(10):
            breaker.record_failure()

        assert breaker.get_state() == "open"

    def test_get_failure_rate(self):
        """Test get_failure_rate method."""
        breaker = CircuitBreaker()

        # No requests
        assert breaker.get_failure_rate() == 0.0

        # 3 failures, 7 successes (30% failure rate)
        for _ in range(7):
            breaker.record_success()
        for _ in range(3):
            breaker.record_failure()

        assert breaker.get_failure_rate() == 0.3

    def test_reset(self):
        """Test reset method."""
        breaker = CircuitBreaker()

        # Record some activity
        breaker.record_success()
        breaker.record_failure()
        for _ in range(10):
            breaker.record_failure()

        # Reset
        breaker.reset()

        # Should be back to initial state
        assert breaker.state == "closed"
        assert breaker.failures == 0
        assert breaker.successes == 0
        assert breaker.last_failure_time is None

    def test_exact_failure_threshold(self):
        """Test behavior at exact failure threshold."""
        breaker = CircuitBreaker(failure_threshold=0.5)

        # Exactly 50% failure rate (5 failures, 5 successes)
        for _ in range(5):
            breaker.record_success()
        for _ in range(5):
            breaker.record_failure()

        # Should be open (>= threshold)
        assert breaker.state == "open"

    def test_high_failure_threshold(self):
        """Test with high failure threshold."""
        breaker = CircuitBreaker(failure_threshold=0.9)

        # 80% failure rate (8 failures, 2 successes)
        for _ in range(2):
            breaker.record_success()
        for _ in range(8):
            breaker.record_failure()

        # Should stay closed (80% < 90%)
        assert breaker.state == "closed"

    def test_zero_recovery_timeout(self):
        """Test with zero recovery timeout."""
        breaker = CircuitBreaker(failure_threshold=0.5, recovery_timeout=0)

        # Open the circuit
        for _ in range(10):
            breaker.record_failure()

        assert breaker.state == "open"

        # Should immediately allow recovery attempt
        time.sleep(0.01)
        is_open = breaker.is_open()

        assert is_open is False
        assert breaker.state == "half_open"
