from __future__ import annotations

from typing import Any


class ArtaSupportError(Exception):
    """Base exception for all SDK errors."""


class ConfigurationError(ArtaSupportError):
    """Raised when required SDK configuration is missing or invalid."""


class APIConnectionError(ArtaSupportError):
    """Raised when a request cannot reach the API service."""


class APIError(ArtaSupportError):
    """Raised when the API returns a non-2xx response."""

    def __init__(
        self,
        message: str,
        *,
        status_code: int | None = None,
        error_code: str | None = None,
        request_id: str | None = None,
        details: Any = None,
        raw: Any = None,
    ):
        super().__init__(message)
        self.message = message
        self.status_code = status_code
        self.error_code = error_code
        self.request_id = request_id
        self.details = details
        self.raw = raw

    def __str__(self) -> str:
        parts: list[str] = [self.message]
        if self.status_code is not None:
            parts.append(f"(status_code={self.status_code})")
        if self.request_id:
            parts.append(f"(request_id={self.request_id})")
        return " ".join(parts)


class AuthenticationError(APIError):
    """Raised for 401/403 API responses."""


class NotFoundError(APIError):
    """Raised for 404 API responses."""


class RateLimitError(APIError):
    """Raised for 429 API responses."""


class ServerError(APIError):
    """Raised for 5xx API responses."""


def build_api_error(
    *,
    status_code: int,
    message: str,
    error_code: str | None = None,
    request_id: str | None = None,
    details: Any = None,
    raw: Any = None,
) -> APIError:
    error_cls: type[APIError] = APIError
    if status_code in (401, 403):
        error_cls = AuthenticationError
    elif status_code == 404:
        error_cls = NotFoundError
    elif status_code == 429:
        error_cls = RateLimitError
    elif status_code >= 500:
        error_cls = ServerError

    return error_cls(
        message,
        status_code=status_code,
        error_code=error_code,
        request_id=request_id,
        details=details,
        raw=raw,
    )
