"""Tests for Annie SDK exception hierarchy."""

from __future__ import annotations

import pytest

from annie_sdk import (
    AnnieError,
    APIError,
    AuthenticationError,
    ConnectionError,
    RateLimitError,
    TimeoutError,
    ValidationError,
)


class TestAnnieError:
    def test_is_exception(self):
        assert issubclass(AnnieError, Exception)

    def test_message(self):
        err = AnnieError("something broke")
        assert err.message == "something broke"
        assert str(err) == "something broke"

    def test_default_message(self):
        err = AnnieError()
        assert err.message == "An error occurred"


class TestAPIError:
    def test_inherits(self):
        assert issubclass(APIError, AnnieError)

    def test_fields(self):
        err = APIError(
            "server error",
            status_code=500,
            code="INTERNAL",
            response={"detail": "oops"},
        )
        assert err.message == "server error"
        assert err.status_code == 500
        assert err.code == "INTERNAL"
        assert err.response == {"detail": "oops"}

    def test_defaults(self):
        err = APIError()
        assert err.status_code is None
        assert err.code is None
        assert err.response is None


class TestValidationError:
    def test_inherits(self):
        assert issubclass(ValidationError, AnnieError)

    def test_field(self):
        err = ValidationError("bad input", field="email")
        assert err.field == "email"
        assert err.message == "bad input"

    def test_default_field(self):
        err = ValidationError()
        assert err.field is None


class TestConnectionError:
    def test_inherits(self):
        assert issubclass(ConnectionError, AnnieError)

    def test_original_error(self):
        original = OSError("connection refused")
        err = ConnectionError("failed", original_error=original)
        assert err.original_error is original
        assert err.message == "failed"

    def test_default_original_error(self):
        err = ConnectionError()
        assert err.original_error is None


class TestAuthenticationError:
    def test_inherits(self):
        assert issubclass(AuthenticationError, AnnieError)

    def test_default_message(self):
        err = AuthenticationError()
        assert err.message == "Authentication failed"


class TestTimeoutError:
    def test_inherits(self):
        assert issubclass(TimeoutError, AnnieError)

    def test_default_message(self):
        err = TimeoutError()
        assert err.message == "Request timed out"


class TestRateLimitError:
    def test_inherits(self):
        assert issubclass(RateLimitError, AnnieError)

    def test_retry_after(self):
        err = RateLimitError("slow down", retry_after=30)
        assert err.retry_after == 30

    def test_default_retry_after(self):
        err = RateLimitError()
        assert err.retry_after is None


class TestAllExceptionsInherit:
    @pytest.mark.parametrize(
        "exc_class",
        [
            APIError,
            ValidationError,
            ConnectionError,
            AuthenticationError,
            TimeoutError,
            RateLimitError,
        ],
    )
    def test_inherits_from_annie_error(self, exc_class):
        assert issubclass(exc_class, AnnieError)

    @pytest.mark.parametrize(
        "exc_class",
        [
            APIError,
            ValidationError,
            ConnectionError,
            AuthenticationError,
            TimeoutError,
            RateLimitError,
        ],
    )
    def test_catchable_as_annie_error(self, exc_class):
        with pytest.raises(AnnieError):
            raise exc_class()
