"""统一错误码与 HTTP 异常类。

约定的响应 body 格式（参考 ADR-0002）::

    { "code": "STRATEGY_NOT_FOUND", "message": "...", "details": {...} }

子类化 ``InalphaError`` 自定义业务错误，``code`` / ``status_code`` 当类属性覆盖即可。
``raise`` 后会被 ``install_error_handler`` 转成标准 JSON 响应。
"""
from __future__ import annotations

from typing import Any

from fastapi import HTTPException


class InalphaError(HTTPException):
    """业务错误基类。

    实例属性约定（**code / status_code 一定要走 instance attr**）：

    - 子类用 class attr 设默认 ``code`` / ``status_code``
    - 构造器 ``code=...`` / ``status_code=...`` 会**写到 self**（不仅写到 detail dict）
    - 这样 caller 的 ``except InalphaError as e: log(e.code)`` 拿到的是真实运行时 code，
      不是父类默认值（D-8b' review 找到的高风险 bug 修复）

    HTTP body 仍按 ADR-0002 格式：``{code, message, details}`` 进 ``self.detail``。
    """

    code: str = "INTERNAL_ERROR"
    status_code: int = 500

    def __init__(
        self,
        message: str,
        *,
        code: str | None = None,
        status_code: int | None = None,
        details: dict[str, Any] | None = None,
    ) -> None:
        # 1) 把 override 写到 self（实例 attr 覆盖 class attr）
        effective_code = code if code is not None else self.code
        effective_status = status_code if status_code is not None else self.status_code
        self.code = effective_code
        # HTTPException 内部也保留 status_code 字段，super().__init__ 会再设一次
        self.message = message
        self.details = details or {}

        # 2) 调父类构造把 status_code + detail body 装好
        super().__init__(
            status_code=effective_status,
            detail={
                "code": effective_code,
                "message": message,
                "details": self.details,
            },
        )


class NotFoundError(InalphaError):
    """资源不存在。"""

    code = "NOT_FOUND"
    status_code = 404


class ValidationError(InalphaError):
    """业务校验失败（不是 FastAPI 请求体 schema 校验）。"""

    code = "VALIDATION_ERROR"
    status_code = 400


class UnauthorizedError(InalphaError):
    """未认证：缺 token / token 无效 / 过期。"""

    code = "UNAUTHORIZED"
    status_code = 401


class ForbiddenError(InalphaError):
    """已认证但无权限。"""

    code = "FORBIDDEN"
    status_code = 403


class ConflictError(InalphaError):
    """状态冲突（如重复创建、并发修改、状态机不允许转换）。"""

    code = "CONFLICT"
    status_code = 409


class RateLimitedError(InalphaError):
    """限流。"""

    code = "RATE_LIMITED"
    status_code = 429
