import unittest
from datetime import datetime, timedelta
from unittest.mock import Mock

from analogai.foundation.modules.belief.belief_service import BeliefService
from analogai.foundation.modules.notion.notion import Notion
from analogai.foundation.common.dtos.notion_expression import NotionExpression
from analogai.foundation.core.value_objects.relation import Relation
from analogai.foundation.core.value_objects.modifier import Modifier
from analogai.foundation.modules.direct_statement.direct_statement import DirectStatement as Statement


class TestBeliefService(unittest.TestCase):
    def setUp(self):
        self.notion_service = Mock()
        self.statement_service = Mock()
        self.service = BeliefService(
            statement_service=self.statement_service,
            notion_service=self.notion_service,
        )

        self.agent_id = "test_agent_id"
        self.user_id = "user_id"
        self.subject_notion = Notion(user_id=self.user_id, agent_id=self.agent_id, caption="source", id="source_id")
        self.complement_notion = Notion(user_id=self.user_id, agent_id=self.agent_id, caption="target", id="target_id")
        self.relation = Relation.from_string("causes")
        self.subject_expression = NotionExpression(caption="source")
        self.object_expression = NotionExpression(caption="target")
        self.statement_service.find_by_subject_and_relation.return_value = []
        self.statement_service.find_by_subject.return_value = []
        self.statement_service.find_by_object.return_value = []
        self.statement_service.find_by_agent_id.return_value = []

    def test_find_returns_none_when_empty(self):
        self.statement_service.find_by_subject_and_relation.return_value = []
        result = self.service.find(
            subject_notion=self.subject_notion,
            complement_notion=self.complement_notion,
            relation=self.relation,
        )

        self.assertIsNone(result)

    def test_find_returns_belief_for_matching_statement(self):
        statement = Statement(
            user_id=self.user_id,
            agent_id=self.agent_id,
            subject_notion=self.subject_notion,
            complement_notion=self.complement_notion,
            relation=self.relation,
            probability=0.7,
            validity=0.9,
        )
        self.statement_service.find_by_agent_id.return_value = [statement]

        belief = self.service.find(
            subject_notion=self.subject_notion,
            complement_notion=self.complement_notion,
            relation=self.relation,
        )

        self.assertIsNotNone(belief)
        self.assertEqual(len(belief.statements), 1)

    def test_find_by_expressions_resolves_belief(self):
        statement = Statement(
            user_id=self.user_id,
            agent_id=self.agent_id,
            subject_notion=self.subject_notion,
            complement_notion=self.complement_notion,
            relation=self.relation,
        )
        self.statement_service.find_by_agent_id.return_value = [statement]
        self.notion_service.find.side_effect = [self.subject_notion, self.complement_notion]

        belief = self.service.find_by_expressions(
            agent_id=self.agent_id,
            subject_notion_expression=self.subject_expression,
            complement_notion_expression=self.object_expression,
            relation=self.relation,
        )

        self.assertIsNotNone(belief)
        self.assertEqual(belief.subject_notion.id, "source_id")

    def test_search_by_notions_groups_beliefs(self):
        statement_a = Statement(
            user_id=self.user_id,
            agent_id=self.agent_id,
            subject_notion=self.subject_notion,
            complement_notion=self.complement_notion,
            relation=Relation.from_string("causes"),
        )
        statement_b = Statement(
            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"),
        )
        self.statement_service.find_by_subject.return_value = [statement_a, statement_b]

        beliefs = self.service.search_by_notions(self.subject_notion, self.complement_notion)
        self.assertEqual(len(beliefs), 2)

    def test_search_by_subject_notion_expression_returns_none(self):
        missing_expr = NotionExpression(caption="missing")
        self.notion_service.find.return_value = None
        result = self.service.search_by_subject_notion_expression(self.agent_id, missing_expr)
        self.assertIsNone(result)

    def test_get_all_for_agent(self):
        statement = Statement(
            user_id=self.user_id,
            agent_id=self.agent_id,
            subject_notion=self.subject_notion,
            complement_notion=self.complement_notion,
            relation=self.relation,
        )
        self.statement_service.find_by_agent_id.return_value = [statement]

        beliefs = self.service.get_all_for_agent(self.agent_id)
        self.assertEqual(len(beliefs), 1)

    def test_get_recently_updated_returns_recent(self):
        statement = Statement(
            user_id=self.user_id,
            agent_id=self.agent_id,
            subject_notion=self.subject_notion,
            complement_notion=self.complement_notion,
            relation=self.relation,
        )
        self.statement_service.find_by_agent_id.return_value = [statement]

        since = datetime.now() - timedelta(minutes=1)
        recent = self.service.get_recently_updated(self.agent_id, since)
        self.assertGreater(len(recent), 0)

    def test_find_with_modifier_filters(self):
        statement = Statement(
            user_id=self.user_id,
            agent_id=self.agent_id,
            subject_notion=self.subject_notion,
            complement_notion=self.complement_notion,
            relation=self.relation,
            modifier=Modifier(location="Berlin"),
        )
        self.statement_service.find_by_agent_id.return_value = [statement]

        belief = self.service.find(
            subject_notion=self.subject_notion,
            complement_notion=self.complement_notion,
            relation=self.relation,
            modifier=Modifier(location="Berlin"),
        )

        self.assertIsNotNone(belief)

    def test_delete_removes_attached_statements(self):
        statement_a = Statement(
            user_id=self.user_id,
            agent_id=self.agent_id,
            subject_notion=self.subject_notion,
            complement_notion=self.complement_notion,
            relation=self.relation,
        )
        statement_b = Statement(
            user_id=self.user_id,
            agent_id=self.agent_id,
            subject_notion=self.subject_notion,
            complement_notion=self.complement_notion,
            relation=self.relation,
        )
        belief = self.service.add_statement_to_belief(statement_a)
        belief.add_statement(statement_b)

        self.service.delete(belief)

        self.assertEqual(self.statement_service.delete.call_count, 2)
        self.statement_service.delete.assert_any_call(statement_a)
        self.statement_service.delete.assert_any_call(statement_b)


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