"""Integration tests for Documents API (sync).

These tests require a valid Pinecone API key and a documents-enabled index.

Setup:
    export PINECONE_API_KEY="your-api-key"
    export PINECONE_DOCUMENTS_INDEX_HOST="your-document-index-host"
    pytest tests/integration/test_documents.py -v

Note: Integration tests make real API calls and may incur costs.
"""

from __future__ import annotations

import os

from typing import TYPE_CHECKING

import pytest


if TYPE_CHECKING:
    from pinecone import Index, Pinecone


pytestmark = pytest.mark.integration


@pytest.fixture()
def documents_index_host() -> str:
    """Get documents index host from environment.

    Returns:
        Documents index host URL.

    Raises:
        pytest.skip: If PINECONE_DOCUMENTS_INDEX_HOST is not set.
    """
    host = os.getenv("PINECONE_DOCUMENTS_INDEX_HOST")
    if not host:
        pytest.skip("PINECONE_DOCUMENTS_INDEX_HOST environment variable not set")
    return host


@pytest.fixture()
def documents_index(integration_client: Pinecone, documents_index_host: str) -> Index:
    """Create an index instance for documents operations.

    Args:
        integration_client: The Pinecone client fixture.
        documents_index_host: The documents index host fixture.

    Returns:
        Index instance configured for documents operations.
    """
    return integration_client.index(host=documents_index_host)


@pytest.fixture()
def test_namespace() -> str:
    """Get test namespace name.

    For dedicated indexes that are restricted to a single namespace,
    this returns the existing namespace. For other indexes, you could
    generate a unique namespace.

    Returns:
        Namespace string for tests.
    """
    # Use the existing namespace since the dedicated index only supports one
    return "my-namespace"


class TestDocumentsUpsertSuccess:
    """Test successful document upsert operations."""

    def test_upsert_single_document(self, documents_index: Index, test_namespace: str) -> None:
        """Test upserting a single document.

        Args:
            documents_index: The documents index fixture.
            test_namespace: Test namespace for isolation.
        """
        response = documents_index.documents.upsert(
            namespace=test_namespace,
            documents=[
                {
                    "_id": "doc1",
                    "my_text_field": "The quick brown fox jumps over the lazy dog",
                }
            ],
        )

        # Verify response structure
        assert response is not None
        assert hasattr(response, "upserted_count")
        assert response.upserted_count == 1

    def test_upsert_multiple_documents(self, documents_index: Index, test_namespace: str) -> None:
        """Test upserting multiple documents.

        Args:
            documents_index: The documents index fixture.
            test_namespace: Test namespace for isolation.
        """
        documents = [
            {
                "_id": "doc1",
                "my_text_field": "Machine learning is a subset of artificial intelligence",
            },
            {
                "_id": "doc2",
                "my_text_field": "Python is a popular programming language",
            },
            {
                "_id": "doc3",
                "my_text_field": "Vector databases store embeddings efficiently",
            },
        ]

        response = documents_index.documents.upsert(
            namespace=test_namespace,
            documents=documents,
        )

        # Verify correct number of documents upserted
        assert response.upserted_count == len(documents)

    def test_upsert_with_metadata_fields(self, documents_index: Index, test_namespace: str) -> None:
        """Test upserting documents with additional metadata fields.

        Args:
            documents_index: The documents index fixture.
            test_namespace: Test namespace for isolation.
        """
        response = documents_index.documents.upsert(
            namespace=test_namespace,
            documents=[
                {
                    "_id": "doc1",
                    "my_text_field": "This is a test document",
                    "category": "test",
                    "priority": 1,
                    "active": True,
                },
                {
                    "_id": "doc2",
                    "my_text_field": "Another test document",
                    "category": "test",
                    "priority": 2,
                    "active": False,
                },
            ],
        )

        assert response.upserted_count == 2

    def test_upsert_updates_existing_document(
        self, documents_index: Index, test_namespace: str
    ) -> None:
        """Test that upserting with same _id updates the document.

        Args:
            documents_index: The documents index fixture.
            test_namespace: Test namespace for isolation.
        """
        # First upsert
        documents_index.documents.upsert(
            namespace=test_namespace,
            documents=[{"_id": "doc1", "my_text_field": "Original content"}],
        )

        # Update with same _id
        response = documents_index.documents.upsert(
            namespace=test_namespace,
            documents=[{"_id": "doc1", "my_text_field": "Updated content"}],
        )

        assert response.upserted_count == 1


class TestDocumentsSearchSuccess:
    """Test successful document search operations."""

    def test_text_search(self, documents_index: Index, test_namespace: str) -> None:
        """Test text-based document search.

        Args:
            documents_index: The documents index fixture.
            test_namespace: Test namespace for isolation.
        """
        # First upsert some documents
        documents_index.documents.upsert(
            namespace=test_namespace,
            documents=[
                {
                    "_id": "doc1",
                    "my_text_field": "The quick brown fox jumps over the lazy dog",
                },
                {
                    "_id": "doc2",
                    "my_text_field": "A fast red fox leaps across the sleepy hound",
                },
                {
                    "_id": "doc3",
                    "my_text_field": "The lazy dog slept all afternoon in the sun",
                },
            ],
        )

        # Search with text query
        response = documents_index.documents.search(
            namespace=test_namespace,
            top_k=2,
            score_by=[
                {
                    "type": "text",
                    "field": "my_text_field",
                    "text_query": "lazy dog",
                }
            ],
            include_fields=["_id", "my_text_field"],
        )

        # Verify response structure
        assert response is not None
        assert hasattr(response, "documents")
        assert len(response.documents) > 0

        # Verify document structure
        for doc in response.documents:
            assert hasattr(doc, "id")
            assert hasattr(doc, "score")
            assert doc.id in ["doc1", "doc2", "doc3"]

    def test_vector_search(self, documents_index: Index, test_namespace: str) -> None:
        """Test vector-based document search.

        Args:
            documents_index: The documents index fixture.
            test_namespace: Test namespace for isolation.
        """
        # First upsert documents with vectors
        documents_index.documents.upsert(
            namespace=test_namespace,
            documents=[
                {
                    "_id": "vec1",
                    "my_text_field": "First document",
                    "_values": [0.1, 0.2, 0.3, 0.4, 0.5],
                },
                {
                    "_id": "vec2",
                    "my_text_field": "Second document",
                    "_values": [0.2, 0.3, 0.4, 0.5, 0.6],
                },
            ],
        )

        # Search with vector
        response = documents_index.documents.search(
            namespace=test_namespace,
            top_k=2,
            score_by=[
                {
                    "type": "vector",
                    "field": "_values",
                    "values": [0.15, 0.25, 0.35, 0.45, 0.55],
                }
            ],
            include_fields="*",
        )

        assert response is not None
        assert len(response.documents) > 0

    def test_search_with_filter(self, documents_index: Index, test_namespace: str) -> None:
        """Test document search with metadata filter.

        Args:
            documents_index: The documents index fixture.
            test_namespace: Test namespace for isolation.
        """
        # Upsert documents with metadata
        documents_index.documents.upsert(
            namespace=test_namespace,
            documents=[
                {
                    "_id": "doc1",
                    "my_text_field": "Technology document",
                    "category": "tech",
                },
                {
                    "_id": "doc2",
                    "my_text_field": "Science document",
                    "category": "science",
                },
                {
                    "_id": "doc3",
                    "my_text_field": "Another tech document",
                    "category": "tech",
                },
            ],
        )

        # Search with filter
        response = documents_index.documents.search(
            namespace=test_namespace,
            top_k=10,
            score_by=[
                {
                    "type": "text",
                    "field": "my_text_field",
                    "text_query": "document",
                }
            ],
            filter={"category": {"$eq": "tech"}},
            include_fields=["_id", "category"],
        )

        # Verify only tech documents are returned
        assert len(response.documents) > 0
        for doc in response.documents:
            assert doc.get("category") == "tech"


class TestErrorHandling:
    """Test error handling for invalid inputs."""

    def test_upsert_empty_namespace(self, documents_index: Index) -> None:
        """Test that empty namespace raises ValueError.

        Args:
            documents_index: The documents index fixture.
        """
        with pytest.raises(ValueError, match="namespace"):
            documents_index.documents.upsert(
                namespace="",
                documents=[{"_id": "doc1", "my_text_field": "test"}],
            )

    def test_upsert_empty_documents(self, documents_index: Index, test_namespace: str) -> None:
        """Test that empty documents list raises ValueError.

        Args:
            documents_index: The documents index fixture.
            test_namespace: Test namespace for isolation.
        """
        with pytest.raises(ValueError, match="documents must not be empty"):
            documents_index.documents.upsert(
                namespace=test_namespace,
                documents=[],
            )

    def test_upsert_missing_id_field(self, documents_index: Index, test_namespace: str) -> None:
        """Test that document without _id raises ValueError.

        Args:
            documents_index: The documents index fixture.
            test_namespace: Test namespace for isolation.
        """
        with pytest.raises(ValueError, match="missing required '_id' field"):
            documents_index.documents.upsert(
                namespace=test_namespace,
                documents=[{"my_text_field": "test"}],
            )

    def test_search_empty_namespace(self, documents_index: Index) -> None:
        """Test that empty namespace raises ValueError.

        Args:
            documents_index: The documents index fixture.
        """
        with pytest.raises(ValueError, match="namespace"):
            documents_index.documents.search(
                namespace="",
                top_k=10,
                score_by=[
                    {
                        "type": "text",
                        "field": "my_text_field",
                        "text_query": "test",
                    }
                ],
            )

    def test_search_invalid_top_k_zero(self, documents_index: Index, test_namespace: str) -> None:
        """Test that top_k=0 raises ValueError.

        Args:
            documents_index: The documents index fixture.
            test_namespace: Test namespace for isolation.
        """
        with pytest.raises(ValueError, match="top_k"):
            documents_index.documents.search(
                namespace=test_namespace,
                top_k=0,
                score_by=[
                    {
                        "type": "text",
                        "field": "my_text_field",
                        "text_query": "test",
                    }
                ],
            )

    def test_search_invalid_top_k_too_large(
        self, documents_index: Index, test_namespace: str
    ) -> None:
        """Test that top_k > 10000 raises ValueError.

        Args:
            documents_index: The documents index fixture.
            test_namespace: Test namespace for isolation.
        """
        with pytest.raises(ValueError, match="top_k"):
            documents_index.documents.search(
                namespace=test_namespace,
                top_k=10001,
                score_by=[
                    {
                        "type": "text",
                        "field": "my_text_field",
                        "text_query": "test",
                    }
                ],
            )

    def test_search_empty_score_by(self, documents_index: Index, test_namespace: str) -> None:
        """Test that empty score_by raises ValueError.

        Args:
            documents_index: The documents index fixture.
            test_namespace: Test namespace for isolation.
        """
        with pytest.raises(ValueError, match="score_by must not be empty"):
            documents_index.documents.search(
                namespace=test_namespace,
                top_k=10,
                score_by=[],
            )


class TestResponseStructure:
    """Test response structure and data access."""

    def test_upsert_response_attributes(self, documents_index: Index, test_namespace: str) -> None:
        """Test that upsert response has expected attributes.

        Args:
            documents_index: The documents index fixture.
            test_namespace: Test namespace for isolation.
        """
        response = documents_index.documents.upsert(
            namespace=test_namespace,
            documents=[{"_id": "doc1", "my_text_field": "test"}],
        )

        # Test attribute access
        assert hasattr(response, "upserted_count")
        assert isinstance(response.upserted_count, int)
        assert response.upserted_count > 0

    def test_search_response_attributes(self, documents_index: Index, test_namespace: str) -> None:
        """Test that search response has expected attributes.

        Args:
            documents_index: The documents index fixture.
            test_namespace: Test namespace for isolation.
        """
        # First upsert a document
        documents_index.documents.upsert(
            namespace=test_namespace,
            documents=[{"_id": "doc1", "my_text_field": "test document"}],
        )

        # Search for it
        response = documents_index.documents.search(
            namespace=test_namespace,
            top_k=1,
            score_by=[
                {
                    "type": "text",
                    "field": "my_text_field",
                    "text_query": "test",
                }
            ],
            include_fields=["_id", "my_text_field"],
        )

        # Test response attributes
        assert hasattr(response, "documents")
        assert hasattr(response, "usage")
        assert isinstance(response.documents, list)

        # Test document attributes if results returned
        if response.documents:
            doc = response.documents[0]
            assert hasattr(doc, "id")
            assert hasattr(doc, "score")
