"""Unit tests for document upsert operation."""

import httpx
import pytest
import respx

from pinecone._internal.http import HTTPClient, HTTPConfig
from pinecone.index.documents import Documents


@respx.mock
def test_upsert_success():
    """Test successful document upsert."""
    respx.post("https://test.io/namespaces/my-ns/documents/upsert").mock(
        return_value=httpx.Response(200, json={"upserted_count": 2})
    )

    client = HTTPClient(api_key="test", config=HTTPConfig(http2=False))
    documents = Documents(http=client, base_url="https://test.io")

    response = documents.upsert(
        namespace="my-ns",
        documents=[
            {"_id": "doc1", "title": "Hello"},
            {"_id": "doc2", "title": "World"},
        ],
    )

    assert response.upserted_count == 2


@respx.mock
def test_upsert_validates_empty_namespace():
    """Test that empty namespace raises ValueError."""
    client = HTTPClient(api_key="test", config=HTTPConfig(http2=False))
    documents = Documents(http=client, base_url="https://test.io")

    with pytest.raises(ValueError, match="namespace"):
        documents.upsert(namespace="", documents=[{"_id": "doc1"}])


@respx.mock
def test_upsert_validates_empty_documents():
    """Test that empty documents list raises ValueError."""
    client = HTTPClient(api_key="test", config=HTTPConfig(http2=False))
    documents = Documents(http=client, base_url="https://test.io")

    with pytest.raises(ValueError, match="documents must not be empty"):
        documents.upsert(namespace="my-ns", documents=[])


@respx.mock
def test_upsert_validates_missing_id():
    """Test that document without _id raises ValueError."""
    client = HTTPClient(api_key="test", config=HTTPConfig(http2=False))
    documents = Documents(http=client, base_url="https://test.io")

    with pytest.raises(ValueError, match="missing required '_id' field"):
        documents.upsert(namespace="my-ns", documents=[{"title": "No ID"}])


@respx.mock
def test_upsert_validates_non_string_id():
    """Test that non-string _id raises TypeError."""
    client = HTTPClient(api_key="test", config=HTTPConfig(http2=False))
    documents = Documents(http=client, base_url="https://test.io")

    with pytest.raises(TypeError, match="non-string '_id': int"):
        documents.upsert(
            namespace="my-ns",
            documents=[
                {"_id": 123, "text": "Numeric ID"},
            ],
        )


@respx.mock
def test_upsert_validates_empty_id():
    """Test that empty string _id raises ValueError."""
    client = HTTPClient(api_key="test", config=HTTPConfig(http2=False))
    documents = Documents(http=client, base_url="https://test.io")

    with pytest.raises(ValueError, match="empty '_id'"):
        documents.upsert(
            namespace="my-ns",
            documents=[
                {"_id": "", "text": "Empty ID"},
            ],
        )


@respx.mock
def test_upsert_validates_duplicate_ids():
    """Test that duplicate _ids in batch raise ValueError."""
    client = HTTPClient(api_key="test", config=HTTPConfig(http2=False))
    documents = Documents(http=client, base_url="https://test.io")

    with pytest.raises(ValueError, match="duplicate '_id': 'doc1'"):
        documents.upsert(
            namespace="my-ns",
            documents=[
                {"_id": "doc1", "text": "First"},
                {"_id": "doc2", "text": "Second"},
                {"_id": "doc1", "text": "Duplicate!"},
            ],
        )


@respx.mock
def test_upsert_validates_batch_size_exceeds_limit():
    """Test that > 100 documents raises ValueError."""
    client = HTTPClient(api_key="test", config=HTTPConfig(http2=False))
    documents = Documents(http=client, base_url="https://test.io")

    # Create 101 documents
    large_batch = [{"_id": f"doc{i}", "text": "test"} for i in range(101)]

    with pytest.raises(ValueError, match=r"documents.*100"):
        documents.upsert(namespace="my-ns", documents=large_batch)


@respx.mock
def test_upsert_validates_batch_size_at_limit():
    """Test that exactly 100 documents is accepted."""
    respx.post("https://test.io/namespaces/my-ns/documents/upsert").mock(
        return_value=httpx.Response(200, json={"upserted_count": 100})
    )

    client = HTTPClient(api_key="test", config=HTTPConfig(http2=False))
    documents = Documents(http=client, base_url="https://test.io")

    # Create exactly 100 documents
    max_batch = [{"_id": f"doc{i}", "text": "test"} for i in range(100)]

    response = documents.upsert(namespace="my-ns", documents=max_batch)
    assert response.upserted_count == 100


@respx.mock
def test_upsert_sends_correct_format():
    """Test that upsert sends documents wrapped in a documents object."""
    route = respx.post("https://test.io/namespaces/my-ns/documents/upsert").mock(
        return_value=httpx.Response(200, json={"upserted_count": 2})
    )

    client = HTTPClient(api_key="test", config=HTTPConfig(http2=False))
    documents = Documents(http=client, base_url="https://test.io")

    documents.upsert(
        namespace="my-ns",
        documents=[
            {"_id": "doc1", "text": "Hello"},
            {"_id": "doc2", "text": "World"},
        ],
    )

    # Verify the request was made
    assert route.called
    request = route.calls.last.request

    # Verify the body is JSON with a "documents" wrapper
    import json

    body = json.loads(request.content.decode("utf-8"))

    assert "documents" in body
    assert len(body["documents"]) == 2
    assert body["documents"][0] == {"_id": "doc1", "text": "Hello"}
    assert body["documents"][1] == {"_id": "doc2", "text": "World"}


@respx.mock
def test_upsert_with_dense_vectors():
    """Test upserting documents with _values field."""
    route = respx.post("https://test.io/namespaces/my-ns/documents/upsert").mock(
        return_value=httpx.Response(200, json={"upserted_count": 2})
    )

    client = HTTPClient(api_key="test", config=HTTPConfig(http2=False))
    documents = Documents(http=client, base_url="https://test.io")

    documents.upsert(
        namespace="my-ns",
        documents=[
            {
                "_id": "movie1",
                "text": "A sci-fi epic about interstellar travel",
                "genre": "sci-fi",
                "_values": [0.1, 0.2, 0.3],
            },
            {
                "_id": "movie2",
                "text": "A romantic comedy set in New York",
                "genre": "comedy",
                "_values": [0.4, 0.5, 0.6],
            },
        ],
    )

    # Verify the request was made
    assert route.called
    request = route.calls.last.request

    import json

    body = json.loads(request.content.decode("utf-8"))

    # Verify _values field is preserved
    assert body["documents"][0]["_values"] == [0.1, 0.2, 0.3]
    assert body["documents"][1]["_values"] == [0.4, 0.5, 0.6]


@respx.mock
def test_upsert_with_sparse_vectors():
    """Test upserting documents with _sparse_values field."""
    route = respx.post("https://test.io/namespaces/my-ns/documents/upsert").mock(
        return_value=httpx.Response(200, json={"upserted_count": 1})
    )

    client = HTTPClient(api_key="test", config=HTTPConfig(http2=False))
    documents = Documents(http=client, base_url="https://test.io")

    documents.upsert(
        namespace="my-ns",
        documents=[
            {
                "_id": "movie1",
                "text": "A sci-fi epic about interstellar travel",
                "genre": "sci-fi",
                "_sparse_values": {
                    "indices": [0, 5, 10, 100],
                    "values": [0.5, 0.3, 0.8, 0.2],
                },
            },
        ],
    )

    # Verify the request was made
    assert route.called
    request = route.calls.last.request

    import json

    body = json.loads(request.content.decode("utf-8"))

    # Verify _sparse_values field is preserved
    assert body["documents"][0]["_sparse_values"] == {
        "indices": [0, 5, 10, 100],
        "values": [0.5, 0.3, 0.8, 0.2],
    }


@respx.mock
def test_upsert_with_string_array_metadata():
    """Test upserting documents with string array values."""
    route = respx.post("https://test.io/namespaces/my-ns/documents/upsert").mock(
        return_value=httpx.Response(200, json={"upserted_count": 2})
    )

    client = HTTPClient(api_key="test", config=HTTPConfig(http2=False))
    documents = Documents(http=client, base_url="https://test.io")

    documents.upsert(
        namespace="my-ns",
        documents=[
            {
                "_id": "movie1",
                "text": "A sci-fi epic about interstellar travel",
                "genre": "sci-fi",
                "tags": ["space", "adventure"],
            },
            {
                "_id": "movie2",
                "text": "A romantic comedy set in New York",
                "genre": "comedy",
                "tags": ["romance", "funny"],
            },
        ],
    )

    # Verify the request was made
    assert route.called
    request = route.calls.last.request

    import json

    body = json.loads(request.content.decode("utf-8"))

    # Verify string arrays are preserved
    assert body["documents"][0]["tags"] == ["space", "adventure"]
    assert body["documents"][1]["tags"] == ["romance", "funny"]
