"""OpenAI 互換のエラー応答。

401 / 403 / 404 / 413 / 429 / 502 / 504 を含む全ての 4xx / 5xx が
同一のエンベロープを返す。control API の契約もこの形に一致する。

    {"error": {"message": "...", "type": "invalid_request_error", "code": "unauthorized"}}
"""

from __future__ import annotations

from typing import Any, Mapping

from starlette.responses import JSONResponse

TYPE_INVALID_REQUEST = "invalid_request_error"
TYPE_AUTHENTICATION = "authentication_error"
TYPE_PERMISSION = "permission_error"
TYPE_RATE_LIMIT = "rate_limit_error"
TYPE_API = "api_error"


class GatewayError(Exception):
    """エラー応答へ変換できる例外。"""

    __slots__ = ("status_code", "message", "type", "code", "headers")

    def __init__(
        self,
        status_code: int,
        message: str,
        *,
        type: str = TYPE_INVALID_REQUEST,
        code: str = "invalid_request",
        headers: Mapping[str, str] | None = None,
    ) -> None:
        super().__init__(message)
        self.status_code = status_code
        self.message = message
        self.type = type
        self.code = code
        self.headers = dict(headers or {})

    def to_response(self) -> JSONResponse:
        return error_response(
            self.status_code,
            self.message,
            type=self.type,
            code=self.code,
            headers=self.headers,
        )


def error_payload(message: str, *, type: str, code: str) -> dict[str, Any]:
    """共通エラー本文を組み立てる。"""

    return {"error": {"message": message, "type": type, "code": code}}


def error_response(
    status_code: int,
    message: str,
    *,
    type: str = TYPE_INVALID_REQUEST,
    code: str = "invalid_request",
    headers: Mapping[str, str] | None = None,
) -> JSONResponse:
    """共通エラー本文を持つ JSON 応答を返す。"""

    return JSONResponse(
        error_payload(message, type=type, code=code),
        status_code=status_code,
        headers=dict(headers or {}),
    )


def unauthorized(message: str = "Invalid API key provided.", *, code: str = "invalid_api_key") -> GatewayError:
    return GatewayError(401, message, type=TYPE_AUTHENTICATION, code=code)


def control_unauthorized(message: str = "Invalid control token.") -> GatewayError:
    return GatewayError(401, message, type=TYPE_INVALID_REQUEST, code="unauthorized")


def forbidden_model(message: str, *, code: str = "model_not_allowed") -> GatewayError:
    return GatewayError(403, message, type=TYPE_PERMISSION, code=code)


def not_found(message: str = "Not found.", *, code: str = "not_found") -> GatewayError:
    return GatewayError(404, message, type=TYPE_INVALID_REQUEST, code=code)


def bad_request(message: str, *, code: str = "invalid_request") -> GatewayError:
    return GatewayError(400, message, type=TYPE_INVALID_REQUEST, code=code)


def payload_too_large(message: str, *, code: str = "request_too_large") -> GatewayError:
    return GatewayError(413, message, type=TYPE_INVALID_REQUEST, code=code)


def rate_limited(message: str, *, retry_after: int, code: str = "rate_limit_exceeded") -> GatewayError:
    return GatewayError(
        429,
        message,
        type=TYPE_RATE_LIMIT,
        code=code,
        headers={"Retry-After": str(max(1, int(retry_after)))},
    )


def bad_gateway(message: str = "Upstream is unreachable.", *, code: str = "upstream_unreachable") -> GatewayError:
    return GatewayError(502, message, type=TYPE_API, code=code)


def gateway_timeout(message: str = "Upstream timed out.", *, code: str = "upstream_timeout") -> GatewayError:
    return GatewayError(504, message, type=TYPE_API, code=code)
