"""
Tests for ConstrainedSearchStrategy.

Tests cover:
- Initialization and inheritance
- Constraint ranking by restrictiveness
- Progressive constraint search
- Candidate filtering
- Evidence gathering
- Error handling
"""

from unittest.mock import Mock, patch


class TestConstrainedSearchStrategyInit:
    """Tests for ConstrainedSearchStrategy initialization."""

    def test_init_with_required_params(self):
        """Initialize with required parameters."""
        from local_deep_research.advanced_search_system.strategies.constrained_search_strategy import (
            ConstrainedSearchStrategy,
        )

        mock_search = Mock()
        mock_model = Mock()

        strategy = ConstrainedSearchStrategy(
            model=mock_model,
            search=mock_search,
            all_links_of_system=[],
        )

        assert strategy.model is mock_model
        assert strategy.search is mock_search
        assert strategy.use_direct_search is True

    def test_init_with_custom_params(self):
        """Initialize with custom parameters."""
        from local_deep_research.advanced_search_system.strategies.constrained_search_strategy import (
            ConstrainedSearchStrategy,
        )

        mock_search = Mock()
        mock_model = Mock()

        strategy = ConstrainedSearchStrategy(
            model=mock_model,
            search=mock_search,
            all_links_of_system=[],
            max_iterations=15,
            candidate_limit=50,
            min_candidates_per_stage=10,
        )

        assert strategy.max_iterations == 15
        assert strategy.candidate_limit == 50
        assert strategy.min_candidates_per_stage == 10

    def test_init_inherits_from_evidence_based(self):
        """Initialize inherits from EvidenceBasedStrategy."""
        from local_deep_research.advanced_search_system.strategies.constrained_search_strategy import (
            ConstrainedSearchStrategy,
        )
        from local_deep_research.advanced_search_system.strategies.evidence_based_strategy import (
            EvidenceBasedStrategy,
        )

        mock_search = Mock()
        mock_model = Mock()

        strategy = ConstrainedSearchStrategy(
            model=mock_model,
            search=mock_search,
            all_links_of_system=[],
        )

        assert isinstance(strategy, EvidenceBasedStrategy)

    def test_init_state_tracking(self):
        """Initialize state tracking attributes."""
        from local_deep_research.advanced_search_system.strategies.constrained_search_strategy import (
            ConstrainedSearchStrategy,
        )

        mock_search = Mock()
        mock_model = Mock()

        strategy = ConstrainedSearchStrategy(
            model=mock_model,
            search=mock_search,
            all_links_of_system=[],
        )

        assert strategy.constraint_ranking == []
        assert strategy.stage_candidates == {}


class TestConstraintRanking:
    """Tests for constraint ranking methods."""

    def test_rank_constraints_by_restrictiveness(self):
        """Rank constraints by restrictiveness score."""
        from local_deep_research.advanced_search_system.strategies.constrained_search_strategy import (
            ConstrainedSearchStrategy,
        )
        from local_deep_research.advanced_search_system.constraints.base_constraint import (
            Constraint,
            ConstraintType,
        )

        mock_search = Mock()
        mock_model = Mock()

        strategy = ConstrainedSearchStrategy(
            model=mock_model,
            search=mock_search,
            all_links_of_system=[],
        )

        # Create constraints with different types
        constraints = [
            Constraint(
                id="1",
                type=ConstraintType.PROPERTY,
                value="property value",
                description="Property",
                weight=0.5,
            ),
            Constraint(
                id="2",
                type=ConstraintType.STATISTIC,
                value="123 specific number",
                description="Statistic",
                weight=0.8,
            ),
            Constraint(
                id="3",
                type=ConstraintType.EVENT,
                value="event 2020",
                description="Event",
                weight=0.7,
            ),
        ]
        strategy.constraints = constraints

        ranked = strategy._rank_constraints_by_restrictiveness()

        # Statistics should be ranked higher
        assert len(ranked) == 3
        assert ranked[0].type == ConstraintType.STATISTIC

    def test_calculate_restrictiveness_score_statistic(self):
        """Calculate restrictiveness score for statistic constraint."""
        from local_deep_research.advanced_search_system.strategies.constrained_search_strategy import (
            ConstrainedSearchStrategy,
        )
        from local_deep_research.advanced_search_system.constraints.base_constraint import (
            Constraint,
            ConstraintType,
        )

        mock_search = Mock()
        mock_model = Mock()

        strategy = ConstrainedSearchStrategy(
            model=mock_model,
            search=mock_search,
            all_links_of_system=[],
        )

        constraint = Constraint(
            id="1",
            type=ConstraintType.STATISTIC,
            value="123",
            description="Test statistic",
            weight=0.8,
        )

        score = strategy._calculate_restrictiveness_score(constraint)

        # Statistic type gives +10, digits give +5
        assert score >= 15

    def test_calculate_restrictiveness_score_property(self):
        """Calculate restrictiveness score for property constraint."""
        from local_deep_research.advanced_search_system.strategies.constrained_search_strategy import (
            ConstrainedSearchStrategy,
        )
        from local_deep_research.advanced_search_system.constraints.base_constraint import (
            Constraint,
            ConstraintType,
        )

        mock_search = Mock()
        mock_model = Mock()

        strategy = ConstrainedSearchStrategy(
            model=mock_model,
            search=mock_search,
            all_links_of_system=[],
        )

        constraint = Constraint(
            id="1",
            type=ConstraintType.PROPERTY,
            value="simple",
            description="Test property",
            weight=0.5,
        )

        score = strategy._calculate_restrictiveness_score(constraint)

        # Property type gives +4
        assert score >= 4


class TestProgressiveSearch:
    """Tests for progressive constraint search methods."""

    def test_progressive_constraint_search(self):
        """Progressive constraint search processes stages."""
        from local_deep_research.advanced_search_system.strategies.constrained_search_strategy import (
            ConstrainedSearchStrategy,
        )
        from local_deep_research.advanced_search_system.constraints.base_constraint import (
            Constraint,
            ConstraintType,
        )

        mock_search = Mock()
        mock_search.run.return_value = []
        mock_model = Mock()
        mock_model.invoke.return_value = Mock(content="CANDIDATE_1: Test")

        strategy = ConstrainedSearchStrategy(
            model=mock_model,
            search=mock_search,
            all_links_of_system=[],
        )

        constraint = Constraint(
            id="1",
            type=ConstraintType.PROPERTY,
            value="test",
            description="Test constraint",
            weight=0.5,
        )
        strategy.constraint_ranking = [constraint]
        strategy.findings = []  # Initialize findings list

        strategy._progressive_constraint_search()

        assert len(strategy.stage_candidates) >= 0

    def test_generate_constraint_specific_queries(self):
        """Generate constraint-specific queries."""
        from local_deep_research.advanced_search_system.strategies.constrained_search_strategy import (
            ConstrainedSearchStrategy,
        )
        from local_deep_research.advanced_search_system.constraints.base_constraint import (
            Constraint,
            ConstraintType,
        )

        mock_search = Mock()
        mock_model = Mock()

        strategy = ConstrainedSearchStrategy(
            model=mock_model,
            search=mock_search,
            all_links_of_system=[],
        )

        constraint = Constraint(
            id="1",
            type=ConstraintType.STATISTIC,
            value="100 meters",
            description="Height constraint",
            weight=0.8,
        )
        strategy.constraints = [constraint]

        queries = strategy._generate_constraint_specific_queries(constraint)

        assert isinstance(queries, list)
        assert len(queries) > 0
        # Should contain common patterns for statistics
        assert any("100 meters" in q for q in queries)

    def test_generate_additional_queries(self):
        """Generate additional diverse queries."""
        from local_deep_research.advanced_search_system.strategies.constrained_search_strategy import (
            ConstrainedSearchStrategy,
        )
        from local_deep_research.advanced_search_system.constraints.base_constraint import (
            Constraint,
            ConstraintType,
        )

        mock_search = Mock()
        mock_model = Mock()

        strategy = ConstrainedSearchStrategy(
            model=mock_model,
            search=mock_search,
            all_links_of_system=[],
        )

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

        queries = strategy._generate_additional_queries(constraint)

        assert isinstance(queries, list)
        # Should include reference source queries
        assert any(
            "reference" in q.lower() or "authoritative" in q.lower()
            for q in queries
        )


class TestCandidateExtraction:
    """Tests for candidate extraction methods."""

    def test_extract_relevant_candidates(self):
        """Extract relevant candidates from search results."""
        from local_deep_research.advanced_search_system.strategies.constrained_search_strategy import (
            ConstrainedSearchStrategy,
        )
        from local_deep_research.advanced_search_system.constraints.base_constraint import (
            Constraint,
            ConstraintType,
        )

        mock_search = Mock()
        mock_model = Mock()
        mock_model.invoke.return_value = Mock(
            content="Candidate A\nCandidate B\nCandidate C"
        )

        strategy = ConstrainedSearchStrategy(
            model=mock_model,
            search=mock_search,
            all_links_of_system=[],
        )

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

        results = {"current_knowledge": "Information about candidates"}

        candidates = strategy._extract_relevant_candidates(results, constraint)

        assert isinstance(candidates, list)

    def test_extract_relevant_candidates_empty_content(self):
        """Extract candidates returns empty list for empty content."""
        from local_deep_research.advanced_search_system.strategies.constrained_search_strategy import (
            ConstrainedSearchStrategy,
        )
        from local_deep_research.advanced_search_system.constraints.base_constraint import (
            Constraint,
            ConstraintType,
        )

        mock_search = Mock()
        mock_model = Mock()

        strategy = ConstrainedSearchStrategy(
            model=mock_model,
            search=mock_search,
            all_links_of_system=[],
        )

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

        results = {"current_knowledge": ""}

        candidates = strategy._extract_relevant_candidates(results, constraint)

        assert candidates == []

    def test_deduplicate_candidates(self):
        """Deduplicate candidates by name."""
        from local_deep_research.advanced_search_system.strategies.constrained_search_strategy import (
            ConstrainedSearchStrategy,
        )
        from local_deep_research.advanced_search_system.candidates.base_candidate import (
            Candidate,
        )

        mock_search = Mock()
        mock_model = Mock()

        strategy = ConstrainedSearchStrategy(
            model=mock_model,
            search=mock_search,
            all_links_of_system=[],
        )

        candidates = [
            Candidate(name="Test A"),
            Candidate(name="Test B"),
            Candidate(name="test a"),  # Duplicate with different case
            Candidate(name="Test C"),
        ]

        unique = strategy._deduplicate_candidates(candidates)

        # Should have 3 unique candidates
        assert len(unique) == 3


class TestCandidateFiltering:
    """Tests for candidate filtering methods."""

    def test_filter_candidates_with_constraint(self):
        """Filter candidates with constraint check."""
        from local_deep_research.advanced_search_system.strategies.constrained_search_strategy import (
            ConstrainedSearchStrategy,
        )
        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,
        )

        mock_search = Mock()
        mock_search.run.return_value = [
            {"snippet": "Test candidate matches constraint"}
        ]
        mock_model = Mock()

        strategy = ConstrainedSearchStrategy(
            model=mock_model,
            search=mock_search,
            all_links_of_system=[],
        )

        candidates = [
            Candidate(name="Candidate A"),
            Candidate(name="Candidate B"),
        ]

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

        # Mock quick evidence check
        with patch.object(
            strategy,
            "_quick_evidence_check",
            return_value=Mock(confidence=0.8),
        ):
            filtered = strategy._filter_candidates_with_constraint(
                candidates, constraint
            )

        assert isinstance(filtered, list)

    def test_quick_evidence_check(self):
        """Quick evidence check calculates confidence."""
        from local_deep_research.advanced_search_system.strategies.constrained_search_strategy import (
            ConstrainedSearchStrategy,
        )
        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,
        )

        mock_search = Mock()
        mock_model = Mock()

        strategy = ConstrainedSearchStrategy(
            model=mock_model,
            search=mock_search,
            all_links_of_system=[],
        )

        candidate = Candidate(name="Test Entity")
        constraint = Constraint(
            id="1",
            type=ConstraintType.PROPERTY,
            value="has feature",
            description="Feature constraint",
            weight=0.5,
        )

        results = {
            "current_knowledge": "Test Entity has feature and is well known.",
            "search_results": [],
        }

        evidence = strategy._quick_evidence_check(
            results, candidate, constraint
        )

        assert hasattr(evidence, "confidence")
        assert 0 <= evidence.confidence <= 1


class TestEvidenceGathering:
    """Tests for evidence gathering methods."""

    def test_focused_evidence_gathering(self):
        """Focused evidence gathering verifies candidates."""
        from local_deep_research.advanced_search_system.strategies.constrained_search_strategy import (
            ConstrainedSearchStrategy,
        )
        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,
        )

        mock_search = Mock()
        mock_search.run.return_value = []
        mock_model = Mock()

        strategy = ConstrainedSearchStrategy(
            model=mock_model,
            search=mock_search,
            all_links_of_system=[],
        )

        candidate = Candidate(name="Test")
        strategy.candidates = [candidate]

        constraint = Constraint(
            id="1",
            type=ConstraintType.PROPERTY,
            value="test",
            description="Test",
            weight=0.5,
        )
        strategy.constraints = [constraint]

        # Mock evidence evaluator
        with patch.object(
            strategy.evidence_evaluator,
            "extract_evidence",
            return_value=Mock(confidence=0.8, type=Mock(value="inference")),
        ):
            strategy._focused_evidence_gathering()

        # Candidates should be scored and sorted
        assert len(strategy.candidates) > 0


class TestResultValidation:
    """Tests for search result validation."""

    def test_validate_search_results_valid(self):
        """Validate search results accepts valid content."""
        from local_deep_research.advanced_search_system.strategies.constrained_search_strategy import (
            ConstrainedSearchStrategy,
        )
        from local_deep_research.advanced_search_system.constraints.base_constraint import (
            Constraint,
            ConstraintType,
        )

        mock_search = Mock()
        mock_model = Mock()

        strategy = ConstrainedSearchStrategy(
            model=mock_model,
            search=mock_search,
            all_links_of_system=[],
        )

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

        results = {
            "current_knowledge": "This is a valid test value content with enough information.",
            "search_results": [{"title": "Test", "snippet": "Content"}],
        }

        is_valid = strategy._validate_search_results(results, constraint)

        assert is_valid is True

    def test_validate_search_results_empty(self):
        """Validate search results rejects empty content."""
        from local_deep_research.advanced_search_system.strategies.constrained_search_strategy import (
            ConstrainedSearchStrategy,
        )
        from local_deep_research.advanced_search_system.constraints.base_constraint import (
            Constraint,
            ConstraintType,
        )

        mock_search = Mock()
        mock_model = Mock()

        strategy = ConstrainedSearchStrategy(
            model=mock_model,
            search=mock_search,
            all_links_of_system=[],
        )

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

        results = {
            "current_knowledge": "",
            "search_results": [],
        }

        is_valid = strategy._validate_search_results(results, constraint)

        assert is_valid is False

    def test_validate_search_results_no_results(self):
        """Validate search results rejects no results message."""
        from local_deep_research.advanced_search_system.strategies.constrained_search_strategy import (
            ConstrainedSearchStrategy,
        )
        from local_deep_research.advanced_search_system.constraints.base_constraint import (
            Constraint,
            ConstraintType,
        )

        mock_search = Mock()
        mock_model = Mock()

        strategy = ConstrainedSearchStrategy(
            model=mock_model,
            search=mock_search,
            all_links_of_system=[],
        )

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

        results = {
            "current_knowledge": "No results found for this query.",
            "search_results": [],
        }

        is_valid = strategy._validate_search_results(results, constraint)

        assert is_valid is False


class TestFormattingMethods:
    """Tests for formatting helper methods."""

    def test_format_constraint_analysis(self):
        """Format constraint analysis output."""
        from local_deep_research.advanced_search_system.strategies.constrained_search_strategy import (
            ConstrainedSearchStrategy,
        )
        from local_deep_research.advanced_search_system.constraints.base_constraint import (
            Constraint,
            ConstraintType,
        )

        mock_search = Mock()
        mock_model = Mock()

        strategy = ConstrainedSearchStrategy(
            model=mock_model,
            search=mock_search,
            all_links_of_system=[],
        )

        constraint = Constraint(
            id="1",
            type=ConstraintType.PROPERTY,
            value="test",
            description="Test constraint",
            weight=0.5,
        )
        strategy.constraints = [constraint]
        strategy.constraint_ranking = [constraint]

        analysis = strategy._format_constraint_analysis()

        assert "Constraint" in analysis
        assert "Test constraint" in analysis

    def test_format_stage_results(self):
        """Format stage results output."""
        from local_deep_research.advanced_search_system.strategies.constrained_search_strategy import (
            ConstrainedSearchStrategy,
        )
        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,
        )

        mock_search = Mock()
        mock_model = Mock()

        strategy = ConstrainedSearchStrategy(
            model=mock_model,
            search=mock_search,
            all_links_of_system=[],
        )

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

        candidates = [
            Candidate(name="Test A"),
            Candidate(name="Test B"),
        ]

        result = strategy._format_stage_results(0, constraint, candidates)

        assert "Stage 1" in result
        assert "Test A" in result or "Test B" in result

    def test_format_search_summary(self):
        """Format search summary output."""
        from local_deep_research.advanced_search_system.strategies.constrained_search_strategy import (
            ConstrainedSearchStrategy,
        )
        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,
        )

        mock_search = Mock()
        mock_model = Mock()

        strategy = ConstrainedSearchStrategy(
            model=mock_model,
            search=mock_search,
            all_links_of_system=[],
        )

        constraint = Constraint(
            id="1",
            type=ConstraintType.PROPERTY,
            value="test",
            description="Test constraint",
            weight=0.5,
        )
        strategy.constraint_ranking = [constraint]
        strategy.stage_candidates = {0: [Candidate(name="Test")]}
        strategy.candidates = [Candidate(name="Test")]

        summary = strategy._format_search_summary()

        assert "Summary" in summary

    def test_format_debug_summary(self):
        """Format debug summary output."""
        from local_deep_research.advanced_search_system.strategies.constrained_search_strategy import (
            ConstrainedSearchStrategy,
        )
        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,
        )

        mock_search = Mock()
        mock_model = Mock()

        strategy = ConstrainedSearchStrategy(
            model=mock_model,
            search=mock_search,
            all_links_of_system=[],
        )

        constraint = Constraint(
            id="1",
            type=ConstraintType.PROPERTY,
            value="test",
            description="Test constraint",
            weight=0.5,
        )
        strategy.constraints = [constraint]
        strategy.constraint_ranking = [constraint]
        strategy.stage_candidates = {0: [Candidate(name="Test")]}
        strategy.candidates = [Candidate(name="Test")]

        summary = strategy._format_debug_summary()

        assert "Debug" in summary


class TestCandidateGrouping:
    """Tests for candidate grouping methods."""

    def test_group_similar_candidates(self):
        """Group similar candidates by characteristics."""
        from local_deep_research.advanced_search_system.strategies.constrained_search_strategy import (
            ConstrainedSearchStrategy,
        )
        from local_deep_research.advanced_search_system.candidates.base_candidate import (
            Candidate,
        )

        mock_search = Mock()
        mock_model = Mock()

        strategy = ConstrainedSearchStrategy(
            model=mock_model,
            search=mock_search,
            all_links_of_system=[],
        )

        candidates = [
            Candidate(name="AI Model GPT"),
            Candidate(name="AI Model Claude"),
            Candidate(name="City New York"),
            Candidate(name="Year 2020"),
        ]

        grouped = strategy._group_similar_candidates(candidates)

        assert isinstance(grouped, dict)
        assert len(grouped) > 0


class TestSimpleSearch:
    """Tests for simple search fallback."""

    def test_simple_search(self):
        """Simple search returns formatted results."""
        from local_deep_research.advanced_search_system.strategies.constrained_search_strategy import (
            ConstrainedSearchStrategy,
        )

        mock_search = Mock()
        mock_search.run.return_value = [
            {
                "title": "Result 1",
                "snippet": "Content 1",
                "link": "http://test.com",
            },
        ]
        mock_model = Mock()

        strategy = ConstrainedSearchStrategy(
            model=mock_model,
            search=mock_search,
            all_links_of_system=[],
        )

        result = strategy._simple_search("test query")

        assert "current_knowledge" in result
        assert "search_results" in result
        assert "Result 1" in result["current_knowledge"]

    def test_simple_search_no_results(self):
        """Simple search handles no results."""
        from local_deep_research.advanced_search_system.strategies.constrained_search_strategy import (
            ConstrainedSearchStrategy,
        )

        mock_search = Mock()
        mock_search.run.return_value = []
        mock_model = Mock()

        strategy = ConstrainedSearchStrategy(
            model=mock_model,
            search=mock_search,
            all_links_of_system=[],
        )

        result = strategy._simple_search("test query")

        assert "No results found" in result["current_knowledge"]
