"""Unit tests for Collections namespace."""

import pytest
import respx

from httpx import Response

from pinecone._internal.http import AsyncHTTPClient, HTTPClient
from pinecone.async_client.collections import AsyncCollections
from pinecone.client.collections import Collections


class TestCollections:
    """Test Collections class."""

    def test_constructor_initialization(self):
        """Test Collections constructor initializes correctly."""
        client = HTTPClient(api_key="test-key")
        collections = Collections(client, base_url="https://test.pinecone.io")

        assert collections._http is client
        assert collections._base_url == "https://test.pinecone.io"
        assert collections._adapter is not None

    def test_repr(self):
        """Test Collections __repr__ shows base_url."""
        client = HTTPClient(api_key="test-key")
        collections = Collections(client, base_url="https://test.pinecone.io")

        repr_str = repr(collections)
        assert "Collections" in repr_str
        assert "https://test.pinecone.io" in repr_str

    def test_dir_filters_private_attributes(self):
        """Test Collections __dir__ filters out private attributes."""
        client = HTTPClient(api_key="test-key")
        collections = Collections(client)

        public_attrs = dir(collections)

        # Should include public methods
        assert "list" in public_attrs
        assert "create" in public_attrs
        assert "delete" in public_attrs
        assert "describe" in public_attrs

        # Should NOT include private attributes
        assert "_http" not in public_attrs
        assert "_base_url" not in public_attrs
        assert "_adapter" not in public_attrs

    @respx.mock
    def test_list_collections_success(self):
        """Test successful list collections operation."""
        mock_response = {
            "collections": [
                {
                    "name": "test-collection",
                    "status": "Ready",
                    "environment": "us-east-1-aws",
                    "size": 3081918,
                    "vector_count": 99,
                    "dimension": 3,
                }
            ]
        }

        route = respx.get("https://api.pinecone.io/collections").mock(
            return_value=Response(200, json=mock_response)
        )

        client = HTTPClient(api_key="test-key")
        collections = Collections(client)

        paginator = collections.list()
        items = list(paginator)

        # Verify request
        assert route.called
        request = route.calls.last.request
        assert request.headers["X-Pinecone-Api-Version"] == "2025-10"
        assert "Api-Key" in request.headers

        # Verify response
        assert len(items) == 1
        assert items[0].name == "test-collection"
        assert items[0].status == "Ready"
        assert items[0].environment == "us-east-1-aws"
        assert items[0].size == 3081918
        assert items[0].vector_count == 99
        assert items[0].dimension == 3

    @respx.mock
    def test_list_collections_empty(self):
        """Test list with no collections."""
        mock_response = {"collections": []}

        route = respx.get("https://api.pinecone.io/collections").mock(
            return_value=Response(200, json=mock_response)
        )

        client = HTTPClient(api_key="test-key")
        collections = Collections(client)

        paginator = collections.list()
        items = list(paginator)

        assert route.called
        assert len(items) == 0

    @respx.mock
    def test_list_collections_multiple(self):
        """Test list with multiple collections."""
        mock_response = {
            "collections": [
                {
                    "name": "collection-1",
                    "status": "Ready",
                    "environment": "us-east-1-aws",
                    "size": 1000,
                    "vector_count": 10,
                    "dimension": 128,
                },
                {
                    "name": "collection-2",
                    "status": "Initializing",
                    "environment": "us-west-2-aws",
                    "size": 2000,
                    "vector_count": 20,
                    "dimension": 256,
                },
            ]
        }

        route = respx.get("https://api.pinecone.io/collections").mock(
            return_value=Response(200, json=mock_response)
        )

        client = HTTPClient(api_key="test-key")
        collections = Collections(client)

        paginator = collections.list()
        items = list(paginator)

        assert route.called
        assert len(items) == 2
        assert items[0].name == "collection-1"
        assert items[1].name == "collection-2"

    @respx.mock
    def test_list_collections_with_limit(self):
        """Test list respects limit parameter."""
        mock_response = {
            "collections": [
                {
                    "name": f"collection-{i}",
                    "status": "Ready",
                    "environment": "us-east-1-aws",
                    "size": 1000,
                    "vector_count": 10,
                    "dimension": 128,
                }
                for i in range(5)
            ]
        }

        route = respx.get("https://api.pinecone.io/collections").mock(
            return_value=Response(200, json=mock_response)
        )

        client = HTTPClient(api_key="test-key")
        collections = Collections(client)

        paginator = collections.list(limit=3)
        items = list(paginator)

        assert route.called
        assert len(items) == 3

    @respx.mock
    def test_create_collection_success(self):
        """Test successful create collection operation."""
        mock_response = {
            "name": "test-collection",
            "status": "Initializing",
            "environment": "us-east-1-aws",
            "size": 0,
            "vector_count": 0,
            "dimension": 128,
        }

        route = respx.post("https://api.pinecone.io/collections").mock(
            return_value=Response(201, json=mock_response)
        )

        client = HTTPClient(api_key="test-key")
        collections = Collections(client)

        # Should not raise - returns None
        collections.create(name="test-collection", source="test-index")

        # Verify request
        assert route.called
        request = route.calls.last.request
        assert request.headers["X-Pinecone-Api-Version"] == "2025-10"
        assert request.headers["Content-Type"] == "application/json"
        assert "Api-Key" in request.headers

        # Verify request body
        import orjson

        body = orjson.loads(request.content)
        assert body["name"] == "test-collection"
        assert body["source"] == "test-index"

    @respx.mock
    def test_delete_collection_success(self):
        """Test successful delete collection operation."""
        route = respx.delete("https://api.pinecone.io/collections/test-collection").mock(
            return_value=Response(202)
        )

        client = HTTPClient(api_key="test-key")
        collections = Collections(client)

        # Should not raise - returns None
        collections.delete("test-collection")

        # Verify request
        assert route.called
        request = route.calls.last.request
        assert request.headers["X-Pinecone-Api-Version"] == "2025-10"
        assert "Api-Key" in request.headers

    @respx.mock
    def test_describe_collection_success(self):
        """Test successful describe collection operation."""
        mock_response = {
            "name": "test-collection",
            "status": "Ready",
            "environment": "us-east-1-aws",
            "size": 3081918,
            "vector_count": 99,
            "dimension": 128,
        }

        route = respx.get("https://api.pinecone.io/collections/test-collection").mock(
            return_value=Response(200, json=mock_response)
        )

        client = HTTPClient(api_key="test-key")
        collections = Collections(client)

        result = collections.describe("test-collection")

        # Verify request
        assert route.called
        request = route.calls.last.request
        assert request.headers["X-Pinecone-Api-Version"] == "2025-10"
        assert "Api-Key" in request.headers

        # Verify response
        assert result.name == "test-collection"
        assert result.status == "Ready"
        assert result.environment == "us-east-1-aws"
        assert result.size == 3081918
        assert result.vector_count == 99
        assert result.dimension == 128

    def test_create_validates_name(self):
        """Test create validates name parameter is not empty."""
        client = HTTPClient(api_key="test-key")
        collections = Collections(client)

        with pytest.raises(ValueError, match="name"):
            collections.create(name="", source="test-index")

    def test_create_validates_source(self):
        """Test create validates source parameter is not empty."""
        client = HTTPClient(api_key="test-key")
        collections = Collections(client)

        with pytest.raises(ValueError, match="source"):
            collections.create(name="test", source="")

    def test_delete_validates_name(self):
        """Test delete validates name parameter is not empty."""
        client = HTTPClient(api_key="test-key")
        collections = Collections(client)

        with pytest.raises(ValueError, match="name"):
            collections.delete("")

    def test_describe_validates_name(self):
        """Test describe validates name parameter is not empty."""
        client = HTTPClient(api_key="test-key")
        collections = Collections(client)

        with pytest.raises(ValueError, match="name"):
            collections.describe("")

    def test_list_validates_limit_positive(self):
        """Test list validates limit is positive."""
        client = HTTPClient(api_key="test-key")
        collections = Collections(client)

        with pytest.raises(ValueError, match="limit"):
            collections.list(limit=0)

        with pytest.raises(ValueError, match="limit"):
            collections.list(limit=-1)

    def test_create_validates_timeout_positive(self):
        """Test create validates timeout is positive."""
        client = HTTPClient(api_key="test-key")
        collections = Collections(client)

        with pytest.raises(ValueError, match="timeout"):
            collections.create(name="test", source="source", timeout=0)

        with pytest.raises(ValueError, match="timeout"):
            collections.create(name="test", source="source", timeout=-1)

    @respx.mock
    def test_describe_collection_not_found(self):
        """Test describe raises exception when collection not found."""
        mock_response = {
            "status": 404,
            "error": {
                "code": "NOT_FOUND",
                "message": "Collection test-collection not found.",
            },
        }

        route = respx.get("https://api.pinecone.io/collections/test-collection").mock(
            return_value=Response(404, json=mock_response)
        )

        client = HTTPClient(api_key="test-key")
        collections = Collections(client)

        # HTTP client converts 404 to NotFoundError
        from pinecone.exceptions import NotFoundError

        with pytest.raises(NotFoundError):
            collections.describe("test-collection")

        assert route.called

    def test_delete_validates_timeout_positive(self):
        """Test delete validates timeout is positive."""
        client = HTTPClient(api_key="test-key")
        collections = Collections(client)

        with pytest.raises(ValueError, match="timeout"):
            collections.delete("test", timeout=0)

        with pytest.raises(ValueError, match="timeout"):
            collections.delete("test", timeout=-1)


class TestAsyncCollections:
    """Test AsyncCollections class."""

    def test_constructor_initialization(self):
        """Test AsyncCollections constructor initializes correctly."""
        client = AsyncHTTPClient(api_key="test-key")
        collections = AsyncCollections(client, base_url="https://test.pinecone.io")

        assert collections._http is client
        assert collections._base_url == "https://test.pinecone.io"
        assert collections._adapter is not None

    def test_repr(self):
        """Test AsyncCollections __repr__ shows base_url."""
        client = AsyncHTTPClient(api_key="test-key")
        collections = AsyncCollections(client, base_url="https://test.pinecone.io")

        repr_str = repr(collections)
        assert "AsyncCollections" in repr_str
        assert "https://test.pinecone.io" in repr_str

    def test_dir_filters_private_attributes(self):
        """Test AsyncCollections __dir__ filters out private attributes."""
        client = AsyncHTTPClient(api_key="test-key")
        collections = AsyncCollections(client)

        public_attrs = dir(collections)

        # Should include public methods
        assert "list" in public_attrs
        assert "create" in public_attrs
        assert "delete" in public_attrs
        assert "describe" in public_attrs

        # Should NOT include private attributes
        assert "_http" not in public_attrs
        assert "_base_url" not in public_attrs
        assert "_adapter" not in public_attrs

    @respx.mock
    @pytest.mark.asyncio()
    async def test_list_collections_success(self):
        """Test successful list collections operation (async).

        Note: list() is not async - it returns an AsyncPaginator which is async-iterable.
        """
        mock_response = {
            "collections": [
                {
                    "name": "test-collection",
                    "status": "Ready",
                    "environment": "us-east-1-aws",
                    "size": 3081918,
                    "vector_count": 99,
                    "dimension": 3,
                }
            ]
        }

        route = respx.get("https://api.pinecone.io/collections").mock(
            return_value=Response(200, json=mock_response)
        )

        client = AsyncHTTPClient(api_key="test-key")
        collections = AsyncCollections(client)

        paginator = collections.list()
        items = []
        async for item in paginator:
            items.append(item)

        # Verify request
        assert route.called
        request = route.calls.last.request
        assert request.headers["X-Pinecone-Api-Version"] == "2025-10"
        assert "Api-Key" in request.headers

        # Verify response
        assert len(items) == 1
        assert items[0].name == "test-collection"
        assert items[0].status == "Ready"
        assert items[0].environment == "us-east-1-aws"
        assert items[0].size == 3081918
        assert items[0].vector_count == 99
        assert items[0].dimension == 3

    @respx.mock
    @pytest.mark.asyncio()
    async def test_list_collections_empty(self):
        """Test list with no collections (async)."""
        mock_response = {"collections": []}

        route = respx.get("https://api.pinecone.io/collections").mock(
            return_value=Response(200, json=mock_response)
        )

        client = AsyncHTTPClient(api_key="test-key")
        collections = AsyncCollections(client)

        paginator = collections.list()
        items = []
        async for item in paginator:
            items.append(item)

        assert route.called
        assert len(items) == 0

    @respx.mock
    @pytest.mark.asyncio()
    async def test_list_collections_multiple(self):
        """Test list with multiple collections (async)."""
        mock_response = {
            "collections": [
                {
                    "name": "collection-1",
                    "status": "Ready",
                    "environment": "us-east-1-aws",
                    "size": 1000,
                    "vector_count": 10,
                    "dimension": 128,
                },
                {
                    "name": "collection-2",
                    "status": "Initializing",
                    "environment": "us-west-2-aws",
                    "size": 2000,
                    "vector_count": 20,
                    "dimension": 256,
                },
            ]
        }

        route = respx.get("https://api.pinecone.io/collections").mock(
            return_value=Response(200, json=mock_response)
        )

        client = AsyncHTTPClient(api_key="test-key")
        collections = AsyncCollections(client)

        paginator = collections.list()
        items = []
        async for item in paginator:
            items.append(item)

        assert route.called
        assert len(items) == 2
        assert items[0].name == "collection-1"
        assert items[1].name == "collection-2"

    @respx.mock
    @pytest.mark.asyncio()
    async def test_list_collections_with_limit(self):
        """Test list respects limit parameter (async)."""
        mock_response = {
            "collections": [
                {
                    "name": f"collection-{i}",
                    "status": "Ready",
                    "environment": "us-east-1-aws",
                    "size": 1000,
                    "vector_count": 10,
                    "dimension": 128,
                }
                for i in range(5)
            ]
        }

        route = respx.get("https://api.pinecone.io/collections").mock(
            return_value=Response(200, json=mock_response)
        )

        client = AsyncHTTPClient(api_key="test-key")
        collections = AsyncCollections(client)

        paginator = collections.list(limit=3)
        items = []
        async for item in paginator:
            items.append(item)

        assert route.called
        assert len(items) == 3

    @respx.mock
    @pytest.mark.asyncio()
    async def test_create_collection_success(self):
        """Test successful async create collection operation."""
        mock_response = {
            "name": "test-collection",
            "status": "Initializing",
            "environment": "us-east-1-aws",
            "size": 0,
            "vector_count": 0,
            "dimension": 128,
        }

        route = respx.post("https://api.pinecone.io/collections").mock(
            return_value=Response(201, json=mock_response)
        )

        client = AsyncHTTPClient(api_key="test-key")
        collections = AsyncCollections(client)

        # Should not raise - returns None
        await collections.create(name="test-collection", source="test-index")

        # Verify request
        assert route.called
        request = route.calls.last.request
        assert request.headers["X-Pinecone-Api-Version"] == "2025-10"
        assert request.headers["Content-Type"] == "application/json"
        assert "Api-Key" in request.headers

        # Verify request body
        import orjson

        body = orjson.loads(request.content)
        assert body["name"] == "test-collection"
        assert body["source"] == "test-index"

    @respx.mock
    @pytest.mark.asyncio()
    async def test_delete_collection_success(self):
        """Test successful async delete collection operation."""
        route = respx.delete("https://api.pinecone.io/collections/test-collection").mock(
            return_value=Response(202)
        )

        client = AsyncHTTPClient(api_key="test-key")
        collections = AsyncCollections(client)

        # Should not raise - returns None
        await collections.delete("test-collection")

        # Verify request
        assert route.called
        request = route.calls.last.request
        assert request.headers["X-Pinecone-Api-Version"] == "2025-10"
        assert "Api-Key" in request.headers

    @respx.mock
    @pytest.mark.asyncio()
    async def test_describe_collection_success(self):
        """Test successful async describe collection operation."""
        mock_response = {
            "name": "test-collection",
            "status": "Ready",
            "environment": "us-east-1-aws",
            "size": 3081918,
            "vector_count": 99,
            "dimension": 128,
        }

        route = respx.get("https://api.pinecone.io/collections/test-collection").mock(
            return_value=Response(200, json=mock_response)
        )

        client = AsyncHTTPClient(api_key="test-key")
        collections = AsyncCollections(client)

        result = await collections.describe("test-collection")

        # Verify request
        assert route.called
        request = route.calls.last.request
        assert request.headers["X-Pinecone-Api-Version"] == "2025-10"
        assert "Api-Key" in request.headers

        # Verify response
        assert result.name == "test-collection"
        assert result.status == "Ready"
        assert result.environment == "us-east-1-aws"
        assert result.size == 3081918
        assert result.vector_count == 99
        assert result.dimension == 128

    @pytest.mark.asyncio()
    async def test_create_validates_name(self):
        """Test async create validates name parameter is not empty."""
        client = AsyncHTTPClient(api_key="test-key")
        collections = AsyncCollections(client)

        with pytest.raises(ValueError, match="name"):
            await collections.create(name="", source="test-index")

    @pytest.mark.asyncio()
    async def test_create_validates_source(self):
        """Test async create validates source parameter is not empty."""
        client = AsyncHTTPClient(api_key="test-key")
        collections = AsyncCollections(client)

        with pytest.raises(ValueError, match="source"):
            await collections.create(name="test", source="")

    @pytest.mark.asyncio()
    async def test_delete_validates_name(self):
        """Test async delete validates name parameter is not empty."""
        client = AsyncHTTPClient(api_key="test-key")
        collections = AsyncCollections(client)

        with pytest.raises(ValueError, match="name"):
            await collections.delete("")

    @pytest.mark.asyncio()
    async def test_describe_validates_name(self):
        """Test async describe validates name parameter is not empty."""
        client = AsyncHTTPClient(api_key="test-key")
        collections = AsyncCollections(client)

        with pytest.raises(ValueError, match="name"):
            await collections.describe("")

    def test_list_validates_limit_positive(self):
        """Test list validates limit is positive (not async since list() is not async)."""
        client = AsyncHTTPClient(api_key="test-key")
        collections = AsyncCollections(client)

        with pytest.raises(ValueError, match="limit"):
            collections.list(limit=0)

        with pytest.raises(ValueError, match="limit"):
            collections.list(limit=-1)

    @pytest.mark.asyncio()
    async def test_create_validates_timeout_positive(self):
        """Test async create validates timeout is positive."""
        client = AsyncHTTPClient(api_key="test-key")
        collections = AsyncCollections(client)

        with pytest.raises(ValueError, match="timeout"):
            await collections.create(name="test", source="source", timeout=0)

        with pytest.raises(ValueError, match="timeout"):
            await collections.create(name="test", source="source", timeout=-1)

    @pytest.mark.asyncio()
    async def test_delete_validates_timeout_positive(self):
        """Test async delete validates timeout is positive."""
        client = AsyncHTTPClient(api_key="test-key")
        collections = AsyncCollections(client)

        with pytest.raises(ValueError, match="timeout"):
            await collections.delete("test", timeout=0)

        with pytest.raises(ValueError, match="timeout"):
            await collections.delete("test", timeout=-1)

    @respx.mock
    def test_list_handles_null_pagination(self):
        """Test list handles API response with null pagination field."""
        mock_response = {
            "collections": [
                {
                    "name": "test-collection",
                    "size": 1000,
                    "status": "Ready",
                    "dimension": 384,
                    "vector_count": 100,
                    "environment": "us-east-1-aws",
                }
            ],
            "pagination": None,  # API returns null
        }

        respx.get("https://api.pinecone.io/collections").mock(
            return_value=Response(200, json=mock_response)
        )

        client = HTTPClient(api_key="test-key")
        collections = Collections(client)

        # Should not crash with AttributeError
        result = list(collections.list())
        assert len(result) == 1
        assert result[0].name == "test-collection"

    @respx.mock
    @pytest.mark.asyncio()
    async def test_async_list_handles_null_pagination(self):
        """Test async list handles API response with null pagination field."""
        mock_response = {
            "collections": [
                {
                    "name": "test-collection",
                    "size": 1000,
                    "status": "Ready",
                    "dimension": 384,
                    "vector_count": 100,
                    "environment": "us-east-1-aws",
                }
            ],
            "pagination": None,  # API returns null
        }

        respx.get("https://api.pinecone.io/collections").mock(
            return_value=Response(200, json=mock_response)
        )

        client = AsyncHTTPClient(api_key="test-key")
        collections = AsyncCollections(client)

        # Should not crash with AttributeError
        result = [item async for item in collections.list()]
        assert len(result) == 1
        assert result[0].name == "test-collection"
