"""Tests for the exception hierarchy."""

from __future__ import annotations

import pytest

from presentations_ai import (
    APIConnectionError,
    APIError,
    APITimeoutError,
    AuthenticationError,
    BadRequestError,
    CloudyError,
    InsufficientCreditsError,
    InternalServerError,
    NotFoundError,
    PresentationsAIError,
    RateLimitError,
)


class TestExceptionHierarchy:
    """Test that exceptions follow the correct inheritance chain."""

    def test_base_error_is_exception(self) -> None:
        assert issubclass(PresentationsAIError, Exception)

    def test_api_error_inherits_from_base(self) -> None:
        assert issubclass(APIError, PresentationsAIError)

    def test_http_errors_inherit_from_api_error(self) -> None:
        assert issubclass(BadRequestError, APIError)
        assert issubclass(AuthenticationError, APIError)
        assert issubclass(InsufficientCreditsError, APIError)
        assert issubclass(NotFoundError, APIError)
        assert issubclass(RateLimitError, APIError)
        assert issubclass(InternalServerError, APIError)

    def test_transport_errors_inherit_from_base(self) -> None:
        assert issubclass(APITimeoutError, PresentationsAIError)
        assert issubclass(APIConnectionError, PresentationsAIError)

    def test_cloudy_error_inherits_from_base(self) -> None:
        assert issubclass(CloudyError, PresentationsAIError)

    def test_catch_all_api_errors(self) -> None:
        """except APIError catches all HTTP status errors."""
        with pytest.raises(APIError):
            raise BadRequestError("bad", status_code=400)

        with pytest.raises(APIError):
            raise RateLimitError("rate", status_code=429)

    def test_catch_all_sdk_errors(self) -> None:
        """except PresentationsAIError catches everything."""
        with pytest.raises(PresentationsAIError):
            raise AuthenticationError("auth", status_code=401)

        with pytest.raises(PresentationsAIError):
            raise APITimeoutError("timeout")

        with pytest.raises(PresentationsAIError):
            raise CloudyError("cloudy")


class TestExceptionAttributes:
    """Test that exceptions carry structured error data."""

    def test_base_error_attributes(self) -> None:
        e = PresentationsAIError("something failed", code="TEST_CODE", remediation="try again")
        assert e.message == "something failed"
        assert e.code == "TEST_CODE"
        assert e.remediation == "try again"
        assert str(e) == "something failed"

    def test_api_error_attributes(self) -> None:
        body = {"error": "invalid request"}
        e = APIError(
            "bad request",
            status_code=400,
            code="API_VALIDATION_FAILED",
            remediation="fix your params",
            body=body,
        )
        assert e.status_code == 400
        assert e.code == "API_VALIDATION_FAILED"
        assert e.body == body
        assert "400" in repr(e)

    def test_connection_error_cause(self) -> None:
        cause = ConnectionError("DNS failed")
        e = APIConnectionError("cannot connect", cause=cause)
        assert e.cause is cause

    def test_base_error_defaults(self) -> None:
        e = PresentationsAIError("oops")
        assert e.code is None
        assert e.remediation is None
