"""Test string evaluation functionality."""

from unittest.mock import Mock, patch

import pytest

from veritell_langchain import (
    VeritellAPIError,
    VeritellAuthError,
    VeritellConnectionError,
    VeritellEvaluationResult,
    VeritellEvaluator,
    VeritellValidationError,
)


@pytest.fixture
def evaluator():
    """Create a VeritellEvaluator instance with test API key."""
    return VeritellEvaluator(api_key="test-api-overview-key")


def test_evaluate_strings_basic(evaluator):
    """Test basic string evaluation."""
    mock_response = Mock()
    mock_response.status_code = 200
    mock_response.json.return_value = {
        "score": 0.95,
        "passed": True,
        "feedback": "Excellent match",
        "metadata": {"confidence": "high"},
    }

    with patch.object(evaluator._session, "post", return_value=mock_response):
        result = evaluator.evaluate_strings(
            output="The capital of France is Paris",
            expected="The capital of France is Paris",
        )

        assert isinstance(result, VeritellEvaluationResult)
        assert result.score == 0.95
        assert result.passed is True
        assert result.feedback == "Excellent match"
        assert result.metadata == {"confidence": "high"}


def test_evaluate_with_criteria(evaluator):
    """Test evaluation with criteria."""
    mock_response = Mock()
    mock_response.status_code = 200
    mock_response.json.return_value = {
        "score": 0.8,
        "passed": True,
        "feedback": "Good response",
    }

    with patch.object(evaluator._session, "post", return_value=mock_response) as mock_post:
        result = evaluator.evaluate(
            input_text="What is the capital of France?",
            output_text="Paris",
            expected_output="Paris",
            criteria="Must be factually accurate",
        )

        # Verify criteria was passed in request
        call_args = mock_post.call_args
        assert call_args[1]["json"]["criteria"] == "Must be factually accurate"
        assert result.score == 0.8


def test_evaluate_validation_empty_input(evaluator):
    """Test that empty input raises validation error."""
    with pytest.raises(VeritellValidationError) as exc_info:
        evaluator.evaluate(input_text="", output_text="test")
    assert "input_text must be a non-empty string" in str(exc_info.value)


def test_evaluate_validation_empty_output(evaluator):
    """Test that empty output raises validation error."""
    with pytest.raises(VeritellValidationError) as exc_info:
        evaluator.evaluate(input_text="test", output_text="")
    assert "output_text must be a non-empty string" in str(exc_info.value)


def test_evaluate_validation_non_string_input(evaluator):
    """Test that non-string input raises validation error."""
    with pytest.raises(VeritellValidationError):
        evaluator.evaluate(input_text=123, output_text="test")


def test_evaluate_auth_error(evaluator):
    """Test that 401 response raises auth error."""
    mock_response = Mock()
    mock_response.status_code = 401

    with patch.object(evaluator._session, "post", return_value=mock_response):
        with pytest.raises(VeritellAuthError) as exc_info:
            evaluator.evaluate(input_text="test", output_text="test")
        assert "Authentication failed" in str(exc_info.value)


def test_evaluate_api_error(evaluator):
    """Test that API errors are handled correctly."""
    mock_response = Mock()
    mock_response.status_code = 500
    mock_response.json.return_value = {"error": "Internal server error"}
    mock_response.text = "Internal server error"

    with patch.object(evaluator._session, "post", return_value=mock_response):
        with pytest.raises(VeritellAPIError) as exc_info:
            evaluator.evaluate(input_text="test", output_text="test")
        assert exc_info.value.status_code == 500
        assert "Internal server error" in str(exc_info.value)


def test_evaluate_timeout_error(evaluator):
    """Test that timeout errors are handled."""
    import requests

    with patch.object(evaluator._session, "post", side_effect=requests.exceptions.Timeout()):
        with pytest.raises(VeritellConnectionError) as exc_info:
            evaluator.evaluate(input_text="test", output_text="test")
        assert "timed out" in str(exc_info.value)


def test_evaluate_connection_error(evaluator):
    """Test that connection errors are handled."""
    import requests

    with patch.object(
        evaluator._session, "post", side_effect=requests.exceptions.ConnectionError()
    ):
        with pytest.raises(VeritellConnectionError) as exc_info:
            evaluator.evaluate(input_text="test", output_text="test")
        assert "Connection error" in str(exc_info.value)


def test_evaluate_with_metadata(evaluator):
    """Test evaluation with metadata."""
    mock_response = Mock()
    mock_response.status_code = 200
    mock_response.json.return_value = {"score": 0.9, "passed": True, "feedback": "Good"}

    with patch.object(evaluator._session, "post", return_value=mock_response) as mock_post:
        metadata = {"user_id": "123", "session_id": "abc"}
        evaluator.evaluate(input_text="test", output_text="test", metadata=metadata)

        call_args = mock_post.call_args
        assert call_args[1]["json"]["metadata"] == metadata


def test_context_manager(evaluator):
    """Test that evaluator works as context manager."""
    with patch.object(evaluator._session, "close") as mock_close:
        with evaluator as eval:
            assert eval is evaluator
        mock_close.assert_called_once()


def test_result_repr():
    """Test VeritellEvaluationResult string representation."""
    result = VeritellEvaluationResult(score=0.95, passed=True, feedback="This is a short feedback")
    repr_str = repr(result)
    assert "0.95" in repr_str
    assert "True" in repr_str
    assert "short feedback" in repr_str


def test_result_repr_long_feedback():
    """Test VeritellEvaluationResult with long feedback."""
    long_feedback = "x" * 100
    result = VeritellEvaluationResult(score=0.5, passed=False, feedback=long_feedback)
    repr_str = repr(result)
    assert len(repr_str) < 200  # Should be truncated


def test_evaluator_session_headers(evaluator):
    """Test that session headers are set correctly."""
    assert "Authorization" in evaluator._session.headers
    assert evaluator._session.headers["Authorization"] == "Bearer test-api-overview-key"
    assert evaluator._session.headers["Content-Type"] == "application/json"


def test_evaluate_request_format(evaluator):
    """Test that request is formatted correctly."""
    mock_response = Mock()
    mock_response.status_code = 200
    mock_response.json.return_value = {
        "score": 1.0,
        "passed": True,
        "feedback": "Perfect",
    }

    with patch.object(evaluator._session, "post", return_value=mock_response) as mock_post:
        evaluator.evaluate(
            input_text="input",
            output_text="output",
            expected_output="expected",
            criteria="criteria",
        )

        # Verify request format
        call_args = mock_post.call_args
        assert call_args[0][0] == f"{evaluator.api_base_url}/evaluate"
        payload = call_args[1]["json"]
        assert payload["input"] == "input"
        assert payload["output"] == "output"
        assert payload["expected_output"] == "expected"
        assert payload["criteria"] == "criteria"
        assert call_args[1]["timeout"] == evaluator.timeout
