from __future__ import annotations

import logging
from dataclasses import dataclass
from http import HTTPStatus

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

from backend.errors import BusinessError

logger = logging.getLogger(__name__)


@dataclass(slots=True)
class AppError(Exception):
    code: str
    message: str
    status_code: int = HTTPStatus.BAD_REQUEST


def _request_id(request: Request) -> str:
    return str(getattr(request.state, "request_id", "unavailable"))


def _response(request: Request, status_code: int, code: str, message: str) -> JSONResponse:
    request_id = _request_id(request)
    return JSONResponse(
        status_code=status_code,
        headers={"X-Request-ID": request_id},
        content={
            "error": {
                "code": code,
                "message": message,
                "request_id": request_id,
            }
        },
    )


def install_error_handlers(application: FastAPI) -> None:
    @application.exception_handler(BusinessError)
    async def business_error(request: Request, error: BusinessError) -> JSONResponse:
        status_code = {
            "invalid_credentials": HTTPStatus.UNAUTHORIZED,
            "account_unavailable": HTTPStatus.FORBIDDEN,
            "username_taken": HTTPStatus.CONFLICT,
            "account_not_found": HTTPStatus.NOT_FOUND,
            "model_not_found": HTTPStatus.NOT_FOUND,
            "permanent_membership": HTTPStatus.CONFLICT,
            "model_in_use": HTTPStatus.CONFLICT,
            "model_name_taken": HTTPStatus.CONFLICT,
            "model_pool_full": HTTPStatus.CONFLICT,
            "model_not_configured": HTTPStatus.SERVICE_UNAVAILABLE,
            "profile_unavailable": HTTPStatus.SERVICE_UNAVAILABLE,
        }.get(error.code, HTTPStatus.BAD_REQUEST)
        return _response(request, status_code, error.code, error.message)

    @application.exception_handler(AppError)
    async def application_error(request: Request, error: AppError) -> JSONResponse:
        return _response(request, error.status_code, error.code, error.message)

    @application.exception_handler(RequestValidationError)
    async def validation_error(request: Request, _error: RequestValidationError) -> JSONResponse:
        return _response(
            request,
            HTTPStatus.UNPROCESSABLE_ENTITY,
            "invalid_request",
            "请求内容不符合要求，请检查后重试。",
        )

    @application.exception_handler(HTTPException)
    async def http_error(request: Request, error: HTTPException) -> JSONResponse:
        code = {
            HTTPStatus.UNAUTHORIZED: "authentication_required",
            HTTPStatus.FORBIDDEN: "access_denied",
            HTTPStatus.NOT_FOUND: "not_found",
            HTTPStatus.CONFLICT: "conflict",
        }.get(error.status_code, "request_failed")
        message = {
            HTTPStatus.UNAUTHORIZED: "请先登录。",
            HTTPStatus.FORBIDDEN: "当前账号无权执行此操作。",
            HTTPStatus.NOT_FOUND: "请求的内容不存在。",
            HTTPStatus.CONFLICT: "当前状态已发生变化，请刷新后重试。",
        }.get(error.status_code, "请求失败，请稍后重试。")
        return _response(request, error.status_code, code, message)

    @application.exception_handler(Exception)
    async def unexpected_error(request: Request, error: Exception) -> JSONResponse:
        logger.exception(
            "Unhandled request failure",
            exc_info=(type(error), error, error.__traceback__),
            extra={"request_id": _request_id(request), "event": "request.failed"},
        )
        return _response(
            request,
            HTTPStatus.INTERNAL_SERVER_ERROR,
            "internal_error",
            "服务暂时不可用，请稍后重试。",
        )
