"""Tests for circuit breaker and fallback logic."""

import time
import pytest
from unittest.mock import patch

from backend.proxy.fallback import CircuitBreaker, CircuitState, _CIRCUIT_WINDOW, _CIRCUIT_HALF_OPEN_AFTER


class TestCircuitState:
    def test_initial_state(self):
        state = CircuitState()
        assert state.failures == []
        assert state.opened_at is None

    def test_record_failure(self):
        state = CircuitState()
        state.record_failure()
        assert len(state.failures) == 1

    def test_record_success_clears_failures(self):
        state = CircuitState()
        state.record_failure()
        state.record_failure()
        state.record_success()
        assert state.failures == []
        assert state.opened_at is None

    def test_is_open_threshold(self):
        state = CircuitState()
        # Below threshold
        state.record_failure()
        state.record_failure()
        assert state.is_open(threshold=3) is False
        
        # At threshold
        state.record_failure()
        assert state.is_open(threshold=3) is True

    def test_failures_expire_after_window(self):
        state = CircuitState()
        # Add old failure
        old_time = time.time() - _CIRCUIT_WINDOW - 10
        state.failures = [old_time, old_time, old_time]
        
        # Old failures should be ignored
        assert state.is_open(threshold=3) is False

    def test_half_open_after_cooldown(self):
        state = CircuitState()
        state.record_failure()
        state.record_failure()
        state.record_failure()
        
        # Circuit is open
        assert state.is_open(threshold=3) is True
        
        # Simulate time passing beyond half-open period
        state.opened_at = time.time() - _CIRCUIT_HALF_OPEN_AFTER - 1
        
        # Should allow a probe request (half-open)
        assert state.is_open(threshold=3) is False


class TestCircuitBreaker:
    def test_initial_availability(self):
        cb = CircuitBreaker(threshold=3)
        assert cb.is_available("model-a") is True
        assert cb.is_available("model-b") is True

    def test_circuit_opens_after_failures(self):
        cb = CircuitBreaker(threshold=3)
        
        cb.record_failure("model-a")
        cb.record_failure("model-a")
        assert cb.is_available("model-a") is True
        
        cb.record_failure("model-a")
        assert cb.is_available("model-a") is False

    def test_success_resets_circuit(self):
        cb = CircuitBreaker(threshold=2)
        
        cb.record_failure("model-a")
        cb.record_failure("model-a")
        assert cb.is_available("model-a") is False
        
        cb.record_success("model-a")
        assert cb.is_available("model-a") is True

    def test_circuits_are_per_model(self):
        cb = CircuitBreaker(threshold=2)
        
        cb.record_failure("model-a")
        cb.record_failure("model-a")
        
        assert cb.is_available("model-a") is False
        assert cb.is_available("model-b") is True

    def test_get_status(self):
        cb = CircuitBreaker(threshold=2)
        
        cb.record_failure("model-a")
        cb.record_failure("model-a")
        cb.record_failure("model-b")
        
        status = cb.get_status()
        assert status["model-a"] == "open"
        assert status["model-b"] == "closed"
