"""High-value edge case tests for query_generation/adaptive_query_generator.py.

Tests pattern initialization, constraint-to-template mapping, expansion logic,
pattern extraction, and fallback query generation.
"""

from unittest.mock import MagicMock

from local_deep_research.advanced_search_system.query_generation.adaptive_query_generator import (
    AdaptiveQueryGenerator,
    QueryPattern,
)
from local_deep_research.advanced_search_system.constraints.base_constraint import (
    Constraint,
    ConstraintType,
)


def _make_constraint(
    ctype, value="test", desc="test desc", weight=1.0, cid=None
):
    return Constraint(
        id=cid or f"{ctype.value}_{value}",
        type=ctype,
        value=value,
        description=desc,
        weight=weight,
    )


def _make_generator():
    mock_model = MagicMock()
    mock_model.invoke.return_value = MagicMock(content="mock response")
    return AdaptiveQueryGenerator(mock_model)


class TestQueryPatternDataclass:
    """Test QueryPattern dataclass behavior."""

    def test_discovered_entities_independent_per_instance(self):
        """Each instance has its own discovered_entities set."""
        p1 = QueryPattern(
            template="t",
            constraint_types=[],
            success_rate=0.5,
            example_queries=[],
            discovered_entities=set(),
        )
        p2 = QueryPattern(
            template="t",
            constraint_types=[],
            success_rate=0.5,
            example_queries=[],
            discovered_entities=set(),
        )
        p1.discovered_entities.add("entity1")
        assert "entity1" not in p2.discovered_entities


class TestInitializeDefaultPatterns:
    """Test default pattern initialization."""

    def test_three_default_patterns(self):
        """Generator starts with exactly 3 default patterns."""
        gen = _make_generator()
        assert len(gen.successful_patterns) == 3

    def test_default_pattern_success_rates(self):
        """Default patterns have documented success rates."""
        gen = _make_generator()
        rates = [p.success_rate for p in gen.successful_patterns]
        assert 0.7 in rates
        assert 0.6 in rates
        assert 0.65 in rates

    def test_default_pattern_constraint_types(self):
        """Default patterns have expected constraint types."""
        gen = _make_generator()
        first_types = gen.successful_patterns[0].constraint_types
        assert ConstraintType.NAME_PATTERN in first_types


class TestGenerateFromPatterns:
    """Test _generate_from_patterns."""

    def test_no_matching_pattern_returns_none(self):
        """Returns None when no pattern matches constraints."""
        gen = _make_generator()
        constraints = [_make_constraint(ConstraintType.EXISTENCE, "test")]
        assert gen._generate_from_patterns(constraints) is None

    def test_name_pattern_fills_entity_alias(self):
        """NAME_PATTERN constraint fills both 'name_pattern' and 'entity'."""
        gen = _make_generator()
        constraints = [
            _make_constraint(ConstraintType.NAME_PATTERN, "mountain"),
            _make_constraint(ConstraintType.PROPERTY, "formed"),
            _make_constraint(ConstraintType.LOCATION, "Colorado"),
        ]
        result = gen._generate_from_patterns(constraints)
        assert result is not None
        assert "mountain" in result

    def test_comparison_fills_both_vars(self):
        """COMPARISON constraint fills both 'comparison' and 'value'."""
        gen = _make_generator()
        constraints = [
            _make_constraint(ConstraintType.NAME_PATTERN, "body part"),
            _make_constraint(ConstraintType.COMPARISON, "84.5 times"),
        ]
        result = gen._generate_from_patterns(constraints)
        assert result is not None
        assert "84.5 times" in result


class TestGenerateWithExpansion:
    """Test _generate_with_expansion."""

    def test_single_constraint_no_expansion_quoted(self):
        """Single constraint with no expansions returns quoted value."""
        gen = _make_generator()
        gen.semantic_expansions["test_val"] = []
        constraints = [
            _make_constraint(ConstraintType.NAME_PATTERN, "test_val")
        ]
        result = gen._generate_with_expansion(constraints)
        assert result == '"test_val"'

    def test_expansion_limited_to_two(self):
        """Only first 2 expansions are used in the OR clause."""
        gen = _make_generator()
        gen.semantic_expansions["val"] = ['"alt1"', '"alt2"', '"alt3"']
        constraints = [_make_constraint(ConstraintType.NAME_PATTERN, "val")]
        result = gen._generate_with_expansion(constraints)
        assert '"alt1"' in result
        assert '"alt2"' in result
        assert '"alt3"' not in result


class TestUpdatePatterns:
    """Test update_patterns."""

    def test_failed_query_added_to_failed_set(self):
        """Failed query is added to failed_queries."""
        gen = _make_generator()
        constraints = [_make_constraint(ConstraintType.NAME_PATTERN, "test")]
        gen.update_patterns(
            "failed query", constraints, success=False, entities_found=[]
        )
        assert "failed query" in gen.failed_queries

    def test_successful_update_adds_to_combinations(self):
        """Successful update records constraint combination."""
        gen = _make_generator()
        c1 = _make_constraint(ConstraintType.LOCATION, "place")
        gen.update_patterns('"place" results', [c1], True, ["entity1"])
        # Should have at least one entry in constraint_combinations
        assert len(gen.constraint_combinations) >= 1


class TestExtractPattern:
    """Test _extract_pattern."""

    def test_new_pattern_has_success_rate_one(self):
        """Extracted patterns start with success_rate=1.0."""
        gen = _make_generator()
        constraints = [_make_constraint(ConstraintType.NAME_PATTERN, "test")]
        pattern = gen._extract_pattern('"test" results', constraints)
        assert pattern is not None
        assert pattern.success_rate == 1.0

    def test_returns_none_when_no_placeholders(self):
        """Returns None when constraint value not found in query."""
        gen = _make_generator()
        constraints = [
            _make_constraint(ConstraintType.NAME_PATTERN, "not_in_query")
        ]
        pattern = gen._extract_pattern("completely different text", constraints)
        assert pattern is None


class TestFormatConstraints:
    """Test _format_constraints."""

    def test_empty_list_returns_empty_string(self):
        gen = _make_generator()
        assert gen._format_constraints([]) == ""

    def test_formats_each_constraint(self):
        gen = _make_generator()
        constraints = [
            _make_constraint(ConstraintType.NAME_PATTERN, "val1", "desc1"),
            _make_constraint(ConstraintType.LOCATION, "val2", "desc2"),
        ]
        result = gen._format_constraints(constraints)
        lines = result.strip().split("\n")
        assert len(lines) == 2
        assert "val1" in lines[0]
        assert "val2" in lines[1]


class TestGenerateSingleConstraintQuery:
    """Test _generate_single_constraint_query for each ConstraintType."""

    def _gen(self):
        return _make_generator()

    def test_name_pattern_template(self):
        c = _make_constraint(ConstraintType.NAME_PATTERN, "mountain")
        result = self._gen()._generate_single_constraint_query(c)
        assert '"mountain"' in result

    def test_location_template(self):
        c = _make_constraint(ConstraintType.LOCATION, "Colorado")
        result = self._gen()._generate_single_constraint_query(c)
        assert '"Colorado"' in result

    def test_property_template(self):
        c = _make_constraint(ConstraintType.PROPERTY, "weight")
        result = self._gen()._generate_single_constraint_query(c)
        assert '"weight"' in result

    def test_statistic_template(self):
        c = _make_constraint(ConstraintType.STATISTIC, "84.5x")
        result = self._gen()._generate_single_constraint_query(c)
        assert '"84.5x"' in result

    def test_comparison_template(self):
        c = _make_constraint(ConstraintType.COMPARISON, "greater than")
        result = self._gen()._generate_single_constraint_query(c)
        assert '"greater than"' in result

    def test_existence_template(self):
        c = _make_constraint(ConstraintType.EXISTENCE, "viewpoint")
        result = self._gen()._generate_single_constraint_query(c)
        assert '"viewpoint"' in result

    def test_temporal_template(self):
        c = _make_constraint(ConstraintType.TEMPORAL, "2000-2021")
        result = self._gen()._generate_single_constraint_query(c)
        assert "2000-2021" in result
