"""Structured exception hierarchy for axm-bib.

All public exceptions inherit from :class:`AXMBibError` so that
callers can use a single ``except AXMBibError`` to catch everything
or be granular with subclasses.

Hierarchy::

    AXMBibError
    ├── ResolutionError          # identifier → BibTeX failed
    │   ├── IdentifierNotFound   # no results for identifier
    │   └── AmbiguousIdentifier  # multiple conflicting results
    ├── ClientError              # external API call failed
    │   ├── RateLimitError       # 429 / Retry-After — recoverable
    │   └── APIUnavailable       # 502/503/504 — temporary
    ├── DownloadError            # PDF download failed
    ├── ExtractionError          # PDF parsing/extraction failed
    └── MergeConflict            # BibEntry merge coherence failure
"""

from __future__ import annotations

__all__ = [
    "APIUnavailable",
    "AXMBibError",
    "AmbiguousIdentifier",
    "ClientError",
    "DownloadError",
    "ExtractionError",
    "IdentifierNotFound",
    "MergeConflict",
    "RateLimitError",
    "ResolutionError",
]


class AXMBibError(Exception):
    """Base exception for all axm-bib errors."""


# ── Resolution errors ─────────────────────────────────────────────────────


class ResolutionError(AXMBibError):
    """Identifier → BibTeX resolution failed."""


class IdentifierNotFound(ResolutionError):  # noqa: N818
    """No results for this identifier."""


class AmbiguousIdentifier(ResolutionError):  # noqa: N818
    """Multiple conflicting results for identifier."""


# ── Client / network errors ──────────────────────────────────────────────


class ClientError(AXMBibError):
    """External API call failed."""


class RateLimitError(ClientError):
    """429 / Retry-After — recoverable.

    Attributes:
        retry_after: Suggested wait time in seconds, or ``None``
            if the server did not provide a ``Retry-After`` header.
    """

    def __init__(self, message: str, *, retry_after: float | None = None) -> None:
        super().__init__(message)
        self.retry_after = retry_after


class APIUnavailable(ClientError):  # noqa: N818
    """502/503/504 — service temporarily down."""


# ── Download / extraction errors ──────────────────────────────────────────


class DownloadError(AXMBibError):
    """PDF download failed."""


class ExtractionError(AXMBibError):
    """PDF parsing/extraction failed."""


# ── Merge errors ──────────────────────────────────────────────────────────


class MergeConflict(AXMBibError):  # noqa: N818
    """BibEntry merge coherence check failure."""
