"""
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_dict_exists(self):
        """Should have source reliability dictionary initialized."""
        from local_deep_research.advanced_search_system.evidence.evaluator import (
            EvidenceEvaluator,
        )

        evaluator = EvidenceEvaluator(model=Mock())

        assert "official" in evaluator.source_reliability
        assert "speculation" in evaluator.source_reliability
        assert (
            evaluator.source_reliability["official"]
            > evaluator.source_reliability["speculation"]
        )

    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 TestEvidenceTypeParsing:
    """Integration tests for evidence type 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_direct_statement(self, evaluator):
        """Should parse direct_statement type."""
        from local_deep_research.advanced_search_system.evidence.base_evidence import (
            EvidenceType,
        )

        result = evaluator._parse_evidence_type("direct_statement")
        assert result == EvidenceType.DIRECT_STATEMENT

    def test_parse_official_record(self, evaluator):
        """Should parse official_record type."""
        from local_deep_research.advanced_search_system.evidence.base_evidence import (
            EvidenceType,
        )

        result = evaluator._parse_evidence_type("official_record")
        assert result == EvidenceType.OFFICIAL_RECORD

    def test_parse_research_finding(self, evaluator):
        """Should parse research_finding type."""
        from local_deep_research.advanced_search_system.evidence.base_evidence import (
            EvidenceType,
        )

        result = evaluator._parse_evidence_type("research_finding")
        assert result == EvidenceType.RESEARCH_FINDING

    def test_parse_news_report(self, evaluator):
        """Should parse news_report type."""
        from local_deep_research.advanced_search_system.evidence.base_evidence import (
            EvidenceType,
        )

        result = evaluator._parse_evidence_type("news_report")
        assert result == EvidenceType.NEWS_REPORT

    def test_parse_statistical_data(self, evaluator):
        """Should parse statistical_data type."""
        from local_deep_research.advanced_search_system.evidence.base_evidence import (
            EvidenceType,
        )

        result = evaluator._parse_evidence_type("statistical_data")
        assert result == EvidenceType.STATISTICAL_DATA

    def test_parse_inference(self, evaluator):
        """Should parse inference type."""
        from local_deep_research.advanced_search_system.evidence.base_evidence import (
            EvidenceType,
        )

        result = evaluator._parse_evidence_type("inference")
        assert result == EvidenceType.INFERENCE

    def test_parse_correlation(self, evaluator):
        """Should parse correlation type."""
        from local_deep_research.advanced_search_system.evidence.base_evidence import (
            EvidenceType,
        )

        result = evaluator._parse_evidence_type("correlation")
        assert result == EvidenceType.CORRELATION

    def test_parse_speculation(self, evaluator):
        """Should parse speculation type."""
        from local_deep_research.advanced_search_system.evidence.base_evidence import (
            EvidenceType,
        )

        result = evaluator._parse_evidence_type("speculation")
        assert result == EvidenceType.SPECULATION

    def test_unknown_type_defaults_to_speculation(self, evaluator):
        """Unknown types should default to speculation."""
        from local_deep_research.advanced_search_system.evidence.base_evidence import (
            EvidenceType,
        )

        result = evaluator._parse_evidence_type("unknown_type")
        assert result == EvidenceType.SPECULATION

    def test_empty_type_defaults_to_speculation(self, evaluator):
        """Empty type string should default to speculation."""
        from local_deep_research.advanced_search_system.evidence.base_evidence import (
            EvidenceType,
        )

        result = evaluator._parse_evidence_type("")
        assert result == EvidenceType.SPECULATION

    def test_type_parsing_case_insensitive(self, evaluator):
        """Type parsing should be case insensitive."""
        from local_deep_research.advanced_search_system.evidence.base_evidence import (
            EvidenceType,
        )

        result1 = evaluator._parse_evidence_type("DIRECT_STATEMENT")
        result2 = evaluator._parse_evidence_type("Direct_Statement")
        result3 = evaluator._parse_evidence_type("direct_statement")

        assert result1 == EvidenceType.DIRECT_STATEMENT
        assert result2 == EvidenceType.DIRECT_STATEMENT
        assert result3 == EvidenceType.DIRECT_STATEMENT


class TestMatchQualityAssessment:
    """Integration tests for match quality assessment."""

    @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 create_evidence(self):
        """Factory for creating Evidence objects."""
        from local_deep_research.advanced_search_system.evidence.base_evidence import (
            Evidence,
            EvidenceType,
        )

        def _create(claim):
            return Evidence(
                claim=claim,
                type=EvidenceType.DIRECT_STATEMENT,
                source="Test",
                confidence=0.9,
            )

        return _create

    @pytest.fixture
    def create_constraint(self):
        """Factory for creating Constraint objects."""
        from local_deep_research.advanced_search_system.constraints.base_constraint import (
            Constraint,
            ConstraintType,
        )

        def _create(value):
            return Constraint(
                id="c1",
                type=ConstraintType.PROPERTY,
                description=f"Must match {value}",
                value=value,
            )

        return _create

    def test_exact_match_returns_full_quality(
        self, evaluator, create_evidence, create_constraint
    ):
        """Exact value match should return 1.0 quality."""
        evidence = create_evidence("The trail goes through mountain range")
        constraint = create_constraint("mountain range")

        quality = evaluator._assess_match_quality(evidence, constraint)

        assert quality == 1.0

    def test_partial_word_match_returns_reduced_quality(
        self, evaluator, create_evidence, create_constraint
    ):
        """Partial word match should return 0.8 quality."""
        evidence = create_evidence("The trail has beautiful mountain views")
        constraint = create_constraint("mountain range")

        quality = evaluator._assess_match_quality(evidence, constraint)

        # "mountain" matches but not "mountain range"
        assert quality == 0.8

    def test_no_match_returns_minimum_quality(
        self, evaluator, create_evidence, create_constraint
    ):
        """No match should return 0.6 quality."""
        evidence = create_evidence("The trail is located in a valley")
        constraint = create_constraint("mountain range")

        quality = evaluator._assess_match_quality(evidence, constraint)

        assert quality == 0.6

    def test_case_insensitive_matching(
        self, evaluator, create_evidence, create_constraint
    ):
        """Matching should be case insensitive."""
        evidence = create_evidence("The MOUNTAIN RANGE is impressive")
        constraint = create_constraint("mountain range")

        quality = evaluator._assess_match_quality(evidence, constraint)

        assert quality == 1.0


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_complete_response(self, evaluator):
        """Should parse all fields from complete response."""
        response = """CLAIM: The candidate meets requirements
TYPE: official_record
SOURCE: Government database
CONFIDENCE: 0.95
REASONING: Verified through official channels
QUOTE: "Record shows compliance"
"""
        parsed = evaluator._parse_evidence_response(response)

        assert parsed["claim"] == "The candidate meets requirements"
        assert parsed["type"] == "official_record"
        assert parsed["source"] == "Government database"
        assert parsed["confidence"] == "0.95"
        assert parsed["reasoning"] == "Verified through official channels"
        assert parsed["quote"] == '"Record shows compliance"'

    def test_parse_response_missing_fields(self, evaluator):
        """Should handle missing fields gracefully."""
        response = """CLAIM: Partial evidence
CONFIDENCE: 0.7"""

        parsed = evaluator._parse_evidence_response(response)

        assert "claim" in parsed
        assert "confidence" in parsed
        assert "type" not in parsed
        assert "source" not in parsed

    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_confidence_with_text(self, evaluator):
        """Should extract confidence from text with numbers."""
        response = """CLAIM: Test
CONFIDENCE: 0.85 (high confidence based on sources)"""

        parsed = evaluator._parse_evidence_response(response)

        # Should extract just the number
        assert parsed["confidence"] == "0.85"

    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
