"""
系统韧性和风险管控模块
提供全局的错误处理、自动恢复、降级策略和用户友好的错误体验
"""

import asyncio
import functools
import threading
import time
import traceback
import uuid
from contextlib import asynccontextmanager
from datetime import datetime, timedelta, timezone
from enum import Enum
from typing import Any, Callable, Dict, List, Optional, Type, Union

from ..core.exceptions import ProxyException
from src.core.logger import logger



class ErrorSeverity(Enum):
    """错误严重程度"""

    LOW = "low"  # 低级错误，不影响核心功能
    MEDIUM = "medium"  # 中级错误，影响部分功能
    HIGH = "high"  # 高级错误，影响主要功能
    CRITICAL = "critical"  # 严重错误，影响系统可用性


class RecoveryStrategy(Enum):
    """恢复策略"""

    RETRY = "retry"  # 重试
    FALLBACK = "fallback"  # 降级
    CIRCUIT_BREAKER = "circuit_breaker"  # 熔断
    GRACEFUL_DEGRADE = "graceful_degrade"  # 优雅降级
    USER_NOTIFY = "user_notify"  # 通知用户


class ErrorPattern:
    """错误模式定义"""

    def __init__(
        self,
        error_types: List[Type[Exception]],
        severity: ErrorSeverity,
        recovery_strategy: RecoveryStrategy,
        user_message: str,
        auto_recover: bool = True,
        max_retries: int = 3,
        retry_delay: float = 1.0,
        circuit_threshold: int = 5,
    ):
        self.error_types = error_types
        self.severity = severity
        self.recovery_strategy = recovery_strategy
        self.user_message = user_message
        self.auto_recover = auto_recover
        self.max_retries = max_retries
        self.retry_delay = retry_delay
        self.circuit_threshold = circuit_threshold


class CircuitBreaker:
    """熔断器"""

    def __init__(self, failure_threshold: int = 5, timeout: int = 60):
        self.failure_threshold = failure_threshold
        self.timeout = timeout
        self.failure_count = 0
        self.last_failure_time = None
        self.state = "closed"  # closed, open, half-open
        self._lock = threading.Lock()

    def call(self, func: Callable, *args, **kwargs):
        """执行函数调用，应用熔断逻辑"""
        with self._lock:
            if self.state == "open":
                if self._should_attempt_reset():
                    self.state = "half-open"
                else:
                    raise Exception("服务暂时不可用，请稍后重试")

            try:
                result = func(*args, **kwargs)
                self._on_success()
                return result
            except Exception as e:
                self._on_failure()
                raise

    def _should_attempt_reset(self) -> bool:
        """检查是否应该尝试重置熔断器"""
        if self.last_failure_time is None:
            return True
        return time.time() - self.last_failure_time >= self.timeout

    def _on_success(self):
        """成功时重置计数器"""
        self.failure_count = 0
        self.state = "closed"

    def _on_failure(self):
        """失败时增加计数器"""
        self.failure_count += 1
        self.last_failure_time = time.time()
        if self.failure_count >= self.failure_threshold:
            self.state = "open"


class ResilienceManager:
    """系统韧性管理器"""

    def __init__(self):
        self.error_patterns: List[ErrorPattern] = []
        self.circuit_breakers: Dict[str, CircuitBreaker] = {}
        self.error_stats: Dict[str, int] = {}
        self.last_errors: List[Dict[str, Any]] = []
        self._setup_default_patterns()

    def _setup_default_patterns(self):
        """设置默认错误处理模式"""

        # 数据库连接错误 - 只捕获特定的数据库相关异常
        try:
            from sqlalchemy.exc import (
                DatabaseError,
                DisconnectionError,
                OperationalError,
                StatementError,
            )
            from sqlalchemy.exc import TimeoutError as SQLTimeoutError

            db_exceptions = [
                OperationalError,
                DisconnectionError,
                SQLTimeoutError,
                StatementError,
                DatabaseError,
            ]
        except ImportError:
            # 如果SQLAlchemy不可用，使用通用异常类型
            db_exceptions = [ConnectionError, OSError]

        self.add_error_pattern(
            ErrorPattern(
                error_types=db_exceptions,
                severity=ErrorSeverity.HIGH,
                recovery_strategy=RecoveryStrategy.RETRY,
                user_message="数据库连接异常，正在重试...",
                max_retries=3,
                retry_delay=1.0,
            )
        )

        # 认证相关错误 - 只捕获特定的认证异常
        try:
            from ..core.exceptions import ForbiddenException, ProviderAuthException

            auth_exceptions = [ProviderAuthException, ForbiddenException]
        except ImportError:
            # 如果无法导入特定异常，使用更保守的方式（不使用通用异常）
            auth_exceptions = []

        if auth_exceptions:
            self.add_error_pattern(
                ErrorPattern(
                    error_types=auth_exceptions,
                    severity=ErrorSeverity.MEDIUM,
                    recovery_strategy=RecoveryStrategy.USER_NOTIFY,
                    user_message="认证失败，请检查API密钥或重新登录",
                    auto_recover=False,
                )
            )

        # 网络请求错误
        self.add_error_pattern(
            ErrorPattern(
                error_types=[ConnectionError, TimeoutError],
                severity=ErrorSeverity.MEDIUM,
                recovery_strategy=RecoveryStrategy.FALLBACK,
                user_message="网络连接异常，正在尝试备用方案...",
                max_retries=2,
            )
        )

    def add_error_pattern(self, pattern: ErrorPattern):
        """添加错误处理模式"""
        self.error_patterns.append(pattern)

    def get_circuit_breaker(self, key: str) -> CircuitBreaker:
        """获取或创建熔断器"""
        if key not in self.circuit_breakers:
            self.circuit_breakers[key] = CircuitBreaker()
        return self.circuit_breakers[key]

    def handle_error(
        self, error: Exception, context: Dict[str, Any] = None, operation: str = "unknown"
    ) -> Dict[str, Any]:
        """处理错误并返回处理结果"""

        error_id = str(uuid.uuid4())[:8]
        context = context or {}

        # 记录错误
        error_info = {
            "error_id": error_id,
            "error_type": type(error).__name__,
            "error_message": str(error),
            "operation": operation,
            "context": context,
            "timestamp": datetime.now(timezone.utc),
            "traceback": traceback.format_exc(),
        }

        self.last_errors.append(error_info)
        # 只保留最近100个错误
        if len(self.last_errors) > 100:
            self.last_errors.pop(0)

        # 更新错误统计
        error_key = f"{type(error).__name__}:{operation}"
        self.error_stats[error_key] = self.error_stats.get(error_key, 0) + 1

        # 查找匹配的错误处理模式
        pattern = self._find_matching_pattern(error)

        if pattern:
            logger.error(f"错误处理 [{error_id}]: {pattern.user_message}")

            return {
                "error_id": error_id,
                "severity": pattern.severity,
                "recovery_strategy": pattern.recovery_strategy,
                "user_message": pattern.user_message,
                "auto_recover": pattern.auto_recover,
                "pattern": pattern,
            }
        else:
            # 未匹配的错误，使用默认处理
            logger.error(f"未知错误 [{error_id}]: {str(error)}")

            return {
                "error_id": error_id,
                "severity": ErrorSeverity.MEDIUM,
                "recovery_strategy": RecoveryStrategy.USER_NOTIFY,
                "user_message": "系统遇到未知错误，请稍后重试或联系管理员",
                "auto_recover": False,
                "pattern": None,
            }

    def _find_matching_pattern(self, error: Exception) -> Optional[ErrorPattern]:
        """查找匹配的错误处理模式"""
        for pattern in self.error_patterns:
            if any(isinstance(error, error_type) for error_type in pattern.error_types):
                return pattern
        return None

    def get_error_stats(self) -> Dict[str, Any]:
        """获取错误统计"""
        return {
            "total_errors": sum(self.error_stats.values()),
            "error_breakdown": self.error_stats.copy(),
            "recent_errors": len(self.last_errors),
            "circuit_breakers": {
                key: {"state": cb.state, "failure_count": cb.failure_count}
                for key, cb in self.circuit_breakers.items()
            },
        }


# 全局韧性管理器实例
resilience_manager = ResilienceManager()


def resilient_operation(
    operation_name: str = None,
    max_retries: int = None,
    retry_delay: float = None,
    circuit_breaker_key: str = None,
    context: Dict[str, Any] = None,
):
    """
    韧性操作装饰器
    自动处理重试、熔断、错误记录等
    """

    def decorator(func: Callable) -> Callable:
        @functools.wraps(func)
        async def async_wrapper(*args, **kwargs):
            op_name = operation_name or f"{func.__module__}.{func.__name__}"
            retries = max_retries or 3
            delay = retry_delay or 1.0

            last_error = None

            for attempt in range(retries + 1):
                try:
                    # 如果指定了熔断器，使用熔断逻辑
                    if circuit_breaker_key:
                        cb = resilience_manager.get_circuit_breaker(circuit_breaker_key)
                        if asyncio.iscoroutinefunction(func):
                            return await cb.call(func, *args, **kwargs)
                        else:
                            return cb.call(func, *args, **kwargs)
                    else:
                        if asyncio.iscoroutinefunction(func):
                            return await func(*args, **kwargs)
                        else:
                            return func(*args, **kwargs)

                except Exception as e:
                    last_error = e

                    # 处理错误
                    error_result = resilience_manager.handle_error(
                        error=e,
                        context={**(context or {}), "attempt": attempt + 1, "max_retries": retries},
                        operation=op_name,
                    )

                    # 如果是最后一次尝试，或者不应该自动恢复，直接抛出
                    if attempt == retries or not error_result.get("auto_recover", True):
                        raise ProxyException(
                            status_code=500,
                            error_type="system_error",
                            message=error_result["user_message"],
                            details={
                                "error_id": error_result["error_id"],
                                "original_error": str(e),
                            },
                        )

                    # 等待后重试
                    if attempt < retries:
                        await asyncio.sleep(delay * (attempt + 1))  # 指数退避

            # 这里不应该到达，但作为安全网
            raise last_error

        @functools.wraps(func)
        def sync_wrapper(*args, **kwargs):
            # 对于同步函数，创建异步包装器并运行
            return asyncio.run(async_wrapper(*args, **kwargs))

        # 根据函数类型返回对应的包装器
        if asyncio.iscoroutinefunction(func):
            return async_wrapper
        else:
            return sync_wrapper

    return decorator


@asynccontextmanager
async def safe_operation(operation_name: str, context: Dict[str, Any] = None):
    """
    安全操作上下文管理器
    自动处理异常并提供用户友好的错误信息
    """
    try:
        yield
    except Exception as e:
        error_result = resilience_manager.handle_error(
            error=e, context=context or {}, operation=operation_name
        )

        # 根据错误严重程度决定是否抛出异常
        if error_result["severity"] in [ErrorSeverity.HIGH, ErrorSeverity.CRITICAL]:
            raise ProxyException(
                status_code=500,
                error_type="system_error",
                message=error_result["user_message"],
                details={"error_id": error_result["error_id"]},
            )
        else:
            # 记录警告但不中断操作
            logger.warning(f"操作警告 [{error_result['error_id']}]: {error_result['user_message']}")


def graceful_degradation(fallback_func: Callable = None, fallback_value: Any = None):
    """
    优雅降级装饰器
    当主要功能失败时，自动切换到备用方案
    """

    def decorator(func: Callable) -> Callable:
        @functools.wraps(func)
        async def async_wrapper(*args, **kwargs):
            try:
                if asyncio.iscoroutinefunction(func):
                    return await func(*args, **kwargs)
                else:
                    return func(*args, **kwargs)
            except Exception as e:
                logger.warning(f"主要功能失败，启用降级模式: {func.__name__}")

                if fallback_func:
                    try:
                        if asyncio.iscoroutinefunction(fallback_func):
                            return await fallback_func(*args, **kwargs)
                        else:
                            return fallback_func(*args, **kwargs)
                    except Exception as fallback_error:
                        logger.exception(f"降级方案也失败了: {fallback_func.__name__}")
                        raise e  # 抛出原始错误
                else:
                    return fallback_value

        if asyncio.iscoroutinefunction(func):
            return async_wrapper
        else:
            return lambda *args, **kwargs: asyncio.run(async_wrapper(*args, **kwargs))

    return decorator


# 导出主要接口
__all__ = [
    "resilience_manager",
    "resilient_operation",
    "safe_operation",
    "graceful_degradation",
    "ErrorSeverity",
    "RecoveryStrategy",
    "ErrorPattern",
]
