"""
Tests for benchmarks/graders.py

Tests cover:
- extract_answer_from_response function
- grade_single_result with mocked LLM
- get_evaluation_llm configuration
"""

from unittest.mock import Mock, patch, MagicMock


class TestExtractAnswerFromResponse:
    """Tests for the extract_answer_from_response function."""

    def test_browsecomp_extracts_exact_answer(self):
        """Test extraction of exact answer from BrowseComp response."""
        from local_deep_research.benchmarks.graders import (
            extract_answer_from_response,
        )

        response = """
Based on my research, I found the following information.

Exact Answer: 42
Confidence: 95%
"""
        result = extract_answer_from_response(
            response, dataset_type="browsecomp"
        )

        assert result["extracted_answer"] == "42"
        assert result["confidence"] == "95"

    def test_browsecomp_missing_answer_returns_none(self):
        """Test handling of missing answer in BrowseComp response."""
        from local_deep_research.benchmarks.graders import (
            extract_answer_from_response,
        )

        response = "Some response without the expected format"
        result = extract_answer_from_response(
            response, dataset_type="browsecomp"
        )

        assert result["extracted_answer"] == "None"
        assert result["confidence"] == "100"

    def test_browsecomp_missing_confidence_defaults(self):
        """Test that missing confidence defaults to 100."""
        from local_deep_research.benchmarks.graders import (
            extract_answer_from_response,
        )

        response = "Exact Answer: Paris"
        result = extract_answer_from_response(
            response, dataset_type="browsecomp"
        )

        assert result["extracted_answer"] == "Paris"
        assert result["confidence"] == "100"

    def test_simpleqa_returns_full_response(self):
        """Test that SimpleQA returns the full response as the answer."""
        from local_deep_research.benchmarks.graders import (
            extract_answer_from_response,
        )

        response = "The capital of France is Paris."
        result = extract_answer_from_response(response, dataset_type="simpleqa")

        assert result["extracted_answer"] == response
        assert result["confidence"] == "100"

    def test_removes_citations_from_response(self):
        """Test that citations are removed from the response."""
        from local_deep_research.benchmarks.graders import (
            extract_answer_from_response,
        )

        response = "The answer is 42 [1] according to the source [2][3]."
        result = extract_answer_from_response(response, dataset_type="simpleqa")

        assert "[1]" not in result["extracted_answer"]
        assert "[2]" not in result["extracted_answer"]
        assert "[3]" not in result["extracted_answer"]
        assert "42" in result["extracted_answer"]

    def test_browsecomp_case_insensitive(self):
        """Test that dataset type matching is case insensitive."""
        from local_deep_research.benchmarks.graders import (
            extract_answer_from_response,
        )

        response = "Exact Answer: test\nConfidence: 80%"
        result = extract_answer_from_response(
            response, dataset_type="BROWSECOMP"
        )

        assert result["extracted_answer"] == "test"
        assert result["confidence"] == "80"


class TestGetEvaluationLLM:
    """Tests for the get_evaluation_llm function."""

    @patch("local_deep_research.benchmarks.graders.get_llm")
    def test_uses_default_config(self, mock_get_llm):
        """Test that default config is used."""
        from local_deep_research.benchmarks.graders import (
            get_evaluation_llm,
            DEFAULT_EVALUATION_CONFIG,
        )

        mock_get_llm.return_value = Mock()

        get_evaluation_llm()

        mock_get_llm.assert_called_once()
        call_kwargs = mock_get_llm.call_args[1]
        assert (
            call_kwargs["model_name"] == DEFAULT_EVALUATION_CONFIG["model_name"]
        )
        assert (
            call_kwargs["temperature"]
            == DEFAULT_EVALUATION_CONFIG["temperature"]
        )

    @patch("local_deep_research.benchmarks.graders.get_llm")
    def test_custom_config_overrides_defaults(self, mock_get_llm):
        """Test that custom config overrides defaults."""
        from local_deep_research.benchmarks.graders import get_evaluation_llm

        mock_get_llm.return_value = Mock()

        custom_config = {
            "model_name": "custom-model",
            "temperature": 0.5,
        }
        get_evaluation_llm(custom_config=custom_config)

        call_kwargs = mock_get_llm.call_args[1]
        assert call_kwargs["model_name"] == "custom-model"
        assert call_kwargs["temperature"] == 0.5

    @patch("local_deep_research.benchmarks.graders.get_llm")
    def test_filters_unsupported_params(self, mock_get_llm):
        """Test that unsupported parameters are filtered out."""
        from local_deep_research.benchmarks.graders import get_evaluation_llm

        mock_get_llm.return_value = Mock()

        custom_config = {
            "model_name": "test-model",
            "unsupported_param": "value",
            "max_tokens": 1000,  # Not supported by LDR's get_llm
        }
        get_evaluation_llm(custom_config=custom_config)

        call_kwargs = mock_get_llm.call_args[1]
        assert "unsupported_param" not in call_kwargs
        assert "max_tokens" not in call_kwargs

    @patch("local_deep_research.benchmarks.graders.get_llm")
    def test_extracts_api_key_from_settings_snapshot(self, mock_get_llm):
        """Test that API key is extracted from settings snapshot."""
        from local_deep_research.benchmarks.graders import get_evaluation_llm

        mock_get_llm.return_value = Mock()

        settings_snapshot = {
            "llm.openai_endpoint.api_key": {"value": "test-api-key"}
        }
        get_evaluation_llm(settings_snapshot=settings_snapshot)

        # Should not raise and should call get_llm
        mock_get_llm.assert_called_once()


class TestGradeSingleResult:
    """Tests for the grade_single_result function."""

    @patch("local_deep_research.benchmarks.graders.get_evaluation_llm")
    def test_grades_correctly(self, mock_get_eval_llm):
        """Test that grade_single_result grades correctly."""
        from local_deep_research.benchmarks.graders import grade_single_result

        # Mock the LLM response
        mock_llm = MagicMock()
        mock_response = MagicMock()
        mock_response.content = """
Extracted Answer: Paris
Reasoning: The model correctly identified Paris as the capital of France.
Correct: yes
"""
        mock_llm.invoke.return_value = mock_response
        mock_get_eval_llm.return_value = mock_llm

        result_data = {
            "problem": "What is the capital of France?",
            "correct_answer": "Paris",
            "response": "The capital of France is Paris.",
        }

        graded = grade_single_result(result_data, dataset_type="simpleqa")

        assert graded["is_correct"] is True
        assert "Paris" in graded["extracted_by_grader"]

    @patch("local_deep_research.benchmarks.graders.get_evaluation_llm")
    def test_handles_grading_error(self, mock_get_eval_llm):
        """Test that grade_single_result handles errors gracefully."""
        from local_deep_research.benchmarks.graders import grade_single_result

        # Mock the LLM to raise an error during invoke (inside the try block)
        mock_llm = MagicMock()
        mock_llm.invoke.side_effect = Exception("LLM invoke error")
        mock_get_eval_llm.return_value = mock_llm

        result_data = {
            "problem": "test",
            "correct_answer": "answer",
            "response": "response",
        }

        graded = grade_single_result(result_data)

        assert graded["is_correct"] is False
        assert "grading_error" in graded

    @patch("local_deep_research.benchmarks.graders.get_evaluation_llm")
    def test_browsecomp_grading_format(self, mock_get_eval_llm):
        """Test BrowseComp-specific grading format extraction."""
        from local_deep_research.benchmarks.graders import grade_single_result

        mock_llm = MagicMock()
        mock_response = MagicMock()
        mock_response.content = """
extracted_final_answer: 42
reasoning: The model found the correct answer by analyzing the data.
correct: yes
confidence: 95
"""
        mock_llm.invoke.return_value = mock_response
        mock_get_eval_llm.return_value = mock_llm

        result_data = {
            "problem": "What is the answer?",
            "correct_answer": "42",
            "response": "The answer is 42.",
        }

        graded = grade_single_result(result_data, dataset_type="browsecomp")

        assert graded["is_correct"] is True
        assert graded["extracted_by_grader"] == "42"
        assert graded["graded_confidence"] == "95"

    @patch("local_deep_research.benchmarks.graders.get_evaluation_llm")
    def test_grading_with_no_judgment(self, mock_get_eval_llm):
        """Test grading when LLM doesn't provide clear judgment."""
        from local_deep_research.benchmarks.graders import grade_single_result

        mock_llm = MagicMock()
        mock_response = MagicMock()
        mock_response.content = "Some response without proper format"
        mock_llm.invoke.return_value = mock_response
        mock_get_eval_llm.return_value = mock_llm

        result_data = {
            "problem": "test",
            "correct_answer": "answer",
            "response": "response",
        }

        graded = grade_single_result(result_data, dataset_type="simpleqa")

        # Should default to False when no clear judgment
        assert graded["is_correct"] is False
        assert graded["extracted_by_grader"] == "None"
