"""
Integration tests for EvidenceEvaluator.

Tests cover:
- EvidenceEvaluator instantiation with mocked LLM
- extract_evidence() with various search results
- Evidence type parsing with actual class
- Confidence scoring and adjustment
- Match quality assessment with real constraints
- Think tag removal from LLM responses
"""

from unittest.mock import Mock

import pytest


class TestEvidenceEvaluatorInstantiation:
    """Tests for EvidenceEvaluator class instantiation."""

    def test_init_with_mock_model(self):
        """Should initialize with a mocked model."""
        from local_deep_research.advanced_search_system.evidence.evaluator import (
            EvidenceEvaluator,
        )

        mock_model = Mock()
        evaluator = EvidenceEvaluator(model=mock_model)

        assert evaluator.model is mock_model
        assert evaluator.source_reliability is not None

    def test_source_reliability_values(self):
        """Should have correct source reliability values."""
        from local_deep_research.advanced_search_system.evidence.evaluator import (
            EvidenceEvaluator,
        )

        evaluator = EvidenceEvaluator(model=Mock())

        assert evaluator.source_reliability["official"] == 1.0
        assert evaluator.source_reliability["research"] == 0.95
        assert evaluator.source_reliability["news"] == 0.8
        assert evaluator.source_reliability["community"] == 0.6
        assert evaluator.source_reliability["inference"] == 0.5
        assert evaluator.source_reliability["speculation"] == 0.3


class TestExtractEvidenceIntegration:
    """Integration tests for extract_evidence method."""

    @pytest.fixture
    def evaluator(self):
        """Create evaluator with mock model."""
        from local_deep_research.advanced_search_system.evidence.evaluator import (
            EvidenceEvaluator,
        )

        mock_model = Mock()
        return EvidenceEvaluator(model=mock_model), mock_model

    @pytest.fixture
    def constraint(self):
        """Create a sample constraint."""
        from local_deep_research.advanced_search_system.constraints.base_constraint import (
            Constraint,
            ConstraintType,
        )

        return Constraint(
            id="c1",
            type=ConstraintType.PROPERTY,
            description="Must contain the word 'mountain'",
            value="mountain",
        )

    def test_extract_evidence_calls_llm(self, evaluator, constraint):
        """Should invoke LLM with formatted prompt."""
        ev, mock_model = evaluator
        mock_model.invoke.return_value = Mock(
            content="""CLAIM: The location contains a mountain
TYPE: direct_statement
SOURCE: Wikipedia
CONFIDENCE: 0.9
REASONING: Explicitly mentioned in the text
QUOTE: "The trail goes through the mountain range"
"""
        )

        search_result = (
            "The popular hiking trail goes through the mountain range."
        )

        ev.extract_evidence(search_result, "Test Trail", constraint)

        mock_model.invoke.assert_called_once()
        assert "Test Trail" in mock_model.invoke.call_args[0][0]
        assert "mountain" in mock_model.invoke.call_args[0][0]

    def test_extract_evidence_returns_evidence_object(
        self, evaluator, constraint
    ):
        """Should return Evidence object with correct fields."""
        from local_deep_research.advanced_search_system.evidence.base_evidence import (
            Evidence,
            EvidenceType,
        )

        ev, mock_model = evaluator
        mock_model.invoke.return_value = Mock(
            content="""CLAIM: The location contains a mountain
TYPE: direct_statement
SOURCE: Wikipedia
CONFIDENCE: 0.9
REASONING: Explicitly stated
QUOTE: "mountain range"
"""
        )

        evidence = ev.extract_evidence("Sample text", "Test", constraint)

        assert isinstance(evidence, Evidence)
        assert evidence.claim == "The location contains a mountain"
        assert evidence.type == EvidenceType.DIRECT_STATEMENT
        assert evidence.source == "Wikipedia"

    def test_extract_evidence_parses_confidence(self, evaluator, constraint):
        """Should parse and clamp confidence value."""
        ev, mock_model = evaluator
        mock_model.invoke.return_value = Mock(
            content="""CLAIM: Test claim
TYPE: news_report
SOURCE: News site
CONFIDENCE: 0.75
REASONING: Based on news
QUOTE: "test"
"""
        )

        evidence = ev.extract_evidence("Sample", "Test", constraint)

        # Confidence is adjusted by match quality
        assert 0.0 <= evidence.confidence <= 1.0

    def test_extract_evidence_handles_high_confidence(
        self, evaluator, constraint
    ):
        """Should clamp confidence above 1.0."""
        ev, mock_model = evaluator
        mock_model.invoke.return_value = Mock(
            content="""CLAIM: Test claim
TYPE: direct_statement
SOURCE: Source
CONFIDENCE: 1.5
REASONING: Reason
QUOTE: Quote
"""
        )

        evidence = ev.extract_evidence("Sample", "Test", constraint)

        assert evidence.confidence <= 1.0

    def test_extract_evidence_handles_negative_confidence(
        self, evaluator, constraint
    ):
        """Should clamp negative confidence to 0."""
        ev, mock_model = evaluator
        mock_model.invoke.return_value = Mock(
            content="""CLAIM: Test claim
TYPE: direct_statement
SOURCE: Source
CONFIDENCE: -0.5
REASONING: Reason
QUOTE: Quote
"""
        )

        evidence = ev.extract_evidence("Sample", "Test", constraint)

        assert evidence.confidence >= 0.0


class TestExtractEvidenceThinkTags:
    """Tests for think tag handling in extract_evidence."""

    @pytest.fixture
    def evaluator(self):
        """Create evaluator with mock model."""
        from local_deep_research.advanced_search_system.evidence.evaluator import (
            EvidenceEvaluator,
        )

        return EvidenceEvaluator(model=Mock())

    @pytest.fixture
    def constraint(self):
        """Create a sample constraint."""
        from local_deep_research.advanced_search_system.constraints.base_constraint import (
            Constraint,
            ConstraintType,
        )

        return Constraint(
            id="c1",
            type=ConstraintType.PROPERTY,
            description="Test constraint",
            value="test value",
        )

    def test_think_tags_removed(self, evaluator, constraint):
        """Should remove think tags from LLM response."""
        evaluator.model.invoke.return_value = Mock(
            content="""<think>Let me analyze this carefully...</think>
CLAIM: The evidence supports the constraint
TYPE: direct_statement
SOURCE: Trusted source
CONFIDENCE: 0.85
REASONING: Clear match found
QUOTE: "test value mentioned"
"""
        )

        evidence = evaluator.extract_evidence("Sample text", "Test", constraint)

        # Should have parsed correctly despite think tags
        assert evidence.claim == "The evidence supports the constraint"
        assert "<think>" not in evidence.claim

    def test_multiple_think_tags_removed(self, evaluator, constraint):
        """Should handle multiple think tags."""
        evaluator.model.invoke.return_value = Mock(
            content="""<think>First thought</think>
<think>Second thought</think>
CLAIM: Valid claim
TYPE: inference
SOURCE: Analysis
CONFIDENCE: 0.7
REASONING: Inferred from context
QUOTE: None
"""
        )

        evidence = evaluator.extract_evidence("Sample", "Test", constraint)

        assert evidence.claim == "Valid claim"


class TestEvidenceResponseParsing:
    """Integration tests for LLM response parsing."""

    @pytest.fixture
    def evaluator(self):
        """Create evaluator with mock model."""
        from local_deep_research.advanced_search_system.evidence.evaluator import (
            EvidenceEvaluator,
        )

        return EvidenceEvaluator(model=Mock())

    def test_parse_response_with_extra_content(self, evaluator):
        """Should ignore non-standard lines."""
        response = """Here is the analysis:

CLAIM: The evidence is clear
TYPE: direct_statement

Additional notes: This is extra content."""

        parsed = evaluator._parse_evidence_response(response)

        assert parsed["claim"] == "The evidence is clear"
        assert parsed["type"] == "direct_statement"

    def test_parse_response_with_colons_in_value(self, evaluator):
        """Should handle colons within field values."""
        response = """CLAIM: Note: this is a claim with colons
SOURCE: URL: https://example.com"""

        parsed = evaluator._parse_evidence_response(response)

        assert "this is a claim with colons" in parsed["claim"]
        assert "https://example.com" in parsed["source"]


class TestEvidenceMetadata:
    """Tests for evidence metadata handling."""

    @pytest.fixture
    def evaluator(self):
        """Create evaluator with mock model."""
        from local_deep_research.advanced_search_system.evidence.evaluator import (
            EvidenceEvaluator,
        )

        return EvidenceEvaluator(model=Mock())

    @pytest.fixture
    def constraint(self):
        """Create a sample constraint."""
        from local_deep_research.advanced_search_system.constraints.base_constraint import (
            Constraint,
            ConstraintType,
        )

        return Constraint(
            id="constraint_123",
            type=ConstraintType.TEMPORAL,
            description="Must be from 2023",
            value="2023",
        )

    def test_evidence_includes_candidate_in_metadata(
        self, evaluator, constraint
    ):
        """Should include candidate name in metadata."""
        evaluator.model.invoke.return_value = Mock(
            content="""CLAIM: Test
TYPE: inference
SOURCE: Test
CONFIDENCE: 0.5
REASONING: Test
QUOTE: Test"""
        )

        evidence = evaluator.extract_evidence(
            "Search result", "CandidateName", constraint
        )

        assert evidence.metadata["candidate"] == "CandidateName"

    def test_evidence_includes_constraint_id_in_metadata(
        self, evaluator, constraint
    ):
        """Should include constraint ID in metadata."""
        evaluator.model.invoke.return_value = Mock(
            content="""CLAIM: Test
TYPE: inference
SOURCE: Test
CONFIDENCE: 0.5
REASONING: Test
QUOTE: Test"""
        )

        evidence = evaluator.extract_evidence(
            "Search result", "Test", constraint
        )

        assert evidence.metadata["constraint_id"] == "constraint_123"

    def test_evidence_includes_constraint_type_in_metadata(
        self, evaluator, constraint
    ):
        """Should include constraint type in metadata."""
        evaluator.model.invoke.return_value = Mock(
            content="""CLAIM: Test
TYPE: inference
SOURCE: Test
CONFIDENCE: 0.5
REASONING: Test
QUOTE: Test"""
        )

        evidence = evaluator.extract_evidence(
            "Search result", "Test", constraint
        )

        assert evidence.metadata["constraint_type"] == "temporal"


class TestSearchResultHandling:
    """Tests for search result handling in extract_evidence."""

    @pytest.fixture
    def evaluator(self):
        """Create evaluator with mock model."""
        from local_deep_research.advanced_search_system.evidence.evaluator import (
            EvidenceEvaluator,
        )

        return EvidenceEvaluator(model=Mock())

    @pytest.fixture
    def constraint(self):
        """Create a sample constraint."""
        from local_deep_research.advanced_search_system.constraints.base_constraint import (
            Constraint,
            ConstraintType,
        )

        return Constraint(
            id="c1",
            type=ConstraintType.PROPERTY,
            description="Test",
            value="test",
        )

    def test_long_search_result_truncated(self, evaluator, constraint):
        """Should truncate search results to 3000 chars in prompt."""
        evaluator.model.invoke.return_value = Mock(
            content="""CLAIM: Test
TYPE: speculation
SOURCE: Unknown
CONFIDENCE: 0.5
REASONING: Test
QUOTE: None"""
        )

        long_result = "x" * 5000
        evaluator.extract_evidence(long_result, "Test", constraint)

        # Check the prompt doesn't include full 5000 chars
        call_args = evaluator.model.invoke.call_args[0][0]
        assert len(call_args) < 5000 + 500  # Prompt overhead

    def test_short_search_result_not_truncated(self, evaluator, constraint):
        """Should not truncate short search results."""
        evaluator.model.invoke.return_value = Mock(
            content="""CLAIM: Test
TYPE: speculation
SOURCE: Unknown
CONFIDENCE: 0.5
REASONING: Test
QUOTE: None"""
        )

        short_result = "This is a short result."
        evaluator.extract_evidence(short_result, "Test", constraint)

        call_args = evaluator.model.invoke.call_args[0][0]
        assert short_result in call_args
