"""
自定义异常 + 全局异常处理器
统一输出格式: { "code": int, "data": Any, "message": str }
"""

from __future__ import annotations

from typing import Any

from fastapi import FastAPI, Request
from fastapi.exceptions import RequestValidationError
from fastapi.responses import JSONResponse
from starlette.exceptions import HTTPException as StarletteHTTPException


# ── 自定义业务异常 ─────────────────────────────────────────
class BizException(Exception):
    """通用业务异常，code 对应 HTTP 状态码"""

    def __init__(self, code: int = 400, message: str = "业务异常", data: Any = None):
        self.code = code
        self.message = message
        self.data = data


class UnauthorizedException(BizException):
    def __init__(self, message: str = "未登录或 Token 已失效"):
        super().__init__(code=401, message=message)


class ForbiddenException(BizException):
    def __init__(self, message: str = "无权访问"):
        super().__init__(code=403, message=message)


class NotFoundException(BizException):
    def __init__(self, message: str = "资源不存在"):
        super().__init__(code=404, message=message)


# ── 统一响应构造 ──────────────────────────────────────────
def _make_response(code: int, message: str, data: Any = None) -> JSONResponse:
    return JSONResponse(
        status_code=code,
        content={"code": code, "data": data, "message": message},
    )


# ── 注册全局异常处理器 ────────────────────────────────────
def register_exception_handlers(app: FastAPI) -> None:

    @app.exception_handler(BizException)
    async def biz_exception_handler(_req: Request, exc: BizException) -> JSONResponse:
        return _make_response(exc.code, exc.message, exc.data)

    @app.exception_handler(StarletteHTTPException)
    async def http_exception_handler(
        _req: Request, exc: StarletteHTTPException
    ) -> JSONResponse:
        return _make_response(exc.status_code, str(exc.detail))

    @app.exception_handler(RequestValidationError)
    async def validation_exception_handler(
        _req: Request, exc: RequestValidationError
    ) -> JSONResponse:
        # 把 Pydantic 校验错误打平为可读字符串
        errors = []
        for e in exc.errors():
            loc = " -> ".join(str(x) for x in e.get("loc", []))
            errors.append(f"{loc}: {e.get('msg', '')}")
        return _make_response(422, "请求参数校验失败", errors)

    @app.exception_handler(Exception)
    async def global_exception_handler(
        _req: Request, exc: Exception
    ) -> JSONResponse:
        # 兜底：未知异常统一 500
        return _make_response(500, f"服务器内部错误: {exc!s}")
