"""High-value edge case tests for search_optimization/cross_constraint_manager.py.

Covers dataclass mutable defaults, relationship graph updates,
coherence formula boundaries, and deduplication edge cases.
"""

from unittest.mock import MagicMock

from local_deep_research.advanced_search_system.search_optimization.cross_constraint_manager import (
    CrossConstraintManager,
    ConstraintRelationship,
    ConstraintCluster,
)
from local_deep_research.advanced_search_system.constraints.base_constraint import (
    Constraint,
    ConstraintType,
)


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


def _make_manager():
    mock_model = MagicMock()
    mock_model.invoke.return_value = MagicMock(
        content="Relationship: complementary\nStrength: 0.8\nEvidence: test"
    )
    return CrossConstraintManager(mock_model)


class TestConstraintRelationshipMutableDefaults:
    """Test ConstraintRelationship dataclass field independence."""

    def test_evidence_lists_independent_per_instance(self):
        """Each instance has its own evidence list."""
        r1 = ConstraintRelationship(
            constraint1_id="a",
            constraint2_id="b",
            relationship_type="complementary",
            strength=0.5,
        )
        r2 = ConstraintRelationship(
            constraint1_id="c",
            constraint2_id="d",
            relationship_type="conflicting",
            strength=0.3,
        )
        r1.evidence.append("test evidence")
        assert len(r2.evidence) == 0

    def test_strength_boundary_values(self):
        """Boundary values 0.0 and 1.0 are stored correctly."""
        r_low = ConstraintRelationship(
            constraint1_id="a",
            constraint2_id="b",
            relationship_type="weak",
            strength=0.0,
        )
        r_high = ConstraintRelationship(
            constraint1_id="a",
            constraint2_id="b",
            relationship_type="strong",
            strength=1.0,
        )
        assert r_low.strength == 0.0
        assert r_high.strength == 1.0


class TestConstraintClusterMutableDefaults:
    """Test ConstraintCluster field independence."""

    def test_search_queries_independent_per_instance(self):
        """Each cluster has its own search_queries list."""
        c = _make_constraint()
        c1 = ConstraintCluster(
            constraints=[c],
            cluster_type="type_based",
            coherence_score=0.5,
        )
        c2 = ConstraintCluster(
            constraints=[c],
            cluster_type="type_based",
            coherence_score=0.5,
        )
        c1.search_queries.append("query1")
        assert len(c2.search_queries) == 0


class TestCalculateClusterCoherence:
    """Test _calculate_cluster_coherence formula."""

    def test_no_relationships_returns_zero(self):
        """With no relationships, coherence is 0.0."""
        mgr = _make_manager()
        constraints = [_make_constraint(value="c1")]
        result = mgr._calculate_cluster_coherence(constraints)
        assert result == 0.0


class TestDeduplicateClusters:
    """Test _deduplicate_clusters edge cases."""

    def test_empty_input_returns_empty(self):
        """Empty list returns empty list."""
        mgr = _make_manager()
        assert mgr._deduplicate_clusters([]) == []

    def test_first_occurrence_kept(self):
        """First cluster with a given constraint set is kept."""
        mgr = _make_manager()
        c = _make_constraint()
        cluster_a = ConstraintCluster(
            constraints=[c],
            cluster_type="type_a",
            coherence_score=0.5,
        )
        cluster_b = ConstraintCluster(
            constraints=[c],
            cluster_type="type_b",
            coherence_score=0.8,
        )
        result = mgr._deduplicate_clusters([cluster_a, cluster_b])
        assert len(result) == 1
        assert result[0].cluster_type == "type_a"


class TestManagerInit:
    """Test CrossConstraintManager initialization."""

    def test_init_creates_empty_relationships(self):
        """Manager starts with empty relationships."""
        mgr = _make_manager()
        assert len(mgr.relationships) == 0

    def test_init_creates_empty_clusters(self):
        """Manager starts with empty clusters."""
        mgr = _make_manager()
        assert len(mgr.clusters) == 0


class TestFormatHelpers:
    """Test _format_constraints_for_clustering and _format_constraints_for_query."""

    def test_clustering_format_each_on_own_line(self):
        """Each constraint appears on its own line."""
        mgr = _make_manager()
        constraints = [
            _make_constraint(value="v1", desc="d1"),
            _make_constraint(value="v2", desc="d2"),
            _make_constraint(value="v3", desc="d3"),
        ]
        result = mgr._format_constraints_for_clustering(constraints)
        lines = [line for line in result.split("\n") if line.strip()]
        assert len(lines) == 3

    def test_query_format_bullet_count(self):
        """Each constraint gets a '- ' bullet prefix."""
        mgr = _make_manager()
        constraints = [
            _make_constraint(value="v1", desc="d1"),
            _make_constraint(value="v2", desc="d2"),
        ]
        result = mgr._format_constraints_for_query(constraints)
        assert result.count("- ") == 2
