import unittest
from unittest.mock import Mock
from analogai.foundation.common.enums import RelationshipType
from analogai.foundation.common.dtos.belief_expression import Relation, Modifier
from analogai.foundation.common.dtos.notion_expression import NotionExpression
from analogai.foundation.core.value_objects.time import Time
from analogai.deepthink.application.usecases.learning.make_direct_statement.models import MakeDirectStatementRequest, MakeDirectStatementResponse
from analogai.deepthink.application.usecases.learning.make_direct_statement.states.common.response_builder import LearnerResponseBuilder
from analogai.deepthink.application.usecases.learning.make_direct_statement.states.context.learner_context import LearnerContext
from analogai.deepthink.application.usecases.learning.make_direct_statement.states.context.learner_context_handler import LearnerContextHandler
from analogai.deepthink.application.usecases.learning.make_direct_statement.usecase import MakeDirectStatementUseCase
from analogai.deepthink.tests.functional.application.usecases.learner.setup import LearnerTestSetup

class TestLearnerFunctional(unittest.TestCase):
    def setUp(self):
        self.setup = LearnerTestSetup()
        self.mock_output_port = self.setup.mock_output_port
        
        context = LearnerContext()
        context_handler = LearnerContextHandler(context, self.setup.notion_service)
        response_builder = LearnerResponseBuilder(context)
        
        self.usecase = MakeDirectStatementUseCase(
            statement_service=self.setup.statement_service,
            notion_service=self.setup.notion_service,
            output_port=self.mock_output_port,
            context_handler=context_handler,
            response_builder=response_builder
        )

    def tearDown(self):
        self.setup.clear()

    def test_learn_new_statement(self):
        request = self.setup.create_learner_request(
            probability=0.8,
            subject_expression=NotionExpression(caption="Dog"),
            object_expression=NotionExpression(caption="Animal"),
            relation=Relation.from_type(RelationshipType.IS_A)
        )
        
        result = self.usecase.execute(request)
        
        self.assertIsNotNone(result)
        self.mock_output_port.mock_output_create_statement_success.assert_called_once()
        call_args = self.mock_output_port.mock_output_create_statement_success.call_args[0][0]
        self.assertEqual(call_args.belief_expression.subject_notion_expression.caption, "Dog")
        self.assertEqual(call_args.belief_expression.complement_notion_expression.caption, "Animal")
        self.assertEqual(call_args.belief_expression.relation, Relation.from_type(RelationshipType.IS_A))
        self.assertEqual(call_args.belief_expression.probability, 0.8)

    def test_learn_statement_with_probability_zero_persists_negation(self):
        request = self.setup.create_learner_request(
            probability=0.0,
            subject_expression=NotionExpression(caption="Human"),
            object_expression=NotionExpression(caption="mortal"),
            relation=Relation.from_type(RelationshipType.IS_A)
        )
        result = self.usecase.execute(request)
        self.assertIsNotNone(result)
        self.mock_output_port.mock_output_create_statement_success.assert_called_once()
        call_args = self.mock_output_port.mock_output_create_statement_success.call_args[0][0]
        self.assertEqual(call_args.belief_expression.probability, 0.0)
        self.assertIn("not", call_args.belief_expression.__str__().lower())

    def test_learn_statement_without_modifier(self):
        request = self.setup.create_learner_request(
            probability=0.8,
            subject_expression=NotionExpression(caption="Dog"),
            object_expression=NotionExpression(caption="Animal"),
            relation=Relation.from_type(RelationshipType.IS_A)
        )
        self.assertIsNone(request.modifier)
        result = self.usecase.execute(request)
        self.assertIsNotNone(result)
        self.mock_output_port.mock_output_create_statement_success.assert_called_once()
        call_args = self.mock_output_port.mock_output_create_statement_success.call_args[0][0]
        self.assertIsNone(call_args.belief_expression.modifier)

    def test_learn_statement_with_modifier(self):
        modifier = Modifier(location="Tbilisi")
        request = MakeDirectStatementRequest(
            user_agent=self.setup.user_agent,
            subject_notion_expression=NotionExpression(caption="it"),
            complement_notion_expression=NotionExpression(caption="rain"),
            relation=Relation.from_type(RelationshipType.DO),
            probability=1.0,
            modifier=modifier
        )
        result = self.usecase.execute(request)
        self.assertIsNotNone(result)
        self.mock_output_port.mock_output_create_statement_success.assert_called_once()
        call_args = self.mock_output_port.mock_output_create_statement_success.call_args[0][0]
        self.assertIsNotNone(call_args.belief_expression.modifier)
        self.assertEqual(call_args.belief_expression.modifier.location, "Tbilisi")

    def test_learn_statement_with_modifier_time(self):
        from_time = Time(year=2024, month=12, day=26)
        modifier = Modifier(from_time=from_time)
        request = MakeDirectStatementRequest(
            user_agent=self.setup.user_agent,
            subject_notion_expression=NotionExpression(caption="it"),
            complement_notion_expression=NotionExpression(caption="rain"),
            relation=Relation.from_type(RelationshipType.DO),
            probability=1.0,
            modifier=modifier
        )
        result = self.usecase.execute(request)
        self.assertIsNotNone(result)
        self.mock_output_port.mock_output_create_statement_success.assert_called_once()
        call_args = self.mock_output_port.mock_output_create_statement_success.call_args[0][0]
        self.assertIsNotNone(call_args.belief_expression.modifier)
        self.assertIsNotNone(call_args.belief_expression.modifier.from_time)
        self.assertEqual(call_args.belief_expression.modifier.from_time.year, 2024)

    def test_learn_duplicate_statement(self):
        initial_request = self.setup.create_learner_request(
            probability=0.8,
            subject_expression=NotionExpression(caption="Cat"),
            object_expression=NotionExpression(caption="Animal"),
            relation=Relation.from_type(RelationshipType.IS_A)
        )
        self.usecase.execute(initial_request)
        self.mock_output_port.mock_output_create_statement_success.reset_mock()
        self.mock_output_port.mock_output_statement_already_known.reset_mock()
        
        request = self.setup.create_learner_request(
            probability=0.8,
            subject_expression=NotionExpression(caption="Cat"),
            object_expression=NotionExpression(caption="Animal"),
            relation=Relation.from_type(RelationshipType.IS_A)
        )
        
        result = self.usecase.execute(request)
        
        self.assertIsNotNone(result)
        self.mock_output_port.mock_output_statement_already_known.assert_called_once()

    def test_learn_statement_with_different_probability(self):
        initial_request = self.setup.create_learner_request(
            probability=0.3,
            subject_expression=NotionExpression(caption="Bird"),
            object_expression=NotionExpression(caption="Animal"),
            relation=Relation.from_type(RelationshipType.IS_A)
        )
        self.usecase.execute(initial_request)
        self.mock_output_port.mock_output_create_statement_success.reset_mock()
        self.mock_output_port.mock_output_statement_update_success.reset_mock()
        
        request = self.setup.create_learner_request(
            probability=0.9,
            subject_expression=NotionExpression(caption="Bird"),
            object_expression=NotionExpression(caption="Animal"),
            relation=Relation.from_type(RelationshipType.IS_A)
        )
        
        result = self.usecase.execute(request)
        
        self.assertIsNotNone(result)
        self.mock_output_port.mock_output_statement_update_success.assert_called_once()

    def test_learn_statement_with_small_probability_difference(self):
        initial_request = self.setup.create_learner_request(
            probability=0.6,
            subject_expression=NotionExpression(caption="Fish"),
            object_expression=NotionExpression(caption="Animal"),
            relation=Relation.from_type(RelationshipType.IS_A)
        )
        self.usecase.execute(initial_request)
        self.mock_output_port.mock_output_create_statement_success.reset_mock()
        self.mock_output_port.mock_output_statement_update_success.reset_mock()
        self.mock_output_port.mock_output_update_not_applicable.reset_mock()
        
        request = self.setup.create_learner_request(
            probability=0.75,
            subject_expression=NotionExpression(caption="Fish"),
            object_expression=NotionExpression(caption="Animal"),
            relation=Relation.from_type(RelationshipType.IS_A)
        )
        
        result = self.usecase.execute(request)
        
        self.assertIsNotNone(result)
        self.mock_output_port.mock_output_update_not_applicable.assert_called_once()

    def test_learn_multiple_statements(self):
        learn_request = self.setup.create_learner_request(
            probability=0.7,
            subject_expression=NotionExpression(caption="Mammal"),
            object_expression=NotionExpression(caption="Animal"),
            relation=Relation.from_type(RelationshipType.IS_A)
        )
        result1 = self.usecase.execute(learn_request)
        
        learn_request = self.setup.create_learner_request(
            probability=0.8,
            subject_expression=NotionExpression(caption="Reptile"),
            object_expression=NotionExpression(caption="Animal"),
            relation=Relation.from_type(RelationshipType.IS_A)
        )
        result2 = self.usecase.execute(learn_request)
        
        self.assertIsNotNone(result1)
        self.assertIsNotNone(result2)
        self.assertEqual(self.mock_output_port.mock_output_create_statement_success.call_count, 2)

    def test_learn_with_complex_relationship(self):
        learn_request = self.setup.create_learner_request(
            probability=0.85,
            subject_expression=NotionExpression(caption="Human"),
            object_expression=NotionExpression(caption="Mammal"),
            relation=Relation.from_type(RelationshipType.IS_A)
        )
        result = self.usecase.execute(learn_request)
        
        self.assertIsNotNone(result)
        self.mock_output_port.mock_output_create_statement_success.assert_called_once()
        call_args = self.mock_output_port.mock_output_create_statement_success.call_args[0][0]
        self.assertEqual(call_args.belief_expression.relation, Relation.from_type(RelationshipType.IS_A))
        self.assertEqual(call_args.belief_expression.probability, 0.85)

    def test_learn_statement_with_different_modifier_creates_new(self):
        from datetime import datetime, timedelta
        today = datetime.now()
        tomorrow = today + timedelta(days=1)
        today_modal = Modifier(from_time=Time(year=today.year, month=today.month, day=today.day))
        tomorrow_modal = Modifier(from_time=Time(year=tomorrow.year, month=tomorrow.month, day=tomorrow.day))
        
        initial_request = self.setup.create_learner_request(
            probability=0.8,
            subject_expression=NotionExpression(caption="it"),
            object_expression=NotionExpression(caption="rain"),
            relation=Relation.from_type(RelationshipType.DO)
        )
        initial_request.modifier = today_modal
        self.usecase.execute(initial_request)
        self.mock_output_port.mock_output_create_statement_success.reset_mock()
        
        request = self.setup.create_learner_request(
            probability=0.8,
            subject_expression=NotionExpression(caption="it"),
            object_expression=NotionExpression(caption="rain"),
            relation=Relation.from_type(RelationshipType.DO)
        )
        request.modifier = tomorrow_modal
        
        result = self.usecase.execute(request)
        
        self.assertIsNotNone(result)
        self.mock_output_port.mock_output_create_statement_success.assert_called_once()

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


