"""Test cases for Voyage reranker provider."""

import pytest
from unittest.mock import Mock, patch
import os

from esperanto.providers.reranker.voyage import VoyageRerankerModel
from esperanto.common_types.reranker import RerankResponse, RerankResult


class TestVoyageReranker:
    """Test cases for Voyage reranker provider."""

    def test_initialization_with_api_key(self):
        """Test proper initialization with API key."""
        api_key = "test-api-key"
        reranker = VoyageRerankerModel(
            model_name="rerank-2",
            api_key=api_key,
            config={}
        )
        
        assert reranker.api_key == api_key
        assert reranker.model_name == "rerank-2"
        assert reranker.provider == "voyage"
        assert reranker.base_url == "https://api.voyageai.com/v1"

    def test_missing_api_key(self):
        """Test handling of missing API key."""
        with patch.dict('os.environ', {}, clear=True):
            with pytest.raises(ValueError, match="Voyage API key not found"):
                VoyageRerankerModel(
                    model_name="rerank-2",
                    api_key=None,
                    config={}
                )

    def test_initialization_with_env_var(self):
        """Test initialization using environment variable."""
        with patch.dict('os.environ', {'VOYAGE_API_KEY': 'env-api-key'}):
            reranker = VoyageRerankerModel(
                model_name="rerank-2",
                api_key=None,
                config={}
            )
            assert reranker.api_key == "env-api-key"

    def test_provider_properties(self):
        """Test provider properties and models."""
        reranker = VoyageRerankerModel(
            model_name="rerank-2",
            api_key="test-key",
            config={}
        )
        
        assert reranker.provider == "voyage"
        assert len(reranker.models) > 0
        # Model type is None when not explicitly provided by the API
        assert all(model.type is None for model in reranker.models)
        assert reranker._get_default_model() == "rerank-2"

    def test_validation_errors(self):
        """Test input validation."""
        reranker = VoyageRerankerModel(
            model_name="rerank-2",
            api_key="test-key",
            config={}
        )
        
        # Test empty query
        with pytest.raises(ValueError, match="Query cannot be empty"):
            reranker.rerank("", ["doc1"])
        
        # Test empty documents
        with pytest.raises(ValueError, match="Documents list cannot be empty"):
            reranker.rerank("query", [])
        
        # Test invalid top_k
        with pytest.raises(ValueError, match="top_k must be positive"):
            reranker.rerank("query", ["doc1"], top_k=0)

    def test_model_listings(self):
        """Test available models are properly listed."""
        reranker = VoyageRerankerModel(
            model_name="rerank-2",
            api_key="test-key",
            config={}
        )
        
        models = reranker.models
        model_names = [m.id for m in models]
        
        # Check that key models are included
        assert "rerank-2" in model_names
        assert "rerank-1" in model_names

    def test_headers_generation(self):
        """Test request headers are properly generated."""
        reranker = VoyageRerankerModel(
            model_name="rerank-2",
            api_key="test-secret-key",
            config={}
        )
        
        headers = reranker._get_headers()
        assert headers["Authorization"] == "Bearer test-secret-key"
        assert headers["Content-Type"] == "application/json"

    def test_request_payload_building(self):
        """Test request payload is properly built."""
        reranker = VoyageRerankerModel(
            model_name="rerank-2",
            api_key="test-key",
            config={}
        )
        
        query = "test query"
        documents = ["doc1", "doc2"]
        top_k = 1
        
        payload = reranker._build_request_payload(query, documents, top_k)
        
        assert payload["query"] == query
        assert payload["documents"] == documents
        assert payload["model"] == "rerank-2"
        assert payload["top_k"] == top_k

    def test_custom_config_in_payload(self):
        """Test custom config is included in request payload."""
        custom_config = {"return_documents": False, "custom_param": "value"}
        reranker = VoyageRerankerModel(
            model_name="rerank-2",
            api_key="test-key",
            config=custom_config
        )
        
        payload = reranker._build_request_payload("query", ["doc1"], 1)
        
        # Custom config is not directly added to payload in current implementation
        # The payload will have return_documents=True (hardcoded)
        assert payload["return_documents"] is True

    def test_response_processing(self):
        """Test response data processing."""
        reranker = VoyageRerankerModel(
            model_name="rerank-2",
            api_key="test-key",
            config={}
        )
        
        # Mock response data - Voyage uses 'data' instead of 'results'
        response_data = {
            "model": "rerank-2",
            "data": [
                {"index": 0, "document": "Machine learning is AI", "relevance_score": 0.95},
                {"index": 1, "document": "Weather is nice", "relevance_score": 0.15}
            ]
        }
        
        result = reranker._parse_response(response_data, ["Machine learning is AI", "Weather is nice"])
        
        assert isinstance(result, RerankResponse)
        assert result.model == "rerank-2"
        assert len(result.results) == 2
        
        # Verify document text
        assert result.results[0].document == "Machine learning is AI"
        assert result.results[1].document == "Weather is nice"

    def test_score_normalization(self):
        """Test that scores are properly normalized."""
        reranker = VoyageRerankerModel(
            model_name="rerank-2",
            api_key="test-key",
            config={}
        )
        
        # Test normalization with scores outside 0-1 range
        scores = [10.0, 5.0, 0.0]
        normalized = reranker._normalize_scores(scores)
        
        # Verify all scores are in 0-1 range
        for score in normalized:
            assert 0.0 <= score <= 1.0
        
        # Verify relative ordering is preserved
        assert normalized[0] > normalized[1] > normalized[2]

    def test_get_model_name(self):
        """Test model name retrieval."""
        reranker = VoyageRerankerModel(
            model_name="custom-model",
            api_key="test-key",
            config={}
        )
        
        assert reranker.get_model_name() == "custom-model"

    def test_default_model(self):
        """Test default model selection."""
        reranker = VoyageRerankerModel(
            model_name=None,
            api_key="test-key",
            config={}
        )
        
        assert reranker.get_model_name() == "rerank-2"