import unittest
from types import SimpleNamespace
from unittest.mock import Mock

from analogai.foundation.modules.fact.subscribers.fact_observer_registry import (
    FactObserverRegistry,
)
from analogai.foundation.modules.fact.subscribers.fact_observer_builder import (
    FactObserverBuilder,
)
from analogai.foundation.modules.fact.subscribers.statement_fact_subscriber import (
    StatementFactSubscriber,
)
from analogai.foundation.modules.fact.subscribers.notion_fact_subscriber import (
    NotionFactSubscriber,
)
from analogai.foundation.modules.fact.subscribers.hypothetical_statement_fact_subscriber import (
    HypotheticalStatementFactSubscriber,
)
from analogai.foundation.modules.fact.source.fact_source import OtherSource


class _ObservableStub:
    def __init__(self):
        self._observers = []

    def add_observer(self, observer):
        if observer not in self._observers:
            self._observers.append(observer)


class TestFactSubscribers(unittest.TestCase):
    def setUp(self):
        self.registry = FactObserverRegistry()
        self.fact_service = Mock()
        self.source = OtherSource("unit")

    def test_statement_subscriber_records_fact(self):
        subscriber = StatementFactSubscriber(self.fact_service, self.registry)
        self.registry.append_current_sentence(text="original", role="user", source=self.source)
        statement = SimpleNamespace(user_id="u1", agent_id="a1", id="s1")

        subscriber.on_statement_created(statement)

        self.fact_service.record_direct_statement_fact.assert_called_once_with(
            user_id="u1",
            agent_id="a1",
            statement_id="s1",
            text="original",
            role="user",
            user_name=None,
            timestamp=None,
            source=self.source,
        )

    def test_notion_subscriber_records_fact(self):
        subscriber = NotionFactSubscriber(self.fact_service, self.registry)
        self.registry.append_current_sentence(text="original", role="user", source=self.source)
        notion = SimpleNamespace(user_id="u1", agent_id="a1", id="n1")

        subscriber.on_notion_created(notion)

        self.fact_service.record_notion_fact.assert_called_once_with(
            user_id="u1",
            agent_id="a1",
            notion_id="n1",
            text="original",
            role="user",
            user_name=None,
            timestamp=None,
            source=self.source,
        )

    def test_hypothetical_subscriber_records_fact(self):
        subscriber = HypotheticalStatementFactSubscriber(self.fact_service, self.registry)
        self.registry.append_current_sentence(text="original", role="user", source=self.source)
        hypothetical = SimpleNamespace(user_id="u1", agent_id="a1", id="h1")

        subscriber.on_hypothetical_statement_created(hypothetical)

        self.fact_service.record_hypothetical_statement_fact.assert_called_once_with(
            user_id="u1",
            agent_id="a1",
            statement_id="h1",
            text="original",
            role="user",
            user_name=None,
            timestamp=None,
            source=self.source,
        )

    def test_builder_registers_observers_once(self):
        registry = FactObserverRegistry()
        fact_service = Mock()
        statement_service = _ObservableStub()
        notion_service = _ObservableStub()
        hypothetical_service = _ObservableStub()

        builder = FactObserverBuilder(fact_service=fact_service, registry=registry)
        builder.observe_statement_service(statement_service)
        builder.observe_statement_service(statement_service)
        builder.observe_notion_service(notion_service)
        builder.observe_hypothetical_statement_service(hypothetical_service)

        self.assertEqual(
            len([o for o in statement_service._observers if isinstance(o, StatementFactSubscriber)]),
            1,
        )
        self.assertEqual(
            len([o for o in notion_service._observers if isinstance(o, NotionFactSubscriber)]),
            1,
        )
        self.assertEqual(
            len(
                [
                    o
                    for o in hypothetical_service._observers
                    if isinstance(o, HypotheticalStatementFactSubscriber)
                ]
            ),
            1,
        )


if __name__ == "__main__":
    unittest.main()
