"""
Canonical protocol-level error taxonomy.

Dispatch raises these typed exceptions; each backend adapter renders them to
its native response (Django `JsonResponse`, FastAPI exception handler, …). The
shared dispatch core never returns error envelopes — it raises, and the adapter
catches at its boundary.
"""

from __future__ import annotations

from enum import Enum
from typing import Any


class ErrorCode(str, Enum):
    NOT_FOUND = "NOT_FOUND"
    BAD_REQUEST = "BAD_REQUEST"
    VALIDATION_ERROR = "VALIDATION_ERROR"
    UNAUTHORIZED = "UNAUTHORIZED"
    FORBIDDEN = "FORBIDDEN"
    NOT_IMPLEMENTED = "NOT_IMPLEMENTED"
    INTERNAL_ERROR = "INTERNAL_ERROR"


STATUS = {
    ErrorCode.NOT_FOUND: 404,
    ErrorCode.BAD_REQUEST: 400,
    ErrorCode.VALIDATION_ERROR: 422,
    ErrorCode.UNAUTHORIZED: 401,
    ErrorCode.FORBIDDEN: 403,
    ErrorCode.NOT_IMPLEMENTED: 501,
    ErrorCode.INTERNAL_ERROR: 500,
}


class MizanError(Exception):
    """Base for protocol-level dispatch errors."""

    code: ErrorCode = ErrorCode.INTERNAL_ERROR

    def __init__(self, message: str, *, details: dict[str, Any] | None = None) -> None:
        super().__init__(message)
        self.message = message
        self.details = details

    @property
    def status_code(self) -> int:
        return STATUS[self.code]


class NotFound(MizanError):           code = ErrorCode.NOT_FOUND          # noqa: E701
class BadRequest(MizanError):         code = ErrorCode.BAD_REQUEST        # noqa: E701
class ValidationFailed(MizanError):   code = ErrorCode.VALIDATION_ERROR   # noqa: E701
class Unauthorized(MizanError):       code = ErrorCode.UNAUTHORIZED       # noqa: E701
class Forbidden(MizanError):          code = ErrorCode.FORBIDDEN          # noqa: E701
class NotImplementedYet(MizanError):  code = ErrorCode.NOT_IMPLEMENTED    # noqa: E701
class InternalError(MizanError):      code = ErrorCode.INTERNAL_ERROR     # noqa: E701
