"""Tests for inference parameter type hints.

This module verifies that the EmbedParameters TypedDict works correctly with
the embed() method, and that rerank() accepts dict[str, Any] for model-specific
parameters. Both support forward compatibility with future API additions.
"""

from typing import TYPE_CHECKING, Any

import pytest
import respx

from httpx import Response
from msgspec import json as msgspec_json

from pinecone import Pinecone


if TYPE_CHECKING:
    from pinecone.__interface__ import EmbedParameters


@pytest.fixture()
def mock_embed_response() -> dict[str, Any]:
    """Mock embed API response."""
    return {
        "data": [
            {"index": 0, "values": [0.1, 0.2, 0.3]},
            {"index": 1, "values": [0.4, 0.5, 0.6]},
        ],
        "model": "multilingual-e5-large",
        "vector_type": "dense",
        "usage": {"total_tokens": 10},
    }


@pytest.fixture()
def mock_rerank_response() -> dict[str, Any]:
    """Mock rerank API response."""
    return {
        "data": [
            {"index": 0, "score": 0.9},
            {"index": 1, "score": 0.5},
        ],
        "model": "bge-reranker-v2-m3",
        "usage": {"total_tokens": 20},
    }


class TestEmbedParametersTyping:
    """Test embed() with different parameter typing approaches."""

    @respx.mock
    def test_embed_with_typed_dict_style(self, mock_embed_response: dict[str, Any]) -> None:
        """Using dict with typed keys gives IDE autocomplete."""
        respx.post("https://api.pinecone.io/embed").mock(
            return_value=Response(200, content=msgspec_json.encode(mock_embed_response))
        )

        pc = Pinecone(api_key="test-key")

        # This style provides IDE autocomplete for known parameter values
        response = pc.inference.embed(
            model="multilingual-e5-large",
            texts=["hello world"],
            parameters={"input_type": "passage", "truncate": "END"},
        )

        assert len(response.embeddings) == 2
        assert response.model == "multilingual-e5-large"

    @respx.mock
    def test_embed_with_plain_dict(self, mock_embed_response: dict[str, Any]) -> None:
        """Plain dict[str, Any] still works for flexibility."""
        respx.post("https://api.pinecone.io/embed").mock(
            return_value=Response(200, content=msgspec_json.encode(mock_embed_response))
        )

        pc = Pinecone(api_key="test-key")

        # This style works for new/unknown parameters
        params: dict[str, Any] = {
            "input_type": "query",
            "truncate": "NONE",
            "future_param": "new_value",  # New API parameters work immediately
        }

        response = pc.inference.embed(
            model="multilingual-e5-large",
            texts=["hello world"],
            parameters=params,
        )

        assert len(response.embeddings) == 2

    @respx.mock
    def test_embed_with_explicit_typed_dict(self, mock_embed_response: dict[str, Any]) -> None:
        """EmbedParameters TypedDict works explicitly."""
        respx.post("https://api.pinecone.io/embed").mock(
            return_value=Response(200, content=msgspec_json.encode(mock_embed_response))
        )

        pc = Pinecone(api_key="test-key")

        # Explicit TypedDict usage
        params: EmbedParameters = {
            "input_type": "passage",
            "truncate": "START",
        }

        response = pc.inference.embed(
            model="multilingual-e5-large",
            texts=["hello world"],
            parameters=params,
        )

        assert len(response.embeddings) == 2

    @respx.mock
    def test_embed_with_new_api_values(self, mock_embed_response: dict[str, Any]) -> None:
        """New API values work immediately without SDK update."""
        respx.post("https://api.pinecone.io/embed").mock(
            return_value=Response(200, content=msgspec_json.encode(mock_embed_response))
        )

        pc = Pinecone(api_key="test-key")

        # Future API value - works because of | str union
        response = pc.inference.embed(
            model="multilingual-e5-large",
            texts=["hello world"],
            parameters={"input_type": "document"},  # Not in current Literal, but works
        )

        assert len(response.embeddings) == 2


class TestRerankParametersTyping:
    """Test rerank() with dict[str, Any] for model-specific parameters."""

    @respx.mock
    def test_rerank_with_empty_parameters(self, mock_rerank_response: dict[str, Any]) -> None:
        """Rerank works with no parameters."""
        respx.post("https://api.pinecone.io/rerank").mock(
            return_value=Response(200, content=msgspec_json.encode(mock_rerank_response))
        )

        pc = Pinecone(api_key="test-key")

        response = pc.inference.rerank(
            model="bge-reranker-v2-m3",
            query="test query",
            documents=["doc1", "doc2"],
        )

        assert len(response.results) == 2

    @respx.mock
    def test_rerank_with_model_specific_parameters(
        self, mock_rerank_response: dict[str, Any]
    ) -> None:
        """dict[str, Any] works for model-specific parameters."""
        respx.post("https://api.pinecone.io/rerank").mock(
            return_value=Response(200, content=msgspec_json.encode(mock_rerank_response))
        )

        pc = Pinecone(api_key="test-key")

        # Forward-compatible: any model-specific parameters work
        params: dict[str, Any] = {
            "model_specific_param": "value",
            "another_param": 123,
        }

        response = pc.inference.rerank(
            model="bge-reranker-v2-m3",
            query="test query",
            documents=["doc1", "doc2"],
            parameters=params,
        )

        assert len(response.results) == 2
