"""engine_v2/circuit_breaker.py — Circuit Breaker 패턴."""

from __future__ import annotations

import time
from enum import Enum


class CBState(Enum):
    CLOSED = "CLOSED"
    OPEN = "OPEN"
    HALF_OPEN = "HALF_OPEN"


class CircuitBreaker:
    """Circuit Breaker.

    확정 수치:
        fail_threshold: 3 (fluke 2회 허용)
        recovery_sec: 120 (600초 타임아웃의 1/5)
        half_max_calls: 1 (복구 확인 최소)
    """

    def __init__(
        self,
        fail_threshold: int = 3,
        recovery_sec: int = 120,
        half_max_calls: int = 1,
    ) -> None:
        self._fail_threshold = fail_threshold
        self._recovery_sec = recovery_sec
        self._half_max_calls = half_max_calls
        self._state = CBState.CLOSED
        self._fail_count = 0
        self._last_fail_time: float = 0.0
        self._half_open_calls = 0

    @property
    def state(self) -> CBState:
        self._check_recovery()
        return self._state

    def allow_request(self) -> bool:
        """요청 허용 여부."""
        self._check_recovery()
        if self._state == CBState.CLOSED:
            return True
        if self._state == CBState.HALF_OPEN:
            if self._half_open_calls < self._half_max_calls:
                self._half_open_calls += 1
                return True
            return False
        return False  # OPEN

    def record_success(self) -> None:
        """성공 기록."""
        if self._state == CBState.HALF_OPEN:
            self._state = CBState.CLOSED
            self._fail_count = 0
            self._half_open_calls = 0

    def record_failure(self) -> None:
        """실패 기록."""
        self._fail_count += 1
        self._last_fail_time = time.monotonic()
        if self._fail_count >= self._fail_threshold:
            self._state = CBState.OPEN

    def _check_recovery(self) -> None:
        """OPEN → HALF_OPEN 전환 체크."""
        if self._state == CBState.OPEN:
            elapsed = time.monotonic() - self._last_fail_time
            if elapsed >= self._recovery_sec:
                self._state = CBState.HALF_OPEN
                self._half_open_calls = 0

    def reset(self) -> None:
        """상태 초기화."""
        self._state = CBState.CLOSED
        self._fail_count = 0
        self._last_fail_time = 0.0
        self._half_open_calls = 0
