"""High-value tests for BrowseCompOptimizedStrategy pure logic methods."""

import pytest
from unittest.mock import patch

from local_deep_research.advanced_search_system.strategies.browsecomp_optimized_strategy import (
    BrowseCompOptimizedStrategy,
    QueryClues,
)


# --- QueryClues dataclass ---


class TestQueryClues:
    def test_defaults(self):
        clues = QueryClues()
        assert clues.location_clues == []
        assert clues.temporal_clues == []
        assert clues.numerical_clues == []
        assert clues.name_clues == []
        assert clues.incident_clues == []
        assert clues.comparison_clues == []
        assert clues.all_clues == []
        assert clues.query_type == "unknown"

    def test_custom_values(self):
        clues = QueryClues(
            location_clues=["Colorado"],
            query_type="location",
        )
        assert clues.location_clues == ["Colorado"]
        assert clues.query_type == "location"

    def test_mutable_defaults_independent(self):
        c1 = QueryClues()
        c2 = QueryClues()
        c1.location_clues.append("test")
        assert c2.location_clues == []


@pytest.fixture
def strategy():
    """Create a BrowseCompOptimizedStrategy with mocked init."""
    with patch.object(
        BrowseCompOptimizedStrategy, "__init__", lambda self, *a, **kw: None
    ):
        s = BrowseCompOptimizedStrategy.__new__(BrowseCompOptimizedStrategy)
        s.query_clues = QueryClues(
            location_clues=["near Grand Canyon"],
            temporal_clues=["2014"],
            numerical_clues=["84.5x ratio"],
            name_clues=["contains body part"],
            incident_clues=["hiker fell to death"],
            comparison_clues=["84.5 times Grand Canyon 2022"],
            all_clues=[
                "near Grand Canyon",
                "2014",
                "hiker fell",
                "body part name",
                "84.5x ratio",
            ],
        )
        s.confirmed_info = {}
        s.candidates = []
        s.search_history = []
        s.iteration = 0
        s.confidence_threshold = 0.90
        s.max_browsecomp_iterations = 15
        s.progress_callback = None
        return s


# --- _get_matched_clues ---


class TestGetMatchedClues:
    def test_name_clue_word_match(self, strategy):
        strategy.query_clues.name_clues = ["contains arm"]
        matched = strategy._get_matched_clues("Arm Creek")
        assert any("name:" in m for m in matched)

    def test_name_clue_no_match(self, strategy):
        strategy.query_clues.name_clues = ["contains arm"]
        matched = strategy._get_matched_clues("Silver Creek")
        assert not any("name:" in m for m in matched)

    def test_location_in_confirmed_info(self, strategy):
        strategy.query_clues.location_clues = ["near Grand Canyon"]
        strategy.confirmed_info = {
            "near Grand Canyon": "arm creek is near the grand canyon"
        }
        matched = strategy._get_matched_clues("Arm Creek")
        assert any("location:" in m for m in matched)

    def test_fact_match(self, strategy):
        strategy.confirmed_info = {"arm creek hiking trail": "scenic viewpoint"}
        matched = strategy._get_matched_clues("Arm Creek")
        assert any("fact:" in m for m in matched)

    def test_temporal_year_match(self, strategy):
        strategy.query_clues.temporal_clues = ["in 2014"]
        strategy.confirmed_info = {"arm creek 2014 accident": "confirmed"}
        matched = strategy._get_matched_clues("Arm Creek")
        assert any("temporal:" in m for m in matched)

    def test_incident_word_match(self, strategy):
        strategy.query_clues.incident_clues = ["hiker fell to death"]
        strategy.confirmed_info = {"arm creek hiker fell": "confirmed"}
        matched = strategy._get_matched_clues("Arm Creek")
        assert any("incident:" in m for m in matched)

    def test_deduplication(self, strategy):
        strategy.query_clues.name_clues = ["arm", "arm"]
        matched = strategy._get_matched_clues("Arm Creek")
        # Duplicates removed via set
        assert len(matched) == len(set(matched))


# --- _get_unverified_clues ---


class TestGetUnverifiedClues:
    def test_all_unverified(self, strategy):
        candidate = {"name": "Test", "matched_clues": []}
        unverified = strategy._get_unverified_clues(candidate)
        assert len(unverified) > 0

    def test_some_verified(self, strategy):
        candidate = {
            "name": "Test",
            "matched_clues": ["name: contains body part"],
        }
        strategy.query_clues.name_clues = ["contains body part"]
        strategy.query_clues.location_clues = ["near Grand Canyon"]
        strategy.query_clues.temporal_clues = []
        strategy.query_clues.numerical_clues = []
        strategy.query_clues.incident_clues = []
        strategy.query_clues.comparison_clues = []
        unverified = strategy._get_unverified_clues(candidate)
        assert "near Grand Canyon" in unverified

    def test_type_prefix_extracted(self, strategy):
        """Matched clues with 'type: clue' format should extract the clue text."""
        candidate = {
            "name": "Test",
            "matched_clues": ["location: near Grand Canyon"],
        }
        strategy.query_clues.location_clues = ["near Grand Canyon"]
        strategy.query_clues.temporal_clues = []
        strategy.query_clues.numerical_clues = []
        strategy.query_clues.name_clues = []
        strategy.query_clues.incident_clues = []
        strategy.query_clues.comparison_clues = []
        unverified = strategy._get_unverified_clues(candidate)
        assert "near Grand Canyon" not in unverified


# --- _evaluate_candidates ---


class TestEvaluateCandidates:
    def test_no_candidates_returns_false(self, strategy):
        strategy.candidates = []
        assert strategy._evaluate_candidates() is False

    def test_high_confidence_high_ratio(self, strategy):
        strategy.candidates = [
            {
                "name": "Test",
                "confidence": 0.92,
                "matched_clues": ["a", "b", "c", "d"],
            },
        ]
        strategy.query_clues.all_clues = ["a", "b", "c", "d", "e"]
        # 4/5 = 0.8 >= 0.8 and 0.92 >= 0.90
        assert strategy._evaluate_candidates() is True

    def test_high_confidence_low_ratio(self, strategy):
        strategy.candidates = [
            {"name": "Test", "confidence": 0.92, "matched_clues": ["a"]},
        ]
        strategy.query_clues.all_clues = ["a", "b", "c", "d", "e"]
        # 1/5 = 0.2 < 0.8
        strategy.iteration = 1  # guard: iteration < 3 -> False
        assert strategy._evaluate_candidates() is False

    def test_very_high_confidence_90pct_ratio(self, strategy):
        strategy.candidates = [
            {
                "name": "Test",
                "confidence": 0.96,
                "matched_clues": list(range(9)),
            },
        ]
        strategy.query_clues.all_clues = list(range(10))
        # 9/10 = 0.9 >= 0.9 and 0.96 >= 0.95
        assert strategy._evaluate_candidates() is True

    def test_iteration_less_than_3_guard(self, strategy):
        strategy.iteration = 2
        strategy.candidates = [
            {"name": "Test", "confidence": 0.5, "matched_clues": []},
        ]
        assert strategy._evaluate_candidates() is False

    def test_iteration_8_with_gap(self, strategy):
        strategy.iteration = 8
        strategy.candidates = [
            {"name": "Leader", "confidence": 0.7, "matched_clues": []},
            {"name": "Second", "confidence": 0.3, "matched_clues": []},
        ]
        strategy.query_clues.all_clues = ["a"]
        # gap = 0.7 - 0.3 = 0.4 > 0.3
        assert strategy._evaluate_candidates() is True

    def test_iteration_8_no_gap(self, strategy):
        strategy.iteration = 8
        strategy.candidates = [
            {"name": "Leader", "confidence": 0.7, "matched_clues": []},
            {"name": "Second", "confidence": 0.6, "matched_clues": []},
        ]
        strategy.query_clues.all_clues = ["a"]
        # gap = 0.1 < 0.3
        assert strategy._evaluate_candidates() is False


# --- _generate_targeted_search ---


class TestGenerateTargetedSearch:
    def test_iteration_1_broad(self, strategy):
        strategy.iteration = 1
        result = strategy._generate_targeted_search()
        assert result is not None
        assert isinstance(result, str)

    def test_iteration_2_temporal(self, strategy):
        strategy.iteration = 2
        result = strategy._generate_targeted_search()
        assert result is not None

    def test_iteration_3_to_8_combinations(self, strategy):
        strategy.iteration = 4
        strategy.candidates = []  # No candidates to verify
        result = strategy._generate_targeted_search()
        assert result is not None

    def test_iteration_gt_8_stats(self, strategy):
        strategy.iteration = 9
        strategy.candidates = []
        result = strategy._generate_targeted_search()
        assert result is not None
        assert "statistics" in result or "data" in result or result is not None

    def test_iteration_gt_8_numerical(self, strategy):
        strategy.iteration = 9
        strategy.candidates = []
        strategy.query_clues.comparison_clues = []
        strategy.query_clues.numerical_clues = ["84.5x ratio"]
        result = strategy._generate_targeted_search()
        assert result is not None

    def test_with_candidates_iteration_gt_2(self, strategy):
        strategy.iteration = 3
        strategy.candidates = [
            {"name": "Arm Creek", "matched_clues": ["name: arm"]},
        ]
        # Set up unverified clues
        strategy.query_clues.incident_clues = ["hiker fell to death"]
        result = strategy._generate_targeted_search()
        assert result is not None

    def test_no_more_clues_returns_none(self, strategy):
        strategy.iteration = 20
        strategy.candidates = []
        strategy.query_clues = QueryClues()
        strategy.search_history = []
        result = strategy._generate_targeted_search()
        assert result is None

    def test_unused_clues_as_fallback(self, strategy):
        strategy.iteration = 20
        strategy.candidates = []
        strategy.query_clues = QueryClues(all_clues=["unique clue"])
        strategy.search_history = []
        # No comparison or numerical clues, so falls through to unused clues
        strategy.query_clues.comparison_clues = []
        strategy.query_clues.numerical_clues = []
        result = strategy._generate_targeted_search()
        assert result == "unique clue"
