"""Verilock SDK exceptions.

All exceptions raised by the SDK inherit from :class:`VerilockError`.
"""

from __future__ import annotations

from typing import Any, Dict, Optional

__all__ = [
    "VerilockError",
    "AuthenticationError",
    "InsufficientBalanceError",
    "NotFoundError",
    "ValidationError",
    "RateLimitError",
]


class VerilockError(Exception):
    """Base exception for all Verilock API errors.

    Attributes:
        message: Human-readable error description.
        code: Machine-readable error code (e.g. ``"authentication_error"``).
        status_code: HTTP status code, if available.
        body: Raw error response body from the API, if available.
    """

    message: str
    code: str
    status_code: Optional[int]
    body: Optional[Dict[str, Any]]

    def __init__(
        self,
        message: str,
        code: str = "unknown",
        status_code: Optional[int] = None,
        body: Optional[Dict[str, Any]] = None,
    ) -> None:
        super().__init__(message)
        self.message = message
        self.code = code
        self.status_code = status_code
        self.body = body

    def __repr__(self) -> str:
        return f"{self.__class__.__name__}(message={self.message!r}, code={self.code!r}, status_code={self.status_code!r})"


class AuthenticationError(VerilockError):
    """Raised when the API key is invalid or missing (HTTP 401)."""

    def __init__(self, message: str, body: Optional[Dict[str, Any]] = None) -> None:
        code = body.get("error", "authentication_error") if body else "authentication_error"
        super().__init__(message, code=code, status_code=401, body=body)


class InsufficientBalanceError(VerilockError):
    """Raised when your account balance is too low (HTTP 402).

    Attributes:
        required: The amount required for the operation.
        balance: Your current account balance.
    """

    required: float
    balance: float

    def __init__(self, body: Dict[str, Any]) -> None:
        super().__init__(
            body.get("message", "Insufficient balance"),
            code="insufficient_balance",
            status_code=402,
            body=body,
        )
        self.required = body.get("required", 0)
        self.balance = body.get("balance", 0)


class NotFoundError(VerilockError):
    """Raised when the requested resource does not exist (HTTP 404)."""

    def __init__(self, message: str) -> None:
        super().__init__(message, code="not_found", status_code=404)


class ValidationError(VerilockError):
    """Raised when request parameters fail validation (HTTP 422).

    Attributes:
        errors: A mapping of field names to lists of validation error messages.
    """

    errors: Dict[str, list[str]]

    def __init__(self, message: str, errors: Optional[Dict[str, list[str]]] = None) -> None:
        super().__init__(message, code="validation_error", status_code=422)
        self.errors = errors or {}


class RateLimitError(VerilockError):
    """Raised when you exceed the API rate limit (HTTP 429).

    Attributes:
        retry_after: Suggested wait time in seconds before retrying, if provided.
    """

    retry_after: Optional[int]

    def __init__(self, message: str, retry_after: Optional[int] = None) -> None:
        super().__init__(message, code="rate_limit_exceeded", status_code=429)
        self.retry_after = retry_after
