import unittest
from unittest.mock import Mock
from analogai.foundation.core.observables.statement_observable_service import StatementObservableService, StatementObserver
from analogai.foundation.modules.direct_statement.direct_statement import DirectStatement as Statement
from analogai.foundation.modules.notion.notion import Notion
from analogai.foundation.modules.user.user import User
from analogai.foundation.core.value_objects.relation import Relation


class TestStatementObserver(StatementObserver):
    def __init__(self):
        self.created_statements = []
        self.updated_statements = []
    
    def on_statement_created(self, statement):
        self.created_statements.append(statement)
    
    def on_statement_updated(self, statement):
        self.updated_statements.append(statement)


class TestStatementObservableService(unittest.TestCase):
    def setUp(self):
        self.service = StatementObservableService()
        self.user = User(id="user1", name="Test User", email="test@example.com")
        self.notion1 = Notion(user_id="user1", agent_id="agent1", caption="Subject", id="notion1")
        self.notion2 = Notion(user_id="user1", agent_id="agent1", caption="Object", id="notion2")
        self.statement = Statement(
            user_id="user1",
            agent_id="agent1",
            subject_notion=self.notion1,
            relation=Relation.from_string("is"),
            complement_notion=self.notion2,
            probability=0.8,
            validity=0.9
        )
    
    def test_add_observer(self):
        observer = Mock(spec=StatementObserver)
        self.service.add_observer(observer)
        
        self.assertIn(observer, self.service._observers)
    
    def test_remove_observer(self):
        observer = Mock(spec=StatementObserver)
        self.service.add_observer(observer)
        self.service.remove_observer(observer)
        
        self.assertNotIn(observer, self.service._observers)
    
    def test_notify_statement_created_calls_observer(self):
        observer = Mock(spec=StatementObserver)
        observer.on_statement_created = Mock()
        self.service.add_observer(observer)
        
        self.service.notify_statement_created(self.statement)
        
        observer.on_statement_created.assert_called_once_with(self.statement)
    
    def test_notify_statement_updated_calls_observer(self):
        observer = Mock(spec=StatementObserver)
        observer.on_statement_updated = Mock()
        self.service.add_observer(observer)
        
        self.service.notify_statement_updated(self.statement)
        
        observer.on_statement_updated.assert_called_once_with(self.statement)
    
    def test_notify_statement_created_calls_all_observers(self):
        observer1 = Mock(spec=StatementObserver)
        observer1.on_statement_created = Mock()
        observer2 = Mock(spec=StatementObserver)
        observer2.on_statement_created = Mock()
        
        self.service.add_observer(observer1)
        self.service.add_observer(observer2)
        
        self.service.notify_statement_created(self.statement)
        
        observer1.on_statement_created.assert_called_once_with(self.statement)
        observer2.on_statement_created.assert_called_once_with(self.statement)
    
    def test_notify_statement_updated_calls_all_observers(self):
        observer1 = Mock(spec=StatementObserver)
        observer1.on_statement_updated = Mock()
        observer2 = Mock(spec=StatementObserver)
        observer2.on_statement_updated = Mock()
        
        self.service.add_observer(observer1)
        self.service.add_observer(observer2)
        
        self.service.notify_statement_updated(self.statement)
        
        observer1.on_statement_updated.assert_called_once_with(self.statement)
        observer2.on_statement_updated.assert_called_once_with(self.statement)
    
    def test_notify_statement_created_handles_missing_method_gracefully(self):
        observer = Mock()
        del observer.on_statement_created
        
        self.service.add_observer(observer)
        
        self.service.notify_statement_created(self.statement)
    
    def test_notify_statement_updated_handles_missing_method_gracefully(self):
        observer = Mock()
        del observer.on_statement_updated
        
        self.service.add_observer(observer)
        
        self.service.notify_statement_updated(self.statement)
    
    def test_notify_statement_created_with_concrete_observer(self):
        observer = TestStatementObserver()
        self.service.add_observer(observer)
        
        self.service.notify_statement_created(self.statement)
        
        self.assertEqual(len(observer.created_statements), 1)
        self.assertEqual(observer.created_statements[0], self.statement)
        self.assertEqual(len(observer.updated_statements), 0)
    
    def test_notify_statement_updated_with_concrete_observer(self):
        observer = TestStatementObserver()
        self.service.add_observer(observer)
        
        self.service.notify_statement_updated(self.statement)
        
        self.assertEqual(len(observer.updated_statements), 1)
        self.assertEqual(observer.updated_statements[0], self.statement)
        self.assertEqual(len(observer.created_statements), 0)
    
    def test_notify_both_created_and_updated(self):
        observer = TestStatementObserver()
        self.service.add_observer(observer)
        
        statement1 = Statement(
            user_id="user1",
            agent_id="agent1",
            subject_notion=self.notion1,
            relation=Relation.from_string("is"),
            complement_notion=self.notion2,
            probability=0.8,
            validity=0.9
        )
        statement2 = Statement(
            user_id="user1",
            agent_id="agent1",
            subject_notion=self.notion1,
            relation=Relation.from_string("has"),
            complement_notion=self.notion2,
            probability=0.7,
            validity=0.8
        )
        
        self.service.notify_statement_created(statement1)
        self.service.notify_statement_updated(statement2)
        
        self.assertEqual(len(observer.created_statements), 1)
        self.assertEqual(observer.created_statements[0], statement1)
        self.assertEqual(len(observer.updated_statements), 1)
        self.assertEqual(observer.updated_statements[0], statement2)


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