from __future__ import annotations

from dataclasses import dataclass
from typing import Any


class UniServerError(Exception):
    """Base exception for the UniServer client package."""


class UniServerConnectionError(UniServerError):
    """Raised when a request cannot reach the UniServer API."""


@dataclass
class UniServerAPIError(UniServerError):
    """Raised for non-2xx responses from the UniServer API."""

    status_code: int
    message: str
    error_code: str | None = None
    payload: Any = None

    def __str__(self) -> str:
        if self.error_code:
            return f"{self.status_code} {self.error_code}: {self.message}"
        return f"{self.status_code}: {self.message}"


class UniServerAuthenticationError(UniServerAPIError):
    """Raised for 401 responses."""


class UniServerAuthorizationError(UniServerAPIError):
    """Raised for 403 responses."""


class UniServerNotFoundError(UniServerAPIError):
    """Raised for 404 responses."""


class UniServerConflictError(UniServerAPIError):
    """Raised for 409 responses."""


class UniServerValidationError(UniServerAPIError):
    """Raised for 422 responses."""


_STATUS_TO_EXCEPTION = {
    401: UniServerAuthenticationError,
    403: UniServerAuthorizationError,
    404: UniServerNotFoundError,
    409: UniServerConflictError,
    422: UniServerValidationError,
}


def map_api_error(
    status_code: int,
    message: str,
    error_code: str | None = None,
    payload: Any = None,
) -> UniServerAPIError:
    exception_cls = _STATUS_TO_EXCEPTION.get(status_code, UniServerAPIError)
    return exception_cls(
        status_code=status_code,
        message=message,
        error_code=error_code,
        payload=payload,
    )
