"""熔断器模块"""
import logging
import threading
import time
from enum import Enum
from typing import Callable, Any

logger = logging.getLogger(__name__)


class CircuitState(Enum):
    """熔断器状态"""
    CLOSED = "closed"      # 关闭状态，正常工作
    OPEN = "open"          # 打开状态，拒绝请求
    HALF_OPEN = "half_open"  # 半开状态，尝试恢复


class CircuitBreaker:
    """熔断器
    
    在连续失败达到阈值时打开熔断器，保护系统避免雪崩
    """
    
    def __init__(
        self,
        failure_threshold: int = 3,
        timeout: float = 60.0,
        success_threshold: int = 1
    ):
        """初始化熔断器
        
        Args:
            failure_threshold: 连续失败阈值
            timeout: 熔断超时时间（秒），超时后进入半开状态
            success_threshold: 半开状态下需要的成功次数才能关闭熔断器
        """
        self.failure_threshold = failure_threshold
        self.timeout = timeout
        self.success_threshold = success_threshold
        
        self.failure_count = 0
        self.success_count = 0
        self.last_failure_time = None
        self.state = CircuitState.CLOSED
        
        self.lock = threading.Lock()
    
    def call(self, func: Callable, *args, **kwargs) -> Any:
        """通过熔断器调用函数
        
        Args:
            func: 要调用的函数
            *args: 位置参数
            **kwargs: 关键字参数
            
        Returns:
            函数返回值
            
        Raises:
            Exception: 当熔断器打开时或函数执行失败时
        """
        with self.lock:
            # 检查是否需要从打开状态转为半开状态
            if self.state == CircuitState.OPEN:
                if self._should_attempt_reset():
                    self.state = CircuitState.HALF_OPEN
                    self.success_count = 0
                    logger.info("熔断器进入半开状态，尝试恢复")
                else:
                    raise Exception("熔断器已打开，拒绝请求")
        
        # 执行函数调用
        try:
            result = func(*args, **kwargs)
            self._on_success()
            return result
        except Exception as e:
            self._on_failure()
            raise e
    
    def _should_attempt_reset(self) -> bool:
        """判断是否应该尝试重置熔断器
        
        Returns:
            是否应该尝试重置
        """
        if self.last_failure_time is None:
            return False
        
        return time.time() - self.last_failure_time >= self.timeout
    
    def _on_success(self):
        """处理成功的调用"""
        with self.lock:
            if self.state == CircuitState.HALF_OPEN:
                self.success_count += 1
                if self.success_count >= self.success_threshold:
                    self.state = CircuitState.CLOSED
                    self.failure_count = 0
                    logger.info("熔断器已关闭，恢复正常")
            else:
                # 在关闭状态下，重置失败计数
                self.failure_count = 0
    
    def _on_failure(self):
        """处理失败的调用"""
        with self.lock:
            self.failure_count += 1
            self.last_failure_time = time.time()
            
            if self.state == CircuitState.HALF_OPEN:
                # 半开状态下失败，立即打开熔断器
                self.state = CircuitState.OPEN
                logger.warning("熔断器在半开状态下失败，重新打开")
            elif self.failure_count >= self.failure_threshold:
                # 失败次数达到阈值，打开熔断器
                self.state = CircuitState.OPEN
                logger.warning(f"连续失败 {self.failure_count} 次，熔断器已打开")
    
    def is_open(self) -> bool:
        """判断熔断器是否打开
        
        Returns:
            熔断器是否打开
        """
        with self.lock:
            return self.state == CircuitState.OPEN
    
    def reset(self):
        """重置熔断器"""
        with self.lock:
            self.state = CircuitState.CLOSED
            self.failure_count = 0
            self.success_count = 0
            self.last_failure_time = None
            logger.info("熔断器已手动重置")
    
    def get_state(self) -> str:
        """获取当前状态
        
        Returns:
            状态字符串
        """
        with self.lock:
            return self.state.value
