from unittest import TestCase

from analogai.foundation.settings import config
from analogai.foundation.modules.fact.factories.fact_factory import FactServiceFactory
from analogai.foundation.common.dtos.notion_expression import NotionExpression
from analogai.foundation.core.value_objects.relation import Relation
from analogai.foundation.tests.functional.services.setup import ServicesTestConfig
from analogai.foundation.modules.fact.source.fact_source import UrlSource
from analogai.foundation.modules.fact.repositories.infrastructure.in_memory_fact_repository import (
    InMemoryFactRepository,
)
from analogai.foundation.modules.fact.source.services.fact_source_service import FactSourceService
from analogai.foundation.modules.fact.source.repositories.infrastructure.in_memory_fact_source_repository import (
    InMemoryFactSourceRepository,
)


class TestFactServiceFunctional(TestCase):
    def setUp(self):
        self._config_override = config.override(
            **{"vector_search.enabled": False, "ENVIRONMENT": "development"}
        )
        self._config_override.__enter__()
        self.config = ServicesTestConfig()
        self.user_id = self.config.user.id
        self.agent_id = self.config.agent.id
        self.subject_notion = self.config.notion_service.create(
            user_id=self.user_id,
            agent_id=self.agent_id,
            expression=NotionExpression(caption="human"),
        )
        self.complement_notion = self.config.notion_service.create(
            user_id=self.user_id,
            agent_id=self.agent_id,
            expression=NotionExpression(caption="mortal"),
        )
        self.source_repo = InMemoryFactSourceRepository()
        self.fact_repo = InMemoryFactRepository()
        self.fact_factory = FactServiceFactory(
            repository=self.fact_repo,
            source_repository=self.source_repo,
            statement_service=self.config.statement_service,
            notion_service=self.config.notion_service,
        )
        self.fact_service = self.fact_factory.create()
        self.fact_registry = self.fact_factory.get_observer_registry()

    def tearDown(self):
        if self.config:
            self.config.tear_down()
        if self._config_override:
            self._config_override.__exit__(None, None, None)

    def test_record_fact_registers_source_and_facts(self):
        source = UrlSource("https://example.com", agent_id=self.agent_id)
        statement = self.config.statement_service.create(
            user_id=self.user_id,
            agent_id=self.agent_id,
            subject_notion=self.subject_notion,
            complement_notion=self.complement_notion,
            relation=Relation.from_string("is"),
            probability=0.9,
            validity=0.9,
        )
        fact_id = self.fact_service.record_direct_statement_fact(
            user_id=self.user_id,
            agent_id=self.agent_id,
            statement_id=statement.id,
            text="Humans are mortal. They think.",
            role="user",
            source=source,
        )
        self.assertTrue(fact_id, "Expected fact to be recorded")
        facts = self.fact_service.get_facts_by_source(source.id)
        self.assertTrue(facts, "Expected facts to be recorded")
        sources = self.source_repo.list_sources_for_agent(self.agent_id)
        self.assertIn(source.id, {item.id for item in sources})
        self.assertIsNotNone(self.source_repo.get_source(source.id))

    def test_record_fact_registers_source_via_service(self):
        source = UrlSource("https://example.com/service", agent_id=self.agent_id)
        source_service = FactSourceService(
            fact_service=self.fact_service,
            belief_service=None,
            source_repository=self.source_repo,
        )
        self.fact_service.set_source_service(source_service)

        statement = self.config.statement_service.create(
            user_id=self.user_id,
            agent_id=self.agent_id,
            subject_notion=self.subject_notion,
            complement_notion=self.complement_notion,
            relation=Relation.from_string("is"),
            probability=0.9,
            validity=0.9,
        )
        fact_id = self.fact_service.record_direct_statement_fact(
            user_id=self.user_id,
            agent_id=self.agent_id,
            statement_id=statement.id,
            text="Service-backed source registration.",
            role="user",
            source=source,
        )

        self.assertTrue(fact_id, "Expected fact to be recorded")
        self.assertIsNotNone(self.source_repo.get_source(source.id))

    def test_observer_records_fact_with_source_on_statement_create(self):
        source = UrlSource("https://example.com/observer", agent_id=self.agent_id)
        sentence = "Humans are mortal."
        self.fact_registry.append_current_sentence(
            text=sentence,
            role="user",
            source=source,
        )
        statement = self.config.statement_service.create(
            user_id=self.user_id,
            agent_id=self.agent_id,
            subject_notion=self.subject_notion,
            complement_notion=self.complement_notion,
            relation=Relation.from_string("is"),
            probability=0.9,
            validity=0.9,
        )

        facts = self.fact_service.get_facts_by_source(source.id)
        self.assertTrue(facts, "Expected observer to record a fact")
        recorded = facts[0]
        self.assertEqual(recorded.statement_id, statement.id)
        self.assertEqual(getattr(recorded, "text", ""), sentence)

    def test_observer_records_fact_with_source_on_notion_create(self):
        source = UrlSource("https://example.com/observer-notion", agent_id=self.agent_id)
        sentence = "A human is a mammal."
        self.fact_registry.append_current_sentence(
            text=sentence,
            role="user",
            source=source,
        )
        notion = self.config.notion_service.create(
            user_id=self.user_id,
            agent_id=self.agent_id,
            expression=NotionExpression(caption="mammal"),
        )

        facts = self.fact_service.get_facts_by_source(source.id)
        self.assertTrue(facts, "Expected observer to record a fact")
        recorded = facts[0]
        self.assertEqual(recorded.notion_id, notion.id)
        self.assertEqual(getattr(recorded, "text", ""), sentence)


if __name__ == "__main__":
    import unittest

    unittest.main()
