import unittest
from datetime import datetime
from unittest.mock import Mock

from analogai.foundation.common.enums import RelationshipType
from analogai.foundation.core.value_objects import Relation
from analogai.foundation.core.value_objects.modifier import Modifier
from analogai.foundation.core.value_objects.time import Time
from analogai.foundation.common.enums.deontic_modality import DeonticModality
from analogai.foundation.modules.notion.notion_service import NotionService
from analogai.foundation.common.design_patterns.state_machine import State
from analogai.deepthink.application.value_objects.user_agent import UserAgent
from analogai.deepthink.application.usecases.learning.make_direct_statement.states.create_statement_state import CreateStatementState
from analogai.deepthink.application.usecases.learning.make_direct_statement.usecase import MakeDirectStatementUseCase
from analogai.deepthink.application.usecases.learning.make_direct_statement.states.common.response_builder import LearnerResponseBuilder
from analogai.deepthink.application.usecases.learning.make_direct_statement.ports import MakeStatementOutputPort

class TestCreateStatementState(unittest.TestCase):
    def setUp(self):
        self.state_machine = Mock(spec=MakeDirectStatementUseCase)
        self.statement_service = Mock()
        self.notion_service = Mock(spec=NotionService)
        self.output_port = Mock(spec=MakeStatementOutputPort)
        self.response_builder = Mock(spec=LearnerResponseBuilder)
        
        self.user_agent = UserAgent(
            user_id="test-user",
            agent_id="test-agent",
            user_authority=0.8
        )
        
        self.context = Mock(spec=['user_agent', 'subject_notion', 'complement_notion', 'relation', 'proposed_probability', 'proposed_validity', 'proposed_quantity', 'modifier', 'subject_notion_expression', 'complement_notion_expression'])
        self.context.user_agent = self.user_agent
        self.context.relation = Relation.from_type(RelationshipType.IS_A)
        self.context.subject_notion_expression = Mock()
        self.context.subject_notion_expression.children = None
        self.context.complement_notion_expression = Mock()
        self.context.complement_notion_expression.children = None
        self.state_machine.context = self.context
        self.create_statement_state = CreateStatementState(
            self.state_machine,
            self.statement_service,
            self.notion_service,
            self.output_port,
            self.response_builder
        )

    def test_init(self):
        self.assertEqual(self.create_statement_state.state_machine, self.state_machine)
        self.assertEqual(self.create_statement_state._statement_service, self.statement_service)
        self.assertEqual(self.create_statement_state._output_port, self.output_port)
        self.assertEqual(self.create_statement_state._response_builder, self.response_builder)
        self.assertIsInstance(self.create_statement_state, State)

    def test_execute(self):
        main_statement = Mock()
        response = Mock()
        self.create_statement_state._generate_statement_hierarchy = Mock(return_value=main_statement)
        self.create_statement_state._store_statement_in_context = Mock()
        self.create_statement_state._build_and_output_response = Mock(return_value=response)
        
        result = self.create_statement_state.execute()
        
        self.create_statement_state._generate_statement_hierarchy.assert_called_once()
        self.create_statement_state._store_statement_in_context.assert_called_once_with(main_statement)
        self.create_statement_state._build_and_output_response.assert_called_once()
        self.assertEqual(result, response)

    def test_generate_statement_hierarchy(self):
        subject_notion = Mock()
        complement_notion = Mock()
        self.context.subject_notion = subject_notion
        self.context.complement_notion = complement_notion
        statement = Mock()
        self.create_statement_state._create_statement_for_pair = Mock(return_value=statement)
        
        result = self.create_statement_state._generate_statement_hierarchy()
        
        self.create_statement_state._create_statement_for_pair.assert_called_once_with(subject_notion, complement_notion)
        self.assertEqual(result, statement)

    def test_store_statement_in_context(self):
        statement = Mock()
        
        self.create_statement_state._store_statement_in_context(statement)
        
        self.assertEqual(self.context.statement, statement)

    def test_create_statement_for_pair(self):
        subject_notion = Mock()
        subject_notion.id = "source-id"
        complement_notion = Mock()
        complement_notion.id = "target-id"
        test_relation = Relation.from_type(RelationshipType.IS_A)
        self.context.relation = test_relation
        self.context.proposed_probability = 0.8
        self.context.proposed_validity = "valid"
        self.context.proposed_quantity = None
        self.context.modifier = None
        statement = Mock()
        self.statement_service.create.return_value = statement
        
        result = self.create_statement_state._create_statement_for_pair(subject_notion, complement_notion)
        
        call_kwargs = self.statement_service.create.call_args[1]
        self.assertEqual(call_kwargs['user_id'], self.context.user_agent.user_id)
        self.assertEqual(call_kwargs['agent_id'], self.context.user_agent.agent_id)
        self.assertIs(call_kwargs['subject_notion'], subject_notion)
        self.assertIs(call_kwargs['complement_notion'], complement_notion)
        self.assertEqual(call_kwargs['relation'], test_relation)
        self.assertEqual(call_kwargs['probability'], 0.8)
        self.assertEqual(call_kwargs['validity'], "valid")
        self.assertEqual(call_kwargs.get('modifier'), None)
        self.assertIs(call_kwargs.get('quantity'), None)
        self.assertEqual(result, statement)

    def test_create_statement_for_pair_with_modifier(self):
        today = datetime.now()
        modifier = Modifier(
            location="Tbilisi",
            from_time=Time(year=today.year, month=today.month, day=today.day),
            deontic_modality=DeonticModality.OBLIGATORY
        )

        subject_notion = Mock()
        subject_notion.id = "source-id"
        complement_notion = Mock()
        complement_notion.id = "target-id"
        self.context.relation = Relation.from_type(RelationshipType.IS_A)
        self.context.proposed_probability = 0.8
        self.context.proposed_validity = "valid"
        self.context.proposed_quantity = 5.0
        self.context.modifier = modifier
        statement = Mock()
        self.statement_service.create.return_value = statement

        result = self.create_statement_state._create_statement_for_pair(subject_notion, complement_notion)
        
        call_kwargs = self.statement_service.create.call_args[1]
        self.assertEqual(call_kwargs.get('modifier'), modifier)
        self.assertEqual(call_kwargs.get('quantity'), 5.0)
        self.assertEqual(result, statement)

    def test_build_and_output_response(self):
        response = Mock()
        self.response_builder.build_response.return_value = response
        self.output_port.output_create_statement_success.return_value = "success_output"
        
        result = self.create_statement_state._build_and_output_response()
        
        self.response_builder.build_response.assert_called_once()
        self.output_port.output_create_statement_success.assert_called_once_with(response)
        self.assertEqual(result, "success_output")

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



