from unittest import TestCase

from analogai.foundation.settings import config
from analogai.foundation.common.dtos.notion_expression import NotionExpression
from analogai.foundation.core.value_objects.relation import Relation
from analogai.foundation.core.value_objects.cause import Cause
from analogai.foundation.core.value_objects.effect import Effect
from analogai.foundation.common.enums.criterion import Criterion
from analogai.foundation.modules.fact.factories.fact_factory import FactServiceFactory
from analogai.foundation.modules.fact.subscribers.fact_observer_builder import FactObserverBuilder
from analogai.foundation.modules.fact.hypothetical_statement_fact import HypotheticalStatementFact
from analogai.foundation.modules.fact.repositories.infrastructure.in_memory_fact_repository import (
    InMemoryFactRepository,
)
from analogai.foundation.modules.fact.source.repositories.infrastructure.in_memory_fact_source_repository import (
    InMemoryFactSourceRepository,
)
from analogai.foundation.modules.fact.source.fact_source import UrlSource
from analogai.foundation.tests.functional.services.setup import ServicesTestConfig


class TestFactObserverIntegration(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()

        builder = FactObserverBuilder(
            fact_service=self.fact_service,
            registry=self.fact_registry,
        )
        builder.observe_statement_service(self.config.statement_service)
        builder.observe_notion_service(self.config.notion_service)
        builder.observe_hypothetical_statement_service(self.config.hypothetical_statement_service)

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

    def test_observers_record_facts_with_sources(self):
        self.fact_registry.clear_current_sentence()
        statement_source = UrlSource("https://example.com/statement", agent_id=self.agent_id)
        self.fact_registry.append_current_sentence(
            text="Humans are mortal.",
            role="user",
            source=statement_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,
        )
        statement_facts = self.fact_service.get_facts_by_source(statement_source.id)
        self.assertEqual(len(statement_facts), 1)
        self.assertEqual(statement_facts[0].statement_id, statement.id)
        self.assertEqual(getattr(statement_facts[0], "text", ""), "Humans are mortal.")

        self.fact_registry.clear_current_sentence()
        notion_source = UrlSource("https://example.com/notion", agent_id=self.agent_id)
        self.fact_registry.append_current_sentence(
            text="A human is a mammal.",
            role="user",
            source=notion_source,
        )
        notion = self.config.notion_service.create(
            user_id=self.user_id,
            agent_id=self.agent_id,
            expression=NotionExpression(caption="mammal"),
        )
        notion_facts = self.fact_service.get_facts_by_source(notion_source.id)
        self.assertEqual(len(notion_facts), 1)
        self.assertEqual(notion_facts[0].notion_id, notion.id)
        self.assertEqual(getattr(notion_facts[0], "text", ""), "A human is a mammal.")

        self.fact_registry.clear_current_sentence()
        hypothetical_source = UrlSource("https://example.com/hypothetical", agent_id=self.agent_id)
        self.fact_registry.append_current_sentence(
            text="If it rains, the ground gets wet.",
            role="user",
            source=hypothetical_source,
        )
        causes = [
            Cause({
                Criterion.SubjectCaption: "rain",
                Criterion.ObjectCaption: "today",
                Criterion.Relation: Relation.from_string("is"),
            })
        ]
        effect = Effect({
            Criterion.SubjectCaption: "ground",
            Criterion.ObjectCaption: "wet",
            Criterion.Relation: Relation.from_string("is"),
        })
        hypothetical = self.config.hypothetical_statement_service.create(
            user_id=self.user_id,
            agent_id=self.agent_id,
            causes=causes,
            effect=effect,
        )
        hypothetical_facts = self.fact_service.get_facts_by_source(hypothetical_source.id)
        self.assertEqual(len(hypothetical_facts), 1)
        self.assertEqual(hypothetical_facts[0].statement_id, hypothetical.id)
        self.assertEqual(
            getattr(hypothetical_facts[0], "text", ""),
            "If it rains, the ground gets wet.",
        )
        self.assertIsInstance(hypothetical_facts[0], HypotheticalStatementFact)


if __name__ == "__main__":
    import unittest

    unittest.main()
