"""Typed exception hierarchy for the AccuWeather client.

Every error raised by this package derives from :class:`AccuWeatherError`,
so callers can use a single ``except AccuWeatherError`` to catch anything
that went wrong while talking to the API.

Example:
    >>> from accuweather_client import WeatherClient
    >>> from accuweather_client.exceptions import RateLimitExceededError
    >>> try:
    ...     client = WeatherClient(token=API_KEY, city="sydney")
    ...     client.get_current_conditions()
    ... except RateLimitExceededError:
    ...     print("Daily quota exhausted - try again tomorrow.")
"""

from collections.abc import Mapping


class AccuWeatherError(Exception):
    """Base class for all errors raised by ``accuweather_client``."""


class InvalidTokenError(AccuWeatherError):
    """The API token is malformed or was rejected by the API (HTTP 401).

    The token itself is never included in the message, so this error is
    safe to log.
    """


class LocationNotFoundError(AccuWeatherError):
    """No location matched the given city / POI / coordinates / postal code."""


class ApiRequestError(AccuWeatherError):
    """The API rejected the request (HTTP 4xx other than 401/429).

    Attributes:
        status_code: The HTTP status code returned by the API, if any.
    """

    def __init__(self, message: str, status_code: int | None = None):
        super().__init__(message)
        self.status_code = status_code


class ApiServerError(AccuWeatherError):
    """The AccuWeather API returned a server-side error (HTTP 5xx).

    Attributes:
        status_code: The HTTP status code returned by the API, if any.
    """

    def __init__(self, message: str, status_code: int | None = None):
        super().__init__(message)
        self.status_code = status_code


class RateLimitExceededError(ApiRequestError):
    """The request quota for your API key has been exhausted.

    AccuWeather's free tier allows 50 requests per day. When the quota is
    exceeded the API answers with HTTP 429 (or a 503 mentioning the quota).

    Attributes:
        rate_limit_headers: Any ``RateLimit-*`` response headers the API
            sent along, useful to see when the quota resets.
    """

    def __init__(
        self,
        message: str,
        status_code: int | None = None,
        rate_limit_headers: Mapping[str, str] | None = None,
    ):
        super().__init__(message, status_code=status_code)
        self.rate_limit_headers = dict(rate_limit_headers or {})
