"""
Behavioral tests for findings/topic module.

Tests Topic and TopicGraph dataclasses with source management and graph logic.
"""


class TestTopicInit:
    """Tests for Topic dataclass initialization."""

    def test_basic_construction(self):
        from local_deep_research.advanced_search_system.findings.topic import (
            Topic,
        )

        t = Topic(
            id="t1",
            title="Climate Change",
            lead_source={"url": "http://example.com"},
        )
        assert t.id == "t1"
        assert t.title == "Climate Change"
        assert t.lead_source == {"url": "http://example.com"}

    def test_supporting_sources_default_empty(self):
        from local_deep_research.advanced_search_system.findings.topic import (
            Topic,
        )

        t = Topic(id="t1", title="t", lead_source={"url": "u"})
        assert t.supporting_sources == []

    def test_rejected_sources_default_empty(self):
        from local_deep_research.advanced_search_system.findings.topic import (
            Topic,
        )

        t = Topic(id="t1", title="t", lead_source={"url": "u"})
        assert t.rejected_sources == []

    def test_related_topic_ids_default_empty(self):
        from local_deep_research.advanced_search_system.findings.topic import (
            Topic,
        )

        t = Topic(id="t1", title="t", lead_source={"url": "u"})
        assert t.related_topic_ids == []

    def test_parent_topic_id_default_none(self):
        from local_deep_research.advanced_search_system.findings.topic import (
            Topic,
        )

        t = Topic(id="t1", title="t", lead_source={"url": "u"})
        assert t.parent_topic_id is None

    def test_child_topic_ids_default_empty(self):
        from local_deep_research.advanced_search_system.findings.topic import (
            Topic,
        )

        t = Topic(id="t1", title="t", lead_source={"url": "u"})
        assert t.child_topic_ids == []


class TestTopicAddSupportingSource:
    """Tests for Topic.add_supporting_source() method."""

    def test_add_one_source(self):
        from local_deep_research.advanced_search_system.findings.topic import (
            Topic,
        )

        t = Topic(id="t1", title="t", lead_source={"url": "u"})
        t.add_supporting_source({"url": "http://support.com"})
        assert len(t.supporting_sources) == 1

    def test_no_duplicate_sources(self):
        from local_deep_research.advanced_search_system.findings.topic import (
            Topic,
        )

        t = Topic(id="t1", title="t", lead_source={"url": "u"})
        source = {"url": "http://support.com"}
        t.add_supporting_source(source)
        t.add_supporting_source(source)
        assert len(t.supporting_sources) == 1


class TestTopicRejectSource:
    """Tests for Topic.reject_source() method."""

    def test_reject_supporting_source(self):
        from local_deep_research.advanced_search_system.findings.topic import (
            Topic,
        )

        t = Topic(id="t1", title="t", lead_source={"url": "u"})
        source = {"url": "http://bad.com"}
        t.add_supporting_source(source)
        t.reject_source(source)
        assert source not in t.supporting_sources
        assert source in t.rejected_sources

    def test_reject_non_supporting_source(self):
        from local_deep_research.advanced_search_system.findings.topic import (
            Topic,
        )

        t = Topic(id="t1", title="t", lead_source={"url": "u"})
        source = {"url": "http://other.com"}
        t.reject_source(source)
        assert source in t.rejected_sources

    def test_no_duplicate_rejected(self):
        from local_deep_research.advanced_search_system.findings.topic import (
            Topic,
        )

        t = Topic(id="t1", title="t", lead_source={"url": "u"})
        source = {"url": "http://bad.com"}
        t.reject_source(source)
        t.reject_source(source)
        assert t.rejected_sources.count(source) == 1


class TestTopicUpdateLeadSource:
    """Tests for Topic.update_lead_source() method."""

    def test_old_lead_becomes_supporting(self):
        from local_deep_research.advanced_search_system.findings.topic import (
            Topic,
        )

        old_lead = {"url": "http://old.com"}
        new_lead = {"url": "http://new.com"}
        t = Topic(id="t1", title="t", lead_source=old_lead)
        t.update_lead_source(new_lead)
        assert t.lead_source == new_lead
        assert old_lead in t.supporting_sources

    def test_new_lead_removed_from_supporting(self):
        from local_deep_research.advanced_search_system.findings.topic import (
            Topic,
        )

        lead = {"url": "http://lead.com"}
        candidate = {"url": "http://candidate.com"}
        t = Topic(id="t1", title="t", lead_source=lead)
        t.add_supporting_source(candidate)
        t.update_lead_source(candidate)
        assert t.lead_source == candidate
        # candidate should not be in supporting after being promoted
        assert candidate not in t.supporting_sources


class TestTopicGetAllSources:
    """Tests for Topic.get_all_sources() method."""

    def test_lead_only(self):
        from local_deep_research.advanced_search_system.findings.topic import (
            Topic,
        )

        lead = {"url": "http://lead.com"}
        t = Topic(id="t1", title="t", lead_source=lead)
        all_sources = t.get_all_sources()
        assert len(all_sources) == 1
        assert all_sources[0] == lead

    def test_lead_plus_supporting(self):
        from local_deep_research.advanced_search_system.findings.topic import (
            Topic,
        )

        lead = {"url": "http://lead.com"}
        t = Topic(id="t1", title="t", lead_source=lead)
        t.add_supporting_source({"url": "http://s1.com"})
        t.add_supporting_source({"url": "http://s2.com"})
        assert len(t.get_all_sources()) == 3


class TestTopicToDict:
    """Tests for Topic.to_dict() method."""

    def test_contains_all_fields(self):
        from local_deep_research.advanced_search_system.findings.topic import (
            Topic,
        )

        t = Topic(id="t1", title="Climate", lead_source={"url": "u"})
        d = t.to_dict()
        assert d["id"] == "t1"
        assert d["title"] == "Climate"
        assert d["lead_source"] == {"url": "u"}
        assert "supporting_sources" in d
        assert "rejected_sources" in d
        assert "created_at" in d
        assert "related_topic_ids" in d
        assert "parent_topic_id" in d
        assert "child_topic_ids" in d

    def test_created_at_is_isoformat(self):
        from local_deep_research.advanced_search_system.findings.topic import (
            Topic,
        )

        t = Topic(id="t1", title="t", lead_source={"url": "u"})
        d = t.to_dict()
        assert isinstance(d["created_at"], str)
        assert "T" in d["created_at"]


class TestTopicGraphInit:
    """Tests for TopicGraph initialization."""

    def test_empty_graph(self):
        from local_deep_research.advanced_search_system.findings.topic import (
            TopicGraph,
        )

        g = TopicGraph()
        assert g.topics == {}


class TestTopicGraphAddAndGet:
    """Tests for TopicGraph.add_topic() and get_topic()."""

    def test_add_and_retrieve(self):
        from local_deep_research.advanced_search_system.findings.topic import (
            Topic,
            TopicGraph,
        )

        g = TopicGraph()
        t = Topic(id="t1", title="Climate", lead_source={"url": "u"})
        g.add_topic(t)
        assert g.get_topic("t1") is t

    def test_get_nonexistent_returns_none(self):
        from local_deep_research.advanced_search_system.findings.topic import (
            TopicGraph,
        )

        g = TopicGraph()
        assert g.get_topic("nonexistent") is None


class TestTopicGraphLinkTopics:
    """Tests for TopicGraph.link_topics() method."""

    def test_bidirectional_link(self):
        from local_deep_research.advanced_search_system.findings.topic import (
            Topic,
            TopicGraph,
        )

        g = TopicGraph()
        t1 = Topic(id="t1", title="A", lead_source={"url": "u1"})
        t2 = Topic(id="t2", title="B", lead_source={"url": "u2"})
        g.add_topic(t1)
        g.add_topic(t2)
        g.link_topics("t1", "t2")
        assert "t2" in t1.related_topic_ids
        assert "t1" in t2.related_topic_ids

    def test_no_duplicate_links(self):
        from local_deep_research.advanced_search_system.findings.topic import (
            Topic,
            TopicGraph,
        )

        g = TopicGraph()
        t1 = Topic(id="t1", title="A", lead_source={"url": "u1"})
        t2 = Topic(id="t2", title="B", lead_source={"url": "u2"})
        g.add_topic(t1)
        g.add_topic(t2)
        g.link_topics("t1", "t2")
        g.link_topics("t1", "t2")
        assert t1.related_topic_ids.count("t2") == 1

    def test_link_nonexistent_topic_no_error(self):
        from local_deep_research.advanced_search_system.findings.topic import (
            Topic,
            TopicGraph,
        )

        g = TopicGraph()
        t1 = Topic(id="t1", title="A", lead_source={"url": "u1"})
        g.add_topic(t1)
        g.link_topics("t1", "nonexistent")
        assert t1.related_topic_ids == []


class TestTopicGraphParentChild:
    """Tests for TopicGraph.set_parent_child() method."""

    def test_set_parent_child(self):
        from local_deep_research.advanced_search_system.findings.topic import (
            Topic,
            TopicGraph,
        )

        g = TopicGraph()
        parent = Topic(id="p", title="Parent", lead_source={"url": "u"})
        child = Topic(id="c", title="Child", lead_source={"url": "u"})
        g.add_topic(parent)
        g.add_topic(child)
        g.set_parent_child("p", "c")
        assert "c" in parent.child_topic_ids
        assert child.parent_topic_id == "p"


class TestTopicGraphGetRootTopics:
    """Tests for TopicGraph.get_root_topics() method."""

    def test_all_roots_when_no_parents(self):
        from local_deep_research.advanced_search_system.findings.topic import (
            Topic,
            TopicGraph,
        )

        g = TopicGraph()
        g.add_topic(Topic(id="t1", title="A", lead_source={"url": "u"}))
        g.add_topic(Topic(id="t2", title="B", lead_source={"url": "u"}))
        roots = g.get_root_topics()
        assert len(roots) == 2

    def test_excludes_children(self):
        from local_deep_research.advanced_search_system.findings.topic import (
            Topic,
            TopicGraph,
        )

        g = TopicGraph()
        parent = Topic(id="p", title="P", lead_source={"url": "u"})
        child = Topic(id="c", title="C", lead_source={"url": "u"})
        g.add_topic(parent)
        g.add_topic(child)
        g.set_parent_child("p", "c")
        roots = g.get_root_topics()
        assert len(roots) == 1
        assert roots[0].id == "p"


class TestTopicGraphGetRelatedTopics:
    """Tests for TopicGraph.get_related_topics() method."""

    def test_returns_related(self):
        from local_deep_research.advanced_search_system.findings.topic import (
            Topic,
            TopicGraph,
        )

        g = TopicGraph()
        t1 = Topic(id="t1", title="A", lead_source={"url": "u"})
        t2 = Topic(id="t2", title="B", lead_source={"url": "u"})
        g.add_topic(t1)
        g.add_topic(t2)
        g.link_topics("t1", "t2")
        related = g.get_related_topics("t1")
        assert len(related) == 1
        assert related[0].id == "t2"

    def test_nonexistent_topic_returns_empty(self):
        from local_deep_research.advanced_search_system.findings.topic import (
            TopicGraph,
        )

        g = TopicGraph()
        assert g.get_related_topics("nonexistent") == []


class TestTopicGraphMergeTopics:
    """Tests for TopicGraph.merge_topics() method."""

    def test_merge_combines_sources(self):
        from local_deep_research.advanced_search_system.findings.topic import (
            Topic,
            TopicGraph,
        )

        g = TopicGraph()
        t1 = Topic(id="t1", title="A", lead_source={"url": "u1"})
        t2 = Topic(id="t2", title="B", lead_source={"url": "u2"})
        t2.add_supporting_source({"url": "u3"})
        g.add_topic(t1)
        g.add_topic(t2)
        merged = g.merge_topics("t1", "t2")
        assert merged is not None
        # t2's supporting source should be in t1's supporting
        assert {"url": "u3"} in merged.supporting_sources

    def test_merge_removes_second_topic(self):
        from local_deep_research.advanced_search_system.findings.topic import (
            Topic,
            TopicGraph,
        )

        g = TopicGraph()
        t1 = Topic(id="t1", title="A", lead_source={"url": "u1"})
        t2 = Topic(id="t2", title="B", lead_source={"url": "u2"})
        g.add_topic(t1)
        g.add_topic(t2)
        g.merge_topics("t1", "t2")
        assert g.get_topic("t2") is None

    def test_merge_updates_title(self):
        from local_deep_research.advanced_search_system.findings.topic import (
            Topic,
            TopicGraph,
        )

        g = TopicGraph()
        t1 = Topic(id="t1", title="A", lead_source={"url": "u1"})
        t2 = Topic(id="t2", title="B", lead_source={"url": "u2"})
        g.add_topic(t1)
        g.add_topic(t2)
        merged = g.merge_topics("t1", "t2", new_title="Combined")
        assert merged.title == "Combined"

    def test_merge_nonexistent_returns_none(self):
        from local_deep_research.advanced_search_system.findings.topic import (
            Topic,
            TopicGraph,
        )

        g = TopicGraph()
        t1 = Topic(id="t1", title="A", lead_source={"url": "u1"})
        g.add_topic(t1)
        assert g.merge_topics("t1", "nonexistent") is None

    def test_merge_updates_references_in_other_topics(self):
        from local_deep_research.advanced_search_system.findings.topic import (
            Topic,
            TopicGraph,
        )

        g = TopicGraph()
        t1 = Topic(id="t1", title="A", lead_source={"url": "u1"})
        t2 = Topic(id="t2", title="B", lead_source={"url": "u2"})
        t3 = Topic(id="t3", title="C", lead_source={"url": "u3"})
        g.add_topic(t1)
        g.add_topic(t2)
        g.add_topic(t3)
        g.link_topics("t2", "t3")
        g.merge_topics("t1", "t2")
        # t3 should now reference t1 instead of t2
        assert "t1" in t3.related_topic_ids
        assert "t2" not in t3.related_topic_ids


class TestTopicGraphToDict:
    """Tests for TopicGraph.to_dict() method."""

    def test_empty_graph_to_dict(self):
        from local_deep_research.advanced_search_system.findings.topic import (
            TopicGraph,
        )

        g = TopicGraph()
        assert g.to_dict() == {}

    def test_graph_with_topics(self):
        from local_deep_research.advanced_search_system.findings.topic import (
            Topic,
            TopicGraph,
        )

        g = TopicGraph()
        g.add_topic(Topic(id="t1", title="A", lead_source={"url": "u"}))
        d = g.to_dict()
        assert "t1" in d
        assert d["t1"]["title"] == "A"
