"""API surface tests.

These tests verify the public API surface matches expectations.
Failures indicate potential breaking changes that need documentation.
"""

from __future__ import annotations

import pinecone

from pinecone import (
    APIConnectionError,
    APIError,
    AsyncHTTPClient,
    BadRequestError,
    ConfigurationError,
    ForbiddenError,
    HTTPClient,
    HTTPConfig,
    NotFoundError,
    PineconeError,
    RateLimitError,
    ServerError,
    TimeoutError as PineconeTimeoutError,
    UnauthorizedError,
)


class TestPublicExports:
    """Test public exports from the pinecone package."""

    def test_version_is_exported(self):
        """Test that __version__ is exported."""
        assert hasattr(pinecone, "__version__")
        assert isinstance(pinecone.__version__, str)
        # Version should follow semver format
        parts = pinecone.__version__.split(".")
        assert len(parts) >= 2, "Version should have at least major.minor"

    def test_all_exports_are_accessible(self):
        """Test that all items in __all__ are importable."""
        for name in pinecone.__all__:
            assert hasattr(pinecone, name), f"{name} in __all__ but not accessible"

    def test_expected_exceptions_exported(self):
        """Test that expected exception classes are exported."""
        expected_exceptions = [
            "PineconeError",
            "APIError",
            "APIConnectionError",
            "BadRequestError",
            "UnauthorizedError",
            "ForbiddenError",
            "NotFoundError",
            "RateLimitError",
            "ServerError",
            "TimeoutError",
            "ConfigurationError",
        ]

        for exc_name in expected_exceptions:
            assert hasattr(pinecone, exc_name), f"Expected exception {exc_name} not exported"
            exc_class = getattr(pinecone, exc_name)
            assert isinstance(exc_class, type), f"{exc_name} should be a class"
            assert issubclass(exc_class, Exception), f"{exc_name} should be an Exception subclass"

    def test_http_client_classes_exported(self):
        """Test that HTTP client classes are exported."""
        assert hasattr(pinecone, "HTTPClient")
        assert hasattr(pinecone, "AsyncHTTPClient")
        assert hasattr(pinecone, "HTTPConfig")

    def test_no_private_exports(self):
        """Test that __all__ doesn't contain private names."""
        for name in pinecone.__all__:
            # Allow __version__ as special case
            if name == "__version__":
                continue
            assert not name.startswith("_"), f"Private name {name} in __all__"


class TestExceptionHierarchy:
    """Test exception class hierarchy."""

    def test_all_exceptions_inherit_from_pinecone_error(self):
        """Test all custom exceptions inherit from PineconeError."""
        exceptions = [
            APIError,
            APIConnectionError,
            BadRequestError,
            UnauthorizedError,
            ForbiddenError,
            NotFoundError,
            RateLimitError,
            ServerError,
            PineconeTimeoutError,
            ConfigurationError,
        ]

        for exc in exceptions:
            assert issubclass(exc, PineconeError), (
                f"{exc.__name__} should inherit from PineconeError"
            )

    def test_api_errors_have_status_code_attribute(self):
        """Test API error instances have status_code attribute.

        Each HTTP error class should have a status_code attribute with the
        correct HTTP status code value when instantiated.
        """
        # Test that instances have correct status_code attribute
        test_cases = [
            (BadRequestError("test"), 400),
            (UnauthorizedError(), 401),
            (ForbiddenError("test"), 403),
            (NotFoundError("test"), 404),
            (RateLimitError(), 429),
            (ServerError("test", 500), 500),
            (ServerError("test", 503), 503),
        ]

        for exc, expected_code in test_cases:
            assert hasattr(exc, "status_code"), f"{type(exc).__name__} missing status_code"
            assert exc.status_code == expected_code, (
                f"{type(exc).__name__}.status_code = {exc.status_code}, expected {expected_code}"
            )


class TestHTTPClientInterface:
    """Test HTTPClient public interface."""

    def test_http_client_has_expected_methods(self):
        """Test HTTPClient has expected HTTP methods."""
        expected_methods = ["get", "post", "put", "delete", "patch", "close"]

        for method_name in expected_methods:
            assert hasattr(HTTPClient, method_name), f"HTTPClient missing {method_name} method"
            method = getattr(HTTPClient, method_name)
            assert callable(method), f"HTTPClient.{method_name} should be callable"

    def test_http_client_context_manager(self):
        """Test HTTPClient implements context manager protocol."""
        assert hasattr(HTTPClient, "__enter__"), "HTTPClient should implement __enter__"
        assert hasattr(HTTPClient, "__exit__"), "HTTPClient should implement __exit__"

    def test_async_http_client_has_expected_methods(self):
        """Test AsyncHTTPClient has expected async HTTP methods."""
        expected_methods = ["get", "post", "put", "delete", "patch", "close"]

        for method_name in expected_methods:
            assert hasattr(AsyncHTTPClient, method_name), (
                f"AsyncHTTPClient missing {method_name} method"
            )

    def test_async_http_client_context_manager(self):
        """Test AsyncHTTPClient implements async context manager protocol."""
        assert hasattr(AsyncHTTPClient, "__aenter__"), "AsyncHTTPClient should implement __aenter__"
        assert hasattr(AsyncHTTPClient, "__aexit__"), "AsyncHTTPClient should implement __aexit__"


class TestHTTPConfigInterface:
    """Test HTTPConfig public interface."""

    def test_http_config_has_expected_attributes(self):
        """Test HTTPConfig has expected configuration attributes."""
        config = HTTPConfig()

        expected_attrs = [
            "timeout",
            "max_retries",
            "max_connections",
            "http2",
        ]

        for attr_name in expected_attrs:
            assert hasattr(config, attr_name), f"HTTPConfig missing {attr_name} attribute"

    def test_http_config_default_values(self):
        """Test HTTPConfig has sensible defaults."""
        config = HTTPConfig()

        # Verify defaults exist and are reasonable
        assert config.timeout > 0, "timeout should be positive"
        assert config.max_retries >= 0, "max_retries should be non-negative"
        assert config.max_connections > 0, "max_connections should be positive"
        assert isinstance(config.http2, bool), "http2 should be boolean"
