"""Tests for axm_bib.exceptions hierarchy."""

from __future__ import annotations

from axm_bib.exceptions import (
    AmbiguousIdentifier,
    APIUnavailable,
    AXMBibError,
    ClientError,
    DownloadError,
    ExtractionError,
    IdentifierNotFound,
    MergeConflict,
    RateLimitError,
    ResolutionError,
)


class TestHierarchyInheritance:
    """Verify the exception class hierarchy."""

    def test_resolution_chain(self) -> None:
        assert issubclass(ResolutionError, AXMBibError)
        assert issubclass(IdentifierNotFound, ResolutionError)
        assert issubclass(AmbiguousIdentifier, ResolutionError)

    def test_client_chain(self) -> None:
        assert issubclass(ClientError, AXMBibError)
        assert issubclass(RateLimitError, ClientError)
        assert issubclass(APIUnavailable, ClientError)

    def test_other_leaves(self) -> None:
        assert issubclass(DownloadError, AXMBibError)
        assert issubclass(ExtractionError, AXMBibError)
        assert issubclass(MergeConflict, AXMBibError)

    def test_base_catch_all(self) -> None:
        """except AXMBibError should catch any subclass."""
        for exc_class in (
            ResolutionError,
            IdentifierNotFound,
            AmbiguousIdentifier,
            ClientError,
            RateLimitError,
            APIUnavailable,
            DownloadError,
            ExtractionError,
            MergeConflict,
        ):
            exc = (
                exc_class("test")
                if exc_class is not RateLimitError
                else RateLimitError("test")
            )
            assert isinstance(exc, AXMBibError)


class TestRateLimitError:
    """RateLimitError carries optional retry_after."""

    def test_with_retry_after(self) -> None:
        exc = RateLimitError("rate limited", retry_after=30.0)
        assert exc.retry_after == 30.0
        assert str(exc) == "rate limited"

    def test_without_retry_after(self) -> None:
        exc = RateLimitError("rate limited")
        assert exc.retry_after is None

    def test_chained_exception(self) -> None:
        """Verify exception chaining preserves cause."""
        original = ValueError("original")
        exc = RateLimitError("rate limited", retry_after=5.0)
        exc.__cause__ = original
        assert exc.__cause__ is original
