"""Unit tests for AsyncNamespaces sub-namespace operations."""

from __future__ import annotations

import pytest
import respx

from httpx import Response

from pinecone._internal.http import AsyncHTTPClient, HTTPConfig
from pinecone.async_index.namespaces import AsyncNamespaces
from pinecone.models.namespaces.namespace import FieldConfig


pytestmark = pytest.mark.unit

BASE_URL = "https://my-index-abc123.svc.pinecone.io"


def _make_namespaces() -> AsyncNamespaces:
    return AsyncNamespaces(
        http=AsyncHTTPClient(api_key="test-key", config=HTTPConfig()),
        base_url=BASE_URL,
    )


class TestAsyncCreate:
    """Tests for AsyncNamespaces.create()."""

    @respx.mock
    @pytest.mark.asyncio()
    async def test_create_basic(self) -> None:
        route = respx.post(f"{BASE_URL}/namespaces").mock(
            return_value=Response(
                200,
                json={"name": "my-ns", "record_count": 0},
            )
        )

        ns = _make_namespaces()
        result = await ns.create(name="my-ns")

        assert route.called
        request = route.calls.last.request
        assert b'"name"' in request.content
        assert b'"my-ns"' in request.content
        assert result.name == "my-ns"
        assert result.record_count == 0

    @respx.mock
    @pytest.mark.asyncio()
    async def test_create_with_schema(self) -> None:
        route = respx.post(f"{BASE_URL}/namespaces").mock(
            return_value=Response(
                200,
                json={
                    "name": "my-ns",
                    "record_count": 0,
                    "schema": {"fields": {"category": {"filterable": True}}},
                },
            )
        )

        ns = _make_namespaces()
        result = await ns.create(
            name="my-ns",
            schema={"category": {"filterable": True}},
        )

        assert route.called
        assert result.schema is not None
        assert result.schema.fields["category"].filterable is True

    @respx.mock
    @pytest.mark.asyncio()
    async def test_create_with_field_config_schema(self) -> None:
        route = respx.post(f"{BASE_URL}/namespaces").mock(
            return_value=Response(
                200,
                json={
                    "name": "my-ns",
                    "record_count": 0,
                    "schema": {"fields": {"category": {"filterable": True}}},
                },
            )
        )

        ns = _make_namespaces()
        result = await ns.create(
            name="my-ns",
            schema={"category": FieldConfig(filterable=True)},
        )

        assert route.called
        request = route.calls.last.request
        assert b'"schema"' in request.content
        assert b'"filterable"' in request.content
        assert result.name == "my-ns"
        assert result.schema is not None
        assert result.schema.fields["category"].filterable is True

    @respx.mock
    @pytest.mark.asyncio()
    async def test_create_sends_api_version_header(self) -> None:
        route = respx.post(f"{BASE_URL}/namespaces").mock(
            return_value=Response(200, json={"name": "my-ns", "record_count": 0})
        )

        ns = _make_namespaces()
        await ns.create(name="my-ns")

        request = route.calls.last.request
        assert request.headers["X-Pinecone-Api-Version"] == "2025-10"

    @pytest.mark.asyncio()
    async def test_create_empty_name_raises(self) -> None:
        ns = _make_namespaces()
        with pytest.raises(ValueError, match="name"):
            await ns.create(name="")


class TestAsyncList:
    """Tests for AsyncNamespaces.list()."""

    @respx.mock
    @pytest.mark.asyncio()
    async def test_list_basic(self) -> None:
        route = respx.get(f"{BASE_URL}/namespaces").mock(
            return_value=Response(
                200,
                json={
                    "namespaces": [
                        {"name": "ns1", "record_count": 100},
                        {"name": "ns2", "record_count": 200},
                    ],
                    "total_count": 2,
                },
            )
        )

        ns = _make_namespaces()
        result = await ns.list()

        assert route.called
        assert len(result.namespaces) == 2
        assert result.namespaces[0].name == "ns1"
        assert result.namespaces[0].record_count == 100
        assert result.namespaces[1].name == "ns2"
        assert result.namespaces[1].record_count == 200
        assert result.total_count == 2

    @respx.mock
    @pytest.mark.asyncio()
    async def test_list_with_prefix(self) -> None:
        route = respx.get(f"{BASE_URL}/namespaces").mock(
            return_value=Response(
                200,
                json={
                    "namespaces": [{"name": "prod-ns1", "record_count": 50}],
                    "total_count": 1,
                },
            )
        )

        ns = _make_namespaces()
        await ns.list(prefix="prod-")

        request = route.calls.last.request
        assert "prefix=prod-" in str(request.url)

    @respx.mock
    @pytest.mark.asyncio()
    async def test_list_with_limit(self) -> None:
        route = respx.get(f"{BASE_URL}/namespaces").mock(
            return_value=Response(
                200,
                json={"namespaces": [{"name": "ns1", "record_count": 10}], "total_count": 5},
            )
        )

        ns = _make_namespaces()
        await ns.list(limit=1)

        request = route.calls.last.request
        assert "limit=1" in str(request.url)

    @respx.mock
    @pytest.mark.asyncio()
    async def test_list_with_pagination(self) -> None:
        respx.get(f"{BASE_URL}/namespaces").mock(
            return_value=Response(
                200,
                json={
                    "namespaces": [{"name": "ns1", "record_count": 10}],
                    "pagination": {"next": "token-abc"},
                    "total_count": 5,
                },
            )
        )

        ns = _make_namespaces()
        result = await ns.list(limit=1)

        assert result.pagination_token == "token-abc"

    @respx.mock
    @pytest.mark.asyncio()
    async def test_list_with_pagination_token_param(self) -> None:
        route = respx.get(f"{BASE_URL}/namespaces").mock(
            return_value=Response(
                200,
                json={"namespaces": [], "total_count": 0},
            )
        )

        ns = _make_namespaces()
        await ns.list(pagination_token="token-abc")

        request = route.calls.last.request
        assert "paginationToken=token-abc" in str(request.url)

    @respx.mock
    @pytest.mark.asyncio()
    async def test_list_no_pagination_token(self) -> None:
        respx.get(f"{BASE_URL}/namespaces").mock(
            return_value=Response(
                200,
                json={"namespaces": [], "total_count": 0},
            )
        )

        ns = _make_namespaces()
        result = await ns.list()

        assert result.pagination_token is None


class TestAsyncDescribe:
    """Tests for AsyncNamespaces.describe()."""

    @respx.mock
    @pytest.mark.asyncio()
    async def test_describe_basic(self) -> None:
        route = respx.get(f"{BASE_URL}/namespaces/my-ns").mock(
            return_value=Response(
                200,
                json={"name": "my-ns", "record_count": 42},
            )
        )

        ns = _make_namespaces()
        result = await ns.describe(name="my-ns")

        assert route.called
        assert result.name == "my-ns"
        assert result.record_count == 42

    @respx.mock
    @pytest.mark.asyncio()
    async def test_describe_with_schema(self) -> None:
        respx.get(f"{BASE_URL}/namespaces/my-ns").mock(
            return_value=Response(
                200,
                json={
                    "name": "my-ns",
                    "record_count": 42,
                    "schema": {"fields": {"category": {"filterable": True}}},
                },
            )
        )

        ns = _make_namespaces()
        result = await ns.describe(name="my-ns")

        assert result.schema is not None
        assert result.schema.fields["category"].filterable is True

    @respx.mock
    @pytest.mark.asyncio()
    async def test_describe_with_indexed_fields(self) -> None:
        respx.get(f"{BASE_URL}/namespaces/my-ns").mock(
            return_value=Response(
                200,
                json={
                    "name": "my-ns",
                    "record_count": 42,
                    "indexed_fields": {"fields": ["field1", "field2"]},
                },
            )
        )

        ns = _make_namespaces()
        result = await ns.describe(name="my-ns")

        assert result.indexed_fields is not None
        assert result.indexed_fields.fields == ["field1", "field2"]

    @pytest.mark.asyncio()
    async def test_describe_empty_name_raises(self) -> None:
        ns = _make_namespaces()
        with pytest.raises(ValueError, match="name"):
            await ns.describe(name="")


class TestAsyncDelete:
    """Tests for AsyncNamespaces.delete()."""

    @respx.mock
    @pytest.mark.asyncio()
    async def test_delete_basic(self) -> None:
        route = respx.delete(f"{BASE_URL}/namespaces/my-ns").mock(
            return_value=Response(200, json={})
        )

        ns = _make_namespaces()
        result = await ns.delete(name="my-ns")

        assert route.called
        assert result is not None

    @respx.mock
    @pytest.mark.asyncio()
    async def test_delete_sends_api_version_header(self) -> None:
        route = respx.delete(f"{BASE_URL}/namespaces/my-ns").mock(
            return_value=Response(200, json={})
        )

        ns = _make_namespaces()
        await ns.delete(name="my-ns")

        request = route.calls.last.request
        assert request.headers["X-Pinecone-Api-Version"] == "2025-10"

    @pytest.mark.asyncio()
    async def test_delete_empty_name_raises(self) -> None:
        ns = _make_namespaces()
        with pytest.raises(ValueError, match="name"):
            await ns.delete(name="")


class TestAsyncModelRepr:
    """Tests for model __repr__ methods (via async client)."""

    def test_async_namespaces_repr(self) -> None:
        ns = _make_namespaces()
        assert "AsyncNamespaces" in repr(ns)
        assert BASE_URL in repr(ns)
