"""Tests for exception hierarchy."""

from llm_rotator.exceptions import (
    AllAttemptsFailedError,
    KeyDeadError,
    LLMRotatorError,
    ModelRateLimitError,
    QuotaExceededError,
    ServerError,
)


class TestLLMRotatorError:
    def test_is_base_exception(self):
        err = LLMRotatorError("something went wrong")
        assert isinstance(err, Exception)
        assert str(err) == "something went wrong"


class TestKeyDeadError:
    def test_attributes(self):
        err = KeyDeadError(key_alias="main", status_code=402)
        assert err.key_alias == "main"
        assert err.status_code == 402
        assert isinstance(err, LLMRotatorError)

    def test_default_message(self):
        err = KeyDeadError(key_alias="main", status_code=401)
        assert "main" in str(err)
        assert "401" in str(err)

    def test_custom_message(self):
        err = KeyDeadError(key_alias="main", status_code=402, message="balance zero")
        assert str(err) == "balance zero"


class TestModelRateLimitError:
    def test_attributes(self):
        err = ModelRateLimitError(key_alias="backup", model="gpt-4o", retry_after=30)
        assert err.key_alias == "backup"
        assert err.model == "gpt-4o"
        assert err.retry_after == 30
        assert isinstance(err, LLMRotatorError)

    def test_retry_after_none(self):
        err = ModelRateLimitError(key_alias="k", model="m")
        assert err.retry_after is None

    def test_message_contains_details(self):
        err = ModelRateLimitError(key_alias="backup", model="gpt-4o", retry_after=30)
        msg = str(err)
        assert "gpt-4o" in msg
        assert "backup" in msg


class TestServerError:
    def test_attributes(self):
        err = ServerError(provider="openai", status_code=503)
        assert err.provider == "openai"
        assert err.status_code == 503
        assert isinstance(err, LLMRotatorError)

    def test_default_message(self):
        err = ServerError(provider="gemini", status_code=500)
        assert "gemini" in str(err)
        assert "500" in str(err)

    def test_custom_message(self):
        err = ServerError(provider="openai", status_code=502, message="bad gateway")
        assert str(err) == "bad gateway"


class TestQuotaExceededError:
    def test_attributes(self):
        from datetime import UTC, datetime

        resets_at = datetime(2026, 1, 2, 0, 0, tzinfo=UTC)
        err = QuotaExceededError(
            scope="openai/flagship", current=250_000, limit=250_000, resets_at=resets_at
        )
        assert err.scope == "openai/flagship"
        assert err.current == 250_000
        assert err.limit == 250_000
        assert err.resets_at == resets_at
        assert isinstance(err, LLMRotatorError)

    def test_resets_at_optional(self):
        err = QuotaExceededError(scope="s", current=10, limit=10)
        assert err.resets_at is None

    def test_message_contains_details(self):
        err = QuotaExceededError(scope="openai/flagship", current=248_000, limit=250_000)
        msg = str(err)
        assert "openai/flagship" in msg
        assert "248000" in msg or "248_000" in msg
        assert "250000" in msg or "250_000" in msg


class TestAllAttemptsFailedError:
    def test_is_exception_group(self):
        errors = [
            KeyDeadError(key_alias="k1", status_code=402),
            ModelRateLimitError(key_alias="k2", model="gpt-4o", retry_after=60),
        ]
        err = AllAttemptsFailedError("All attempts failed", errors)
        assert isinstance(err, ExceptionGroup)
        assert isinstance(err, AllAttemptsFailedError)
        assert len(err.exceptions) == 2

    def test_except_star_works(self):
        errors = [
            KeyDeadError(key_alias="k1", status_code=402),
            ModelRateLimitError(key_alias="k2", model="gpt-4o"),
            ServerError(provider="openai", status_code=500),
        ]
        err = AllAttemptsFailedError("All attempts failed", errors)

        caught_dead = []
        caught_rate = []
        try:
            raise err
        except* KeyDeadError as eg:
            caught_dead.extend(eg.exceptions)
        except* ModelRateLimitError as eg:
            caught_rate.extend(eg.exceptions)
        except* ServerError:
            pass

        assert len(caught_dead) == 1
        assert len(caught_rate) == 1

    def test_derive_returns_same_type(self):
        errors = [
            KeyDeadError(key_alias="k1", status_code=402),
            ServerError(provider="openai", status_code=500),
        ]
        err = AllAttemptsFailedError("All attempts failed", errors)
        derived = err.derive([errors[0]])
        assert isinstance(derived, AllAttemptsFailedError)
        assert len(derived.exceptions) == 1

    def test_message(self):
        errors = [KeyDeadError(key_alias="k1", status_code=402)]
        err = AllAttemptsFailedError("All attempts failed", errors)
        assert "All attempts failed" in str(err)
