"""
自定义异常类
============

分层异常体系：
- AppException: 业务层错误（参数校验、权限、资源不存在等）
- PlatformError: 第三方平台调用错误（网络、限流、认证、服务异常等）

Router 层只处理这两类异常，其余全部兜底为 500。
"""

from fastapi import HTTPException, status

# ═══════════════════════════════════════════════════════════════
# 业务层异常（AppException 体系）
# ═══════════════════════════════════════════════════════════════


class AppException(HTTPException):
    """应用基础异常"""

    def __init__(
        self,
        status_code: int,
        message: str = "操作失败",
        detail: dict | None = None,
    ):
        super().__init__(status_code=status_code, detail=detail or {})
        self.message = message


class NotFoundException(AppException):
    """资源不存在"""

    def __init__(self, message: str = "资源不存在"):
        super().__init__(
            status_code=status.HTTP_404_NOT_FOUND,
            message=message,
        )


class ValidationException(AppException):
    """参数验证失败"""

    def __init__(self, message: str = "参数验证失败"):
        super().__init__(
            status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
            message=message,
        )


class UnauthorizedException(AppException):
    """未授权"""

    def __init__(self, message: str = "未授权，请先登录"):
        super().__init__(
            status_code=status.HTTP_401_UNAUTHORIZED,
            message=message,
        )


class ForbiddenException(AppException):
    """禁止访问"""

    def __init__(self, message: str = "无权访问该资源"):
        super().__init__(
            status_code=status.HTTP_403_FORBIDDEN,
            message=message,
        )


class BusinessException(AppException):
    """业务逻辑错误"""

    def __init__(self, message: str = "业务操作失败"):
        super().__init__(
            status_code=status.HTTP_400_BAD_REQUEST,
            message=message,
        )


class ModelUnavailableException(AppException):
    """AI 模型不可用"""

    def __init__(self, message: str = "AI 模型服务暂时不可用"):
        super().__init__(
            status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
            message=message,
        )


class TaskFailedException(AppException):
    """异步任务执行失败"""

    def __init__(self, message: str = "任务执行失败"):
        super().__init__(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            message=message,
        )


# ═══════════════════════════════════════════════════════════════
# 第三方平台异常（PlatformError 体系）
# ═══════════════════════════════════════════════════════════════


class PlatformErrorType:
    """第三方错误类型标准枚举

    所有 Adapter 必须将供应商异常映射为以下标准类型之一，
    确保前端和网关能够统一处理。
    """

    RATE_LIMIT = "rate_limit"          # 429，可重试
    AUTH_FAILED = "auth_failed"        # 401/403，不可重试
    TIMEOUT = "timeout"                # 连接/读取超时，可重试
    SERVER_ERROR = "server_error"      # 第三方 5xx，可重试
    BAD_REQUEST = "bad_request"        # 参数错误，不可重试
    QUOTA_EXHAUSTED = "quota_exhausted"  # 额度用完，不可重试（或延迟重试）
    NOT_FOUND = "not_found"            # 资源不存在，不可重试
    UNKNOWN = "unknown"                # 兜底


class PlatformError(Exception):
    """第三方平台调用失败的唯一异常类

    Router 层只需 except PlatformError，即可返回标准 HTTP 状态码。
    所有 app/services/ 和 app/ai/ 下的代码，对外抛出的异常必须是此类。

    Example:
        raise PlatformError(
            "Vidu 限流",
            platform="vidu",
            retryable=True,
            error_type=PlatformErrorType.RATE_LIMIT,
            status_code=429,
        )
    """

    def __init__(
        self,
        message: str,
        *,
        platform: str,
        retryable: bool = False,
        error_type: str = PlatformErrorType.UNKNOWN,
        status_code: int | None = None,
    ):
        super().__init__(message)
        self.platform = platform
        self.retryable = retryable
        self.error_type = error_type
        self.status_code = status_code

    def to_http_status(self) -> int:
        """根据 error_type 和 retryable 返回标准 HTTP 状态码"""
        mapping = {
            PlatformErrorType.RATE_LIMIT: 429,
            PlatformErrorType.QUOTA_EXHAUSTED: 429,
            PlatformErrorType.TIMEOUT: 504,
            PlatformErrorType.AUTH_FAILED: 401,
            PlatformErrorType.BAD_REQUEST: 400,
            PlatformErrorType.NOT_FOUND: 404,
        }
        if self.error_type in mapping:
            return mapping[self.error_type]
        return 502 if self.retryable else 400
