"""
Tests for advanced_search_system base classes.

Tests cover:
- Constraint and ConstraintType
- Candidate class
- Evidence and EvidenceType
- BaseFilter abstract class
"""

import pytest
from unittest.mock import Mock


class TestConstraintType:
    """Tests for ConstraintType enum."""

    def test_constraint_type_values(self):
        """Test all constraint type enum values exist."""
        from local_deep_research.advanced_search_system.constraints.base_constraint import (
            ConstraintType,
        )

        assert ConstraintType.PROPERTY.value == "property"
        assert ConstraintType.NAME_PATTERN.value == "name_pattern"
        assert ConstraintType.EVENT.value == "event"
        assert ConstraintType.STATISTIC.value == "statistic"
        assert ConstraintType.TEMPORAL.value == "temporal"
        assert ConstraintType.LOCATION.value == "location"
        assert ConstraintType.COMPARISON.value == "comparison"
        assert ConstraintType.EXISTENCE.value == "existence"

    def test_constraint_type_count(self):
        """Test that all constraint types are defined."""
        from local_deep_research.advanced_search_system.constraints.base_constraint import (
            ConstraintType,
        )

        assert len(ConstraintType) == 8


class TestConstraint:
    """Tests for Constraint dataclass."""

    def test_constraint_creation_minimal(self):
        """Test creating constraint with minimal required fields."""
        from local_deep_research.advanced_search_system.constraints.base_constraint import (
            Constraint,
            ConstraintType,
        )

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

        assert constraint.id == "c1"
        assert constraint.type == ConstraintType.PROPERTY
        assert constraint.description == "Test constraint"
        assert constraint.value == "test value"
        assert constraint.weight == 1.0
        assert constraint.metadata == {}

    def test_constraint_creation_full(self):
        """Test creating constraint with all fields."""
        from local_deep_research.advanced_search_system.constraints.base_constraint import (
            Constraint,
            ConstraintType,
        )

        metadata = {"source": "test"}
        constraint = Constraint(
            id="c2",
            type=ConstraintType.TEMPORAL,
            description="Time constraint",
            value="2024",
            weight=0.5,
            metadata=metadata,
        )

        assert constraint.id == "c2"
        assert constraint.weight == 0.5
        assert constraint.metadata == metadata

    def test_constraint_post_init_creates_metadata(self):
        """Test that __post_init__ creates empty metadata dict."""
        from local_deep_research.advanced_search_system.constraints.base_constraint import (
            Constraint,
            ConstraintType,
        )

        constraint = Constraint(
            id="c3",
            type=ConstraintType.LOCATION,
            description="Location",
            value="Colorado",
        )

        assert constraint.metadata is not None
        assert isinstance(constraint.metadata, dict)

    def test_constraint_to_search_terms_property(self):
        """Test to_search_terms for PROPERTY type."""
        from local_deep_research.advanced_search_system.constraints.base_constraint import (
            Constraint,
            ConstraintType,
        )

        constraint = Constraint(
            id="c1",
            type=ConstraintType.PROPERTY,
            description="Test",
            value="formed during ice age",
        )

        result = constraint.to_search_terms()
        assert result == "formed during ice age"

    def test_constraint_to_search_terms_name_pattern(self):
        """Test to_search_terms for NAME_PATTERN type."""
        from local_deep_research.advanced_search_system.constraints.base_constraint import (
            Constraint,
            ConstraintType,
        )

        constraint = Constraint(
            id="c1",
            type=ConstraintType.NAME_PATTERN,
            description="Test",
            value="body part",
        )

        result = constraint.to_search_terms()
        assert "body part" in result
        assert "name" in result
        assert "trail" in result

    def test_constraint_to_search_terms_event(self):
        """Test to_search_terms for EVENT type."""
        from local_deep_research.advanced_search_system.constraints.base_constraint import (
            Constraint,
            ConstraintType,
        )

        constraint = Constraint(
            id="c1",
            type=ConstraintType.EVENT,
            description="Test",
            value="rockslide 2020",
        )

        result = constraint.to_search_terms()
        assert "rockslide 2020" in result
        assert "accident" in result or "incident" in result

    def test_constraint_to_search_terms_statistic(self):
        """Test to_search_terms for STATISTIC type."""
        from local_deep_research.advanced_search_system.constraints.base_constraint import (
            Constraint,
            ConstraintType,
        )

        constraint = Constraint(
            id="c1",
            type=ConstraintType.STATISTIC,
            description="Test",
            value="84.5x ratio",
        )

        result = constraint.to_search_terms()
        assert "84.5x ratio" in result
        assert "statistics" in result or "data" in result

    def test_constraint_to_search_terms_other(self):
        """Test to_search_terms for other types (e.g., TEMPORAL)."""
        from local_deep_research.advanced_search_system.constraints.base_constraint import (
            Constraint,
            ConstraintType,
        )

        constraint = Constraint(
            id="c1",
            type=ConstraintType.TEMPORAL,
            description="Test",
            value="2024",
        )

        result = constraint.to_search_terms()
        assert result == "2024"

    def test_constraint_is_critical_name_pattern(self):
        """Test is_critical returns True for NAME_PATTERN type."""
        from local_deep_research.advanced_search_system.constraints.base_constraint import (
            Constraint,
            ConstraintType,
        )

        constraint = Constraint(
            id="c1",
            type=ConstraintType.NAME_PATTERN,
            description="Test",
            value="test",
            weight=0.5,  # Low weight but still critical
        )

        assert constraint.is_critical() is True

    def test_constraint_is_critical_high_weight(self):
        """Test is_critical returns True for high weight constraints."""
        from local_deep_research.advanced_search_system.constraints.base_constraint import (
            Constraint,
            ConstraintType,
        )

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

        assert constraint.is_critical() is True

    def test_constraint_is_critical_low_weight(self):
        """Test is_critical returns False for low weight non-NAME_PATTERN."""
        from local_deep_research.advanced_search_system.constraints.base_constraint import (
            Constraint,
            ConstraintType,
        )

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

        assert constraint.is_critical() is False


class TestEvidenceType:
    """Tests for EvidenceType enum."""

    def test_evidence_type_values(self):
        """Test all evidence type enum values exist."""
        from local_deep_research.advanced_search_system.evidence.base_evidence import (
            EvidenceType,
        )

        assert EvidenceType.DIRECT_STATEMENT.value == "direct_statement"
        assert EvidenceType.OFFICIAL_RECORD.value == "official_record"
        assert EvidenceType.RESEARCH_FINDING.value == "research_finding"
        assert EvidenceType.NEWS_REPORT.value == "news_report"
        assert EvidenceType.STATISTICAL_DATA.value == "statistical_data"
        assert EvidenceType.INFERENCE.value == "inference"
        assert EvidenceType.CORRELATION.value == "correlation"
        assert EvidenceType.SPECULATION.value == "speculation"

    def test_evidence_type_base_confidence(self):
        """Test base confidence values for evidence types."""
        from local_deep_research.advanced_search_system.evidence.base_evidence import (
            EvidenceType,
        )

        assert EvidenceType.DIRECT_STATEMENT.base_confidence == 0.95
        assert EvidenceType.OFFICIAL_RECORD.base_confidence == 0.90
        assert EvidenceType.RESEARCH_FINDING.base_confidence == 0.85
        assert EvidenceType.STATISTICAL_DATA.base_confidence == 0.85
        assert EvidenceType.NEWS_REPORT.base_confidence == 0.75
        assert EvidenceType.INFERENCE.base_confidence == 0.50
        assert EvidenceType.CORRELATION.base_confidence == 0.30
        assert EvidenceType.SPECULATION.base_confidence == 0.10


class TestEvidence:
    """Tests for Evidence dataclass."""

    def test_evidence_creation_minimal(self):
        """Test creating evidence with minimal fields."""
        from local_deep_research.advanced_search_system.evidence.base_evidence import (
            Evidence,
            EvidenceType,
        )

        evidence = Evidence(
            claim="Test claim",
            type=EvidenceType.NEWS_REPORT,
            source="https://example.com",
        )

        assert evidence.claim == "Test claim"
        assert evidence.type == EvidenceType.NEWS_REPORT
        assert evidence.source == "https://example.com"
        # Post-init should set confidence based on type
        assert evidence.confidence == 0.75

    def test_evidence_creation_with_confidence(self):
        """Test creating evidence with explicit confidence."""
        from local_deep_research.advanced_search_system.evidence.base_evidence import (
            Evidence,
            EvidenceType,
        )

        evidence = Evidence(
            claim="Test claim",
            type=EvidenceType.NEWS_REPORT,
            source="https://example.com",
            confidence=0.9,
        )

        # Should keep provided confidence, not override
        assert evidence.confidence == 0.9

    def test_evidence_creation_full(self):
        """Test creating evidence with all fields."""
        from local_deep_research.advanced_search_system.evidence.base_evidence import (
            Evidence,
            EvidenceType,
        )

        evidence = Evidence(
            claim="Test claim",
            type=EvidenceType.OFFICIAL_RECORD,
            source="https://official.gov",
            confidence=0.95,
            reasoning="Found in official database",
            raw_text="The official record states...",
            metadata={"page": 1},
        )

        assert evidence.claim == "Test claim"
        assert evidence.reasoning == "Found in official database"
        assert evidence.raw_text == "The official record states..."
        assert evidence.metadata == {"page": 1}

    def test_evidence_timestamp_auto_generated(self):
        """Test that timestamp is automatically generated."""
        from local_deep_research.advanced_search_system.evidence.base_evidence import (
            Evidence,
            EvidenceType,
        )

        evidence = Evidence(
            claim="Test",
            type=EvidenceType.INFERENCE,
            source="test",
        )

        assert evidence.timestamp is not None
        assert len(evidence.timestamp) > 0

    def test_evidence_post_init_sets_confidence(self):
        """Test __post_init__ sets confidence based on type when 0."""
        from local_deep_research.advanced_search_system.evidence.base_evidence import (
            Evidence,
            EvidenceType,
        )

        evidence = Evidence(
            claim="Test",
            type=EvidenceType.DIRECT_STATEMENT,
            source="test",
            confidence=0.0,  # Will be replaced
        )

        assert evidence.confidence == 0.95


class TestCandidate:
    """Tests for Candidate dataclass."""

    def test_candidate_creation_minimal(self):
        """Test creating candidate with minimal fields."""
        from local_deep_research.advanced_search_system.candidates.base_candidate import (
            Candidate,
        )

        candidate = Candidate(name="Test Candidate")

        assert candidate.name == "Test Candidate"
        assert candidate.evidence == {}
        assert candidate.score == 0.0
        assert candidate.metadata == {}

    def test_candidate_add_evidence(self):
        """Test adding evidence to a candidate."""
        from local_deep_research.advanced_search_system.candidates.base_candidate import (
            Candidate,
        )
        from local_deep_research.advanced_search_system.evidence.base_evidence import (
            Evidence,
            EvidenceType,
        )

        candidate = Candidate(name="Test")
        evidence = Evidence(
            claim="Test claim",
            type=EvidenceType.NEWS_REPORT,
            source="https://example.com",
        )

        candidate.add_evidence("constraint_1", evidence)

        assert "constraint_1" in candidate.evidence
        assert candidate.evidence["constraint_1"] == evidence

    def test_candidate_calculate_score_empty(self):
        """Test score calculation with no constraints."""
        from local_deep_research.advanced_search_system.candidates.base_candidate import (
            Candidate,
        )

        candidate = Candidate(name="Test")
        score = candidate.calculate_score([])

        assert score == 0.0

    def test_candidate_calculate_score_with_evidence(self):
        """Test score calculation with evidence."""
        from local_deep_research.advanced_search_system.candidates.base_candidate import (
            Candidate,
        )
        from local_deep_research.advanced_search_system.constraints.base_constraint import (
            Constraint,
            ConstraintType,
        )
        from local_deep_research.advanced_search_system.evidence.base_evidence import (
            Evidence,
            EvidenceType,
        )

        candidate = Candidate(name="Test")

        # Create constraints
        c1 = Constraint(
            id="c1",
            type=ConstraintType.PROPERTY,
            description="Test 1",
            value="test",
            weight=1.0,
        )
        c2 = Constraint(
            id="c2",
            type=ConstraintType.PROPERTY,
            description="Test 2",
            value="test",
            weight=1.0,
        )

        # Add evidence with 0.8 confidence
        evidence = Evidence(
            claim="Test",
            type=EvidenceType.NEWS_REPORT,
            source="test",
            confidence=0.8,
        )
        candidate.add_evidence("c1", evidence)

        score = candidate.calculate_score([c1, c2])

        # Score = (0.8 * 1.0) / (1.0 + 1.0) = 0.4
        assert score == 0.4
        assert candidate.score == 0.4

    def test_candidate_calculate_score_weighted(self):
        """Test score calculation with weighted constraints."""
        from local_deep_research.advanced_search_system.candidates.base_candidate import (
            Candidate,
        )
        from local_deep_research.advanced_search_system.constraints.base_constraint import (
            Constraint,
            ConstraintType,
        )
        from local_deep_research.advanced_search_system.evidence.base_evidence import (
            Evidence,
            EvidenceType,
        )

        candidate = Candidate(name="Test")

        # Create constraints with different weights
        c1 = Constraint(
            id="c1",
            type=ConstraintType.PROPERTY,
            description="Critical",
            value="test",
            weight=2.0,
        )
        c2 = Constraint(
            id="c2",
            type=ConstraintType.PROPERTY,
            description="Minor",
            value="test",
            weight=0.5,
        )

        # Add evidence for c1 only
        evidence = Evidence(
            claim="Test",
            type=EvidenceType.DIRECT_STATEMENT,
            source="test",
            confidence=1.0,
        )
        candidate.add_evidence("c1", evidence)

        score = candidate.calculate_score([c1, c2])

        # Score = (1.0 * 2.0) / (2.0 + 0.5) = 2.0 / 2.5 = 0.8
        assert score == 0.8

    def test_candidate_get_unverified_constraints(self):
        """Test getting unverified constraints."""
        from local_deep_research.advanced_search_system.candidates.base_candidate import (
            Candidate,
        )
        from local_deep_research.advanced_search_system.constraints.base_constraint import (
            Constraint,
            ConstraintType,
        )
        from local_deep_research.advanced_search_system.evidence.base_evidence import (
            Evidence,
            EvidenceType,
        )

        candidate = Candidate(name="Test")

        c1 = Constraint(
            id="c1",
            type=ConstraintType.PROPERTY,
            description="Test 1",
            value="test",
        )
        c2 = Constraint(
            id="c2",
            type=ConstraintType.PROPERTY,
            description="Test 2",
            value="test",
        )
        c3 = Constraint(
            id="c3",
            type=ConstraintType.PROPERTY,
            description="Test 3",
            value="test",
        )

        # Add evidence for c1 only
        evidence = Evidence(
            claim="Test",
            type=EvidenceType.NEWS_REPORT,
            source="test",
        )
        candidate.add_evidence("c1", evidence)

        unverified = candidate.get_unverified_constraints([c1, c2, c3])

        assert len(unverified) == 2
        assert c2 in unverified
        assert c3 in unverified
        assert c1 not in unverified

    def test_candidate_get_weak_evidence(self):
        """Test getting constraints with weak evidence."""
        from local_deep_research.advanced_search_system.candidates.base_candidate import (
            Candidate,
        )
        from local_deep_research.advanced_search_system.evidence.base_evidence import (
            Evidence,
            EvidenceType,
        )

        candidate = Candidate(name="Test")

        # Add strong evidence
        strong = Evidence(
            claim="Strong",
            type=EvidenceType.DIRECT_STATEMENT,
            source="test",
            confidence=0.9,
        )
        candidate.add_evidence("c1", strong)

        # Add weak evidence
        weak = Evidence(
            claim="Weak",
            type=EvidenceType.SPECULATION,
            source="test",
            confidence=0.3,
        )
        candidate.add_evidence("c2", weak)

        weak_constraints = candidate.get_weak_evidence(threshold=0.5)

        assert len(weak_constraints) == 1
        assert "c2" in weak_constraints
        assert "c1" not in weak_constraints

    def test_candidate_get_weak_evidence_custom_threshold(self):
        """Test getting weak evidence with custom threshold."""
        from local_deep_research.advanced_search_system.candidates.base_candidate import (
            Candidate,
        )
        from local_deep_research.advanced_search_system.evidence.base_evidence import (
            Evidence,
            EvidenceType,
        )

        candidate = Candidate(name="Test")

        evidence = Evidence(
            claim="Medium",
            type=EvidenceType.NEWS_REPORT,
            source="test",
            confidence=0.7,
        )
        candidate.add_evidence("c1", evidence)

        # With default threshold of 0.5, should not be weak
        weak_default = candidate.get_weak_evidence()
        assert "c1" not in weak_default

        # With higher threshold, should be weak
        weak_high = candidate.get_weak_evidence(threshold=0.8)
        assert "c1" in weak_high


class TestBaseFilter:
    """Tests for BaseFilter abstract class."""

    def test_base_filter_is_abstract(self):
        """Test that BaseFilter is abstract and cannot be instantiated."""
        from local_deep_research.advanced_search_system.filters.base_filter import (
            BaseFilter,
        )

        with pytest.raises(TypeError):
            BaseFilter()

    def test_base_filter_with_model(self):
        """Test BaseFilter subclass with model."""
        from local_deep_research.advanced_search_system.filters.base_filter import (
            BaseFilter,
        )

        class ConcreteFilter(BaseFilter):
            def filter_results(self, results, query, **kwargs):
                return results

        mock_model = Mock()
        filter_instance = ConcreteFilter(model=mock_model)

        assert filter_instance.model == mock_model

    def test_base_filter_without_model(self):
        """Test BaseFilter subclass without model."""
        from local_deep_research.advanced_search_system.filters.base_filter import (
            BaseFilter,
        )

        class ConcreteFilter(BaseFilter):
            def filter_results(self, results, query, **kwargs):
                return results

        filter_instance = ConcreteFilter()
        assert filter_instance.model is None

    def test_base_filter_filter_results_abstract(self):
        """Test that filter_results must be implemented."""
        from local_deep_research.advanced_search_system.filters.base_filter import (
            BaseFilter,
        )

        class IncompleteFilter(BaseFilter):
            pass

        with pytest.raises(TypeError):
            IncompleteFilter()

    def test_base_filter_concrete_implementation(self):
        """Test a concrete filter implementation."""
        from local_deep_research.advanced_search_system.filters.base_filter import (
            BaseFilter,
        )

        class RelevanceFilter(BaseFilter):
            def filter_results(self, results, query, **kwargs):
                threshold = kwargs.get("threshold", 0.5)
                return [r for r in results if r.get("score", 0) >= threshold]

        filter_instance = RelevanceFilter()
        results = [
            {"title": "A", "score": 0.9},
            {"title": "B", "score": 0.3},
            {"title": "C", "score": 0.7},
        ]

        filtered = filter_instance.filter_results(results, "test query")

        assert len(filtered) == 2
        assert {"title": "A", "score": 0.9} in filtered
        assert {"title": "C", "score": 0.7} in filtered
