import unittest
from unittest.mock import Mock
from analogai.foundation.modules.notion.notion_service import NotionService
from analogai.foundation.common.dtos.notion_expression import NotionExpression
from analogai.foundation.modules.notion.notion import Notion
from analogai.foundation.settings import config


class TestNotionService(unittest.TestCase):
    def setUp(self):
        self._config_override = config.override(**{"vector.enabled": False})
        self._config_override.__enter__()
        self.notion_repository = Mock()
        self.service = NotionService(self.notion_repository)
        self.expression = NotionExpression(caption="test")
        self.user_id = "user123"
        self.agent_id = "agent123"

    def test_find_or_create_existing_notion(self):
        notion = Mock(spec=Notion)
        notion.caption = self.expression.caption
        self.notion_repository.find.return_value = notion
        result = self.service.find_or_create(self.user_id, self.agent_id, self.expression)
        self.notion_repository.find.assert_called_once_with(
            self.agent_id,
            self.expression,
            None,
            similarity_threshold=1.0,
        )
        self.notion_repository.create.assert_not_called()
        self.assertEqual(result, notion)

    def test_find_or_create_no_existing_notion(self):
        self.notion_repository.find.return_value = None
        created_notion = Mock(spec=Notion)
        created_notion.user_id = self.user_id
        created_notion.agent_id = self.agent_id
        created_notion.caption = self.expression.caption
        self.notion_repository.create.return_value = created_notion
        result = self.service.find_or_create(self.user_id, self.agent_id, self.expression)
        self.notion_repository.find.assert_called_once_with(
            self.agent_id,
            self.expression,
            None,
            similarity_threshold=1.0,
        )
        self.notion_repository.create.assert_called_once()
        self.assertEqual(result, created_notion)

    def test_find(self):
        notion = Mock(spec=Notion)
        self.notion_repository.find.return_value = notion
        result = self.service.find(self.agent_id, self.expression)
        self.notion_repository.find.assert_called_once_with(
            self.agent_id,
            self.expression,
            None,
            similarity_threshold=None,
        )
        self.assertEqual(result, notion)
    
    def test_find_returns_none_when_notion_not_found(self):
        self.notion_repository.find.return_value = None
        result = self.service.find(self.agent_id, self.expression)
        self.notion_repository.find.assert_called_once_with(
            self.agent_id,
            self.expression,
            None,
            similarity_threshold=None,
        )
        self.assertIsNone(result)

    def test_create_new_notion(self):
        created_notion = Mock(spec=Notion)
        created_notion.user_id = self.user_id
        created_notion.agent_id = self.agent_id
        created_notion.caption = self.expression.caption
        self.notion_repository.create.return_value = created_notion

        result = self.service.create(self.user_id, self.agent_id, self.expression)

        self.notion_repository.create.assert_called_once()
        self.assertEqual(result, created_notion)

    def test_create_allows_same_caption(self):
        created_notion = Mock(spec=Notion)
        created_notion.user_id = self.user_id
        created_notion.agent_id = self.agent_id
        created_notion.caption = self.expression.caption
        self.notion_repository.create.return_value = created_notion

        result = self.service.create(self.user_id, self.agent_id, self.expression)

        self.notion_repository.find.assert_not_called()
        self.notion_repository.create.assert_called_once()
        self.assertEqual(result, created_notion)

    def test_create_notifies_observers(self):
        observer = Mock()
        observer.on_notion_created = Mock()
        self.service.add_observer(observer)

        created_notion = Mock(spec=Notion)
        self.notion_repository.create.return_value = created_notion

        result = self.service.create(self.user_id, self.agent_id, self.expression)

        observer.on_notion_created.assert_called_once_with(created_notion)

    def test_find_or_create_notifies_observers_when_creating(self):
        observer = Mock()
        observer.on_notion_created = Mock()
        self.service.add_observer(observer)
        
        self.notion_repository.find.return_value = None
        created_notion = Mock(spec=Notion)
        self.notion_repository.create.return_value = created_notion
        
        result = self.service.find_or_create(self.user_id, self.agent_id, self.expression)
        
        observer.on_notion_created.assert_called_once_with(created_notion)
    
    def test_find_or_create_does_not_notify_when_finding_existing(self):
        observer = Mock()
        observer.on_notion_created = Mock()
        self.service.add_observer(observer)
        
        existing_notion = Mock(spec=Notion)
        existing_notion.caption = self.expression.caption
        self.notion_repository.find.return_value = existing_notion
        
        result = self.service.find_or_create(self.user_id, self.agent_id, self.expression)
        
        observer.on_notion_created.assert_not_called()

    def tearDown(self):
        if self._config_override:
            self._config_override.__exit__(None, None, None)


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