import unittest

from analogai.foundation.settings import config
from analogai.foundation.common.utils.expression_factory import ExpressionFactory
from analogai.foundation.modules.notion.notion import Notion
from analogai.foundation.modules.belief.belief import Belief
from analogai.foundation.modules.categorical_deduction.categorical_deduction import CategoricalDeduction
from analogai.foundation.modules.direct_statement.direct_statement import DirectStatement as Statement

from analogai.foundation.modules.user.user import User
from analogai.foundation.modules.agent.agent import Agent
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 TestExpressionFactory(unittest.TestCase):
    def setUp(self):
        self.factory = ExpressionFactory()
        self.user = User(id="user_id", name="Test User", email="test@example.com")
        self.agent = Agent(id="agent_id", knowledge_base="Test KB", creator=self.user, roles=[])
        
        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"
        )
        
        statement = Statement(
            user_id=self.user.id,
            agent_id=self.agent.id,
            subject_notion=self.subject_notion,
            relation=Relation.from_string("is"),
            complement_notion=self.complement_notion,
            probability=0.8,
            validity=0.9
        )
        self.statement = statement
        self.belief = Belief(agent_id=self.agent.id)
        self.belief.add_statement(self.statement)
        self.belief.add_statement(self.statement)

    def test_propagate_notion_expression(self):
        result = self.factory.propagate_notion_expression(self.subject_notion)
        
        self.assertIsNotNone(result)
        self.assertEqual(result.caption, "human")
        self.assertEqual(len(result.children), 0)
        self.assertEqual(len(result.context), 0)
    
    def test_create_notion_expression_from_string(self):
        result = self.factory.create_notion_expression("humans")
        
        self.assertIsNotNone(result)
        if config.get("lemmatize", False):
            self.assertEqual(result.caption, "human")
        else:
            self.assertEqual(result.caption, "humans")
        self.assertEqual(len(result.children), 0)
        self.assertEqual(len(result.context), 0)
    
    def test_create_notion_expression_preserves_proper_nouns(self):
        result = self.factory.create_notion_expression("Socrates")
        
        self.assertIsNotNone(result)
        self.assertEqual(result.caption, "Socrates")
        self.assertEqual(len(result.children), 0)
        self.assertEqual(len(result.context), 0)
    
    def test_propagate_belief_expression(self):
        result = self.factory.propagate_belief_expression(self.belief)
        
        self.assertIsNotNone(result)
        self.assertEqual(result.subject_notion_expression.caption, "human")
        self.assertEqual(result.complement_notion_expression.caption, "mortal")
        self.assertEqual(result.relation, Relation.from_string("is"))
        self.assertEqual(result.probability, self.belief.probability)
        self.assertEqual(result.validity, self.belief.validity)
        self.assertEqual(result.modifier, self.belief.modifier)
    
    def test_propagate_belief_expression_propagates_notion_expressions(self):
        result = self.factory.propagate_belief_expression(self.belief)
        
        self.assertIsInstance(result.subject_notion_expression, type(self.factory.propagate_notion_expression(self.subject_notion)))
        self.assertIsInstance(result.complement_notion_expression, type(self.factory.propagate_notion_expression(self.complement_notion)))
    
    def test_propagate_categorical_deduction_expression(self):
        major_premise_notion = Notion(
            user_id=self.user.id,
            agent_id=self.agent.id,
            caption="Socrates",
            id="socrates_id"
        )
        major_complement_notion = Notion(
            user_id=self.user.id,
            agent_id=self.agent.id,
            caption="human",
            id="human_id"
        )
        
        major_statement = Statement(
            user_id=self.user.id,
            agent_id=self.agent.id,
            subject_notion=major_premise_notion,
            relation=Relation.from_string("is"),
            complement_notion=major_complement_notion,
            probability=0.9,
            validity=0.95
        )
        major_premise = Belief(agent_id=self.agent.id)
        major_premise.add_statement(major_statement)
        
        minor_premise = Belief(agent_id=self.agent.id)
        minor_premise.add_statement(self.statement)

        inference = CategoricalDeduction(
            agent_id=self.agent.id,
            subject_notion=major_premise_notion,
            complement_notion=self.complement_notion,
            relation=Relation.from_string("is"),
            major_premise=major_premise,
            minor_premise=minor_premise,
        )

        result = self.factory.propagate_categorical_deduction_expression(inference)

        self.assertIsNotNone(result)
        self.assertEqual(result.major_premise_expression.subject_notion_expression.caption, "Socrates")
        self.assertEqual(result.major_premise_expression.complement_notion_expression.caption, "human")
        self.assertEqual(result.minor_premise_expression.subject_notion_expression.caption, "human")
        self.assertEqual(result.minor_premise_expression.complement_notion_expression.caption, "mortal")
        self.assertEqual(result.subject_notion_expression.caption, "Socrates")
        self.assertEqual(result.complement_notion_expression.caption, "mortal")
        self.assertEqual(result.relation, Relation.from_string("is"))
        self.assertEqual(result.probability, inference.probability)
        self.assertEqual(result.validity, inference.validity)
    
    def test_propagate_categorical_deduction_expression_propagates_all_nested_expressions(self):
        major_premise_notion = Notion(
            user_id=self.user.id,
            agent_id=self.agent.id,
            caption="Socrates",
            id="socrates_id"
        )
        major_complement_notion = Notion(
            user_id=self.user.id,
            agent_id=self.agent.id,
            caption="human",
            id="human_id"
        )
        
        major_statement = Statement(
            user_id=self.user.id,
            agent_id=self.agent.id,
            subject_notion=major_premise_notion,
            relation=Relation.from_string("is"),
            complement_notion=major_complement_notion,
            probability=0.9,
            validity=0.95
        )
        major_premise = Belief(agent_id=self.agent.id)
        major_premise.add_statement(major_statement)
        
        minor_premise = Belief(agent_id=self.agent.id)
        minor_premise.add_statement(self.statement)

        inference = CategoricalDeduction(
            agent_id=self.agent.id,
            subject_notion=major_premise_notion,
            complement_notion=self.complement_notion,
            relation=Relation.from_string("is"),
            major_premise=major_premise,
            minor_premise=minor_premise,
        )

        result = self.factory.propagate_categorical_deduction_expression(inference)

        self.assertIsNotNone(result.major_premise_expression)
        self.assertIsNotNone(result.minor_premise_expression)
        self.assertIsNotNone(result.subject_notion_expression)
        self.assertIsNotNone(result.complement_notion_expression)
        self.assertEqual(result.major_premise_expression.probability, major_premise.probability)
        self.assertEqual(result.major_premise_expression.validity, major_premise.validity)
        self.assertEqual(result.minor_premise_expression.probability, minor_premise.probability)
        self.assertEqual(result.minor_premise_expression.validity, minor_premise.validity)
    
    def test_propagate_belief_expression_with_modifier(self):
        modifier = Modifier(
            location="Athens",
            deontic_modality=DeonticModality.OBLIGATORY
        )
        
        statement_with_context = Statement(
            user_id=self.user.id,
            agent_id=self.agent.id,
            subject_notion=self.subject_notion,
            relation=Relation.from_string("is"),
            complement_notion=self.complement_notion,
            probability=0.8,
            validity=0.9,
            modifier=modifier
        )
        belief_with_context = Belief(agent_id=self.agent.id)
        belief_with_context.add_statement(statement_with_context)
        
        result = self.factory.propagate_belief_expression(belief_with_context)
        
        self.assertIsNotNone(result.modifier)
        self.assertEqual(result.modifier.location, "Athens")
        self.assertEqual(result.modifier.deontic_modality, DeonticModality.OBLIGATORY)


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