"""High-value pure logic tests for EntityCandidate and EntityKnowledgeGraph."""

from local_deep_research.advanced_search_system.strategies.browsecomp_entity_strategy import (
    EntityCandidate,
    EntityKnowledgeGraph,
)


class TestEntityCandidateDefaults:
    """Test EntityCandidate dataclass field defaults and __post_init__."""

    def test_none_aliases_initialized_to_empty_list(self):
        entity = EntityCandidate(name="Acme", entity_type="company")
        assert entity.aliases == []

    def test_none_properties_initialized_to_empty_dict(self):
        entity = EntityCandidate(name="Acme", entity_type="company")
        assert entity.properties == {}

    def test_none_sources_initialized_to_empty_list(self):
        entity = EntityCandidate(name="Acme", entity_type="company")
        assert entity.sources == []

    def test_none_constraint_matches_initialized_to_empty_dict(self):
        entity = EntityCandidate(name="Acme", entity_type="company")
        assert entity.constraint_matches == {}

    def test_default_confidence_is_zero(self):
        entity = EntityCandidate(name="Acme", entity_type="company")
        assert entity.confidence == 0.0

    def test_explicit_values_preserved(self):
        entity = EntityCandidate(
            name="TestCorp",
            entity_type="company",
            aliases=["TC"],
            properties={"founded": "2000"},
            sources=["http://example.com"],
            confidence=0.9,
            constraint_matches={"size": 0.8},
        )
        assert entity.aliases == ["TC"]
        assert entity.properties == {"founded": "2000"}
        assert entity.sources == ["http://example.com"]
        assert entity.confidence == 0.9
        assert entity.constraint_matches == {"size": 0.8}

    def test_required_fields_stored(self):
        entity = EntityCandidate(name="Alice", entity_type="person")
        assert entity.name == "Alice"
        assert entity.entity_type == "person"


class TestEntityKnowledgeGraphAddEntity:
    """Test EntityKnowledgeGraph.add_entity() for new and merged entities."""

    def test_add_new_entity(self):
        graph = EntityKnowledgeGraph()
        entity = EntityCandidate(name="Acme", entity_type="company")
        graph.add_entity(entity)
        assert "Acme" in graph.entities
        assert graph.entities["Acme"] is entity

    def test_add_multiple_distinct_entities(self):
        graph = EntityKnowledgeGraph()
        graph.add_entity(EntityCandidate(name="A", entity_type="company"))
        graph.add_entity(EntityCandidate(name="B", entity_type="person"))
        assert len(graph.entities) == 2

    def test_merge_aliases_deduplicates(self):
        graph = EntityKnowledgeGraph()
        graph.add_entity(
            EntityCandidate(
                name="Acme", entity_type="company", aliases=["AC", "AcmeCo"]
            )
        )
        graph.add_entity(
            EntityCandidate(
                name="Acme", entity_type="company", aliases=["AC", "AcmeInc"]
            )
        )
        aliases = graph.entities["Acme"].aliases
        assert sorted(aliases) == sorted(["AC", "AcmeCo", "AcmeInc"])

    def test_merge_properties_updates(self):
        graph = EntityKnowledgeGraph()
        graph.add_entity(
            EntityCandidate(
                name="Acme",
                entity_type="company",
                properties={"founded": "2000"},
            )
        )
        graph.add_entity(
            EntityCandidate(
                name="Acme",
                entity_type="company",
                properties={"founded": "2001", "hq": "NYC"},
            )
        )
        props = graph.entities["Acme"].properties
        assert props["founded"] == "2001"  # overwritten by second add
        assert props["hq"] == "NYC"

    def test_merge_sources_deduplicates(self):
        graph = EntityKnowledgeGraph()
        graph.add_entity(
            EntityCandidate(
                name="Acme",
                entity_type="company",
                sources=["http://a.com", "http://b.com"],
            )
        )
        graph.add_entity(
            EntityCandidate(
                name="Acme",
                entity_type="company",
                sources=["http://b.com", "http://c.com"],
            )
        )
        sources = graph.entities["Acme"].sources
        assert len(sources) == 3
        assert set(sources) == {"http://a.com", "http://b.com", "http://c.com"}

    def test_merge_constraint_matches_updates(self):
        graph = EntityKnowledgeGraph()
        graph.add_entity(
            EntityCandidate(
                name="Acme",
                entity_type="company",
                constraint_matches={"size": 0.5},
            )
        )
        graph.add_entity(
            EntityCandidate(
                name="Acme",
                entity_type="company",
                constraint_matches={"size": 0.9, "age": 0.7},
            )
        )
        matches = graph.entities["Acme"].constraint_matches
        assert matches["size"] == 0.9
        assert matches["age"] == 0.7

    def test_merge_does_not_replace_entity_object(self):
        graph = EntityKnowledgeGraph()
        first = EntityCandidate(name="Acme", entity_type="company")
        graph.add_entity(first)
        graph.add_entity(EntityCandidate(name="Acme", entity_type="company"))
        assert graph.entities["Acme"] is first

    def test_merge_with_empty_new_entity(self):
        graph = EntityKnowledgeGraph()
        graph.add_entity(
            EntityCandidate(
                name="Acme",
                entity_type="company",
                aliases=["AC"],
                properties={"k": "v"},
                sources=["http://x.com"],
            )
        )
        # Merge entity with all defaults (empty lists/dicts)
        graph.add_entity(EntityCandidate(name="Acme", entity_type="company"))
        assert graph.entities["Acme"].aliases == ["AC"]
        assert graph.entities["Acme"].properties == {"k": "v"}
        assert graph.entities["Acme"].sources == ["http://x.com"]


class TestEntityKnowledgeGraphConstraintEvidence:
    """Test add_constraint_evidence() and evidence storage."""

    def test_add_single_evidence(self):
        graph = EntityKnowledgeGraph()
        graph.add_constraint_evidence(
            "large_size", "Acme", {"score": 0.8, "text": "big"}
        )
        assert graph.constraint_evidence["large_size"]["Acme"]["score"] == 0.8

    def test_add_evidence_multiple_entities_same_constraint(self):
        graph = EntityKnowledgeGraph()
        graph.add_constraint_evidence("large_size", "Acme", {"score": 0.8})
        graph.add_constraint_evidence("large_size", "Beta", {"score": 0.6})
        assert len(graph.constraint_evidence["large_size"]) == 2

    def test_add_evidence_multiple_constraints_same_entity(self):
        graph = EntityKnowledgeGraph()
        graph.add_constraint_evidence("size", "Acme", {"score": 0.8})
        graph.add_constraint_evidence("age", "Acme", {"score": 0.5})
        assert graph.constraint_evidence["size"]["Acme"]["score"] == 0.8
        assert graph.constraint_evidence["age"]["Acme"]["score"] == 0.5

    def test_overwrite_evidence_for_same_pair(self):
        graph = EntityKnowledgeGraph()
        graph.add_constraint_evidence("size", "Acme", {"score": 0.3})
        graph.add_constraint_evidence("size", "Acme", {"score": 0.9})
        assert graph.constraint_evidence["size"]["Acme"]["score"] == 0.9


class TestEntityKnowledgeGraphGetEntitiesByConstraint:
    """Test get_entities_by_constraint() filtering and sorting."""

    def _make_graph_with_entities(self, entity_specs):
        """Helper: create a graph populated with entities.

        entity_specs is a list of (name, constraint_matches_dict) tuples.
        """
        graph = EntityKnowledgeGraph()
        for name, matches in entity_specs:
            graph.add_entity(
                EntityCandidate(
                    name=name,
                    entity_type="company",
                    constraint_matches=matches,
                )
            )
        return graph

    def test_returns_matching_entities_above_threshold(self):
        graph = self._make_graph_with_entities(
            [
                ("A", {"size": 0.8}),
                ("B", {"size": 0.3}),
                ("C", {"size": 0.6}),
            ]
        )
        result = graph.get_entities_by_constraint("size", min_confidence=0.5)
        names = [ent.name for ent in result]
        assert "A" in names
        assert "C" in names
        assert "B" not in names

    def test_sorted_descending_by_constraint_score(self):
        graph = self._make_graph_with_entities(
            [
                ("A", {"size": 0.6}),
                ("B", {"size": 0.9}),
                ("C", {"size": 0.7}),
            ]
        )
        result = graph.get_entities_by_constraint("size", min_confidence=0.5)
        names = [ent.name for ent in result]
        assert names == ["B", "C", "A"]

    def test_no_matches_returns_empty(self):
        graph = self._make_graph_with_entities(
            [
                ("A", {"size": 0.2}),
                ("B", {"size": 0.1}),
            ]
        )
        result = graph.get_entities_by_constraint("size", min_confidence=0.5)
        assert result == []

    def test_constraint_not_present_returns_empty(self):
        graph = self._make_graph_with_entities(
            [
                ("A", {"age": 0.9}),
            ]
        )
        result = graph.get_entities_by_constraint("size", min_confidence=0.5)
        assert result == []

    def test_empty_graph_returns_empty(self):
        graph = EntityKnowledgeGraph()
        result = graph.get_entities_by_constraint("size", min_confidence=0.5)
        assert result == []

    def test_boundary_confidence_equal_to_threshold_included(self):
        graph = self._make_graph_with_entities(
            [
                ("A", {"size": 0.5}),
            ]
        )
        result = graph.get_entities_by_constraint("size", min_confidence=0.5)
        assert len(result) == 1
        assert result[0].name == "A"

    def test_boundary_confidence_just_below_threshold_excluded(self):
        graph = self._make_graph_with_entities(
            [
                ("A", {"size": 0.4999}),
            ]
        )
        result = graph.get_entities_by_constraint("size", min_confidence=0.5)
        assert result == []

    def test_default_min_confidence_is_half(self):
        graph = self._make_graph_with_entities(
            [
                ("A", {"size": 0.5}),
                ("B", {"size": 0.49}),
            ]
        )
        result = graph.get_entities_by_constraint("size")
        assert len(result) == 1
        assert result[0].name == "A"
