import unittest
from unittest.mock import Mock

from analogai.foundation.modules.belief.belief_service import BeliefService
from analogai.foundation.modules.direct_statement.direct_statement import DirectStatement as Statement
from analogai.foundation.modules.notion.notion import Notion
from analogai.foundation.core.value_objects.time import Time
from analogai.foundation.core.value_objects.modifier import Modifier
from analogai.foundation.core.value_objects.relation import Relation
from analogai.foundation.common.enums.deontic_modality import DeonticModality


class TestBeliefServiceSpatiotemporalDeontic(unittest.TestCase):
    def setUp(self):
        self.notion_service = Mock()
        self.statement_service = Mock()
        self._statements = []
        self.statement_service.find_by_agent_id.side_effect = lambda agent_id: list(self._statements)
        self.service = BeliefService(
            statement_service=self.statement_service,
            notion_service=self.notion_service,
        )

        self.agent_id = "test_agent_id"
        self.user_id = "test_user_id"
        self.subject_notion = Notion(user_id=self.user_id, agent_id=self.agent_id, caption="human", id="human_id")
        self.complement_notion = Notion(user_id=self.user_id, agent_id=self.agent_id, caption="mortal", id="mortal_id")
        self.relation = Relation.from_string("is")

    def create_statement(self, location=None, from_time=None, to_time=None, deontic_modality=None, probability=0.8, validity=0.9):
        
        modifier = None
        if any([location, from_time, to_time, deontic_modality]):
            from analogai.foundation.common.utils.modifier_utils import ModifierBuilder
            builder = ModifierBuilder()
            if location:
                builder.with_location(location)
            if from_time:
                builder.with_from_time(
                    year=from_time.year,
                    month=from_time.month,
                    day=from_time.day,
                    hour=from_time.hour,
                    minute=from_time.minute
                )
            if to_time:
                builder.with_to_time(
                    year=to_time.year,
                    month=to_time.month,
                    day=to_time.day,
                    hour=to_time.hour,
                    minute=to_time.minute
                )
            if deontic_modality:
                builder.with_deontic_modality(deontic_modality)
            modifier = builder.build()
        statement = Statement(
            user_id=self.user_id,
            agent_id=self.agent_id,
            subject_notion=self.subject_notion,
            relation=self.relation,
            complement_notion=self.complement_notion,
            probability=probability,
            validity=validity,
            modifier=modifier,
        )
        self._statements.append(statement)
        return statement

    def test_add_statement_with_matching_location(self):
        
        self.create_statement(location="New York")
        self.create_statement(location="New York")

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

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

    def test_add_statement_with_mismatched_location_raises_error(self):
        
        self.create_statement(location="New York")
        self.create_statement(location="London")

        belief_ny = self.service.find(
            self.subject_notion,
            self.complement_notion,
            self.relation,
            modifier=Modifier(location="New York"),
        )
        belief_ldn = self.service.find(
            self.subject_notion,
            self.complement_notion,
            self.relation,
            modifier=Modifier(location="London"),
        )

        self.assertIsNotNone(belief_ny)
        self.assertIsNotNone(belief_ldn)
        self.assertEqual(len(belief_ny.statements), 1)
        self.assertEqual(len(belief_ldn.statements), 1)

    def test_add_statement_with_matching_from_time(self):
        
        from_time = Time(year=2024, month=1, day=1)
        self.create_statement(from_time=from_time)
        self.create_statement(from_time=from_time)

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

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

    def test_add_statement_with_mismatched_from_time_raises_error(self):
        
        from_time1 = Time(year=2024, month=1, day=1)
        from_time2 = Time(year=2024, month=2, day=1)
        self.create_statement(from_time=from_time1)
        self.create_statement(from_time=from_time2)

        belief1 = self.service.find(
            self.subject_notion,
            self.complement_notion,
            self.relation,
            modifier=Modifier(from_time=from_time1),
        )
        belief2 = self.service.find(
            self.subject_notion,
            self.complement_notion,
            self.relation,
            modifier=Modifier(from_time=from_time2),
        )

        self.assertIsNotNone(belief1)
        self.assertIsNotNone(belief2)
        self.assertEqual(len(belief1.statements), 1)
        self.assertEqual(len(belief2.statements), 1)

    def test_add_statement_with_matching_to_time(self):
        
        to_time = Time(year=2024, month=12, day=31)
        self.create_statement(to_time=to_time)
        self.create_statement(to_time=to_time)

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

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

    def test_add_statement_with_mismatched_to_time_raises_error(self):
        
        to_time1 = Time(year=2024, month=12, day=31)
        to_time2 = Time(year=2025, month=1, day=1)
        self.create_statement(to_time=to_time1)
        self.create_statement(to_time=to_time2)

        belief1 = self.service.find(
            self.subject_notion,
            self.complement_notion,
            self.relation,
            modifier=Modifier(to_time=to_time1),
        )
        belief2 = self.service.find(
            self.subject_notion,
            self.complement_notion,
            self.relation,
            modifier=Modifier(to_time=to_time2),
        )

        self.assertIsNotNone(belief1)
        self.assertIsNotNone(belief2)
        self.assertEqual(len(belief1.statements), 1)
        self.assertEqual(len(belief2.statements), 1)

    def test_add_statement_with_matching_deontic_modality(self):
        
        self.create_statement(deontic_modality=DeonticModality.OBLIGATORY)
        self.create_statement(deontic_modality=DeonticModality.OBLIGATORY)

        belief = self.service.find(
            self.subject_notion,
            self.complement_notion,
            self.relation,
            modifier=Modifier(deontic_modality=DeonticModality.OBLIGATORY),
        )

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

    def test_add_statement_with_mismatched_deontic_modality_raises_error(self):
        
        self.create_statement(deontic_modality=DeonticModality.OBLIGATORY)
        self.create_statement(deontic_modality=DeonticModality.PERMITTED)

        belief1 = self.service.find(
            self.subject_notion,
            self.complement_notion,
            self.relation,
            modifier=Modifier(deontic_modality=DeonticModality.OBLIGATORY),
        )
        belief2 = self.service.find(
            self.subject_notion,
            self.complement_notion,
            self.relation,
            modifier=Modifier(deontic_modality=DeonticModality.PERMITTED),
        )

        self.assertIsNotNone(belief1)
        self.assertIsNotNone(belief2)
        self.assertEqual(len(belief1.statements), 1)
        self.assertEqual(len(belief2.statements), 1)

    def test_add_statement_with_all_matching_parameters(self):
        
        from_time = Time(year=2024, month=1, day=1)
        to_time = Time(year=2024, month=12, day=31)
        self.create_statement(
            location="Paris",
            from_time=from_time,
            to_time=to_time,
            deontic_modality=DeonticModality.PERMITTED
        )
        self.create_statement(
            location="Paris",
            from_time=from_time,
            to_time=to_time,
            deontic_modality=DeonticModality.PERMITTED
        )

        belief = self.service.find(
            self.subject_notion,
            self.complement_notion,
            self.relation,
            modifier=Modifier(
                location="Paris",
                from_time=from_time,
                to_time=to_time,
                deontic_modality=DeonticModality.PERMITTED,
            ),
        )

        self.assertIsNotNone(belief)
        self.assertEqual(len(belief.statements), 2)
        self.assertEqual(belief.location, "Paris")
        self.assertEqual(belief.deontic_modality, DeonticModality.PERMITTED)

    def test_update_composition_validates_parameters(self):
        
        self.create_statement(location="Tokyo", deontic_modality=DeonticModality.FORBIDDEN)
        self.create_statement(location="Osaka", deontic_modality=DeonticModality.FORBIDDEN)

        belief_tokyo = self.service.find(
            self.subject_notion,
            self.complement_notion,
            self.relation,
            modifier=Modifier(location="Tokyo", deontic_modality=DeonticModality.FORBIDDEN),
        )
        belief_osaka = self.service.find(
            self.subject_notion,
            self.complement_notion,
            self.relation,
            modifier=Modifier(location="Osaka", deontic_modality=DeonticModality.FORBIDDEN),
        )

        self.assertIsNotNone(belief_tokyo)
        self.assertIsNotNone(belief_osaka)
        self.assertEqual(len(belief_tokyo.statements), 1)
        self.assertEqual(len(belief_osaka.statements), 1)

    def test_none_values_match(self):
        
        self.create_statement()  # All None
        self.create_statement()  # All None

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

        self.assertEqual(len(belief.statements), 2)
        self.assertIsNone(belief.location)
        self.assertIsNone(belief.from_time)
        self.assertIsNone(belief.to_time)
        self.assertIsNone(belief.deontic_modality)

    def test_time_comparison_handles_partial_values(self):
        
        from_time1 = Time(year=2024, month=1)
        from_time2 = Time(year=2024, month=1)
        self.create_statement(from_time=from_time1)
        self.create_statement(from_time=from_time2)

        belief = self.service.find(
            self.subject_notion,
            self.complement_notion,
            self.relation,
            modifier=Modifier(from_time=from_time1),
        )

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


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