import unittest
from typing import List, Optional
from analogai.foundation.common.dtos.notion_expression import NotionExpression
from analogai.foundation.core.value_objects.relation import Relation
from analogai.foundation.tests.functional.services.setup import ServicesTestConfig


class TestNotionFunctional(unittest.TestCase):
    def setUp(self):
        self.config = ServicesTestConfig()
        self.notion_service = self.config.notion_service
        self.belief_service = self.config.belief_service
        self.user = self.config.user
        self.agent = self.config.agent

    def tearDown(self):
        self.config.tear_down()

    def _create_expression(self, caption: str, context: Optional[List[NotionExpression]] = None) -> NotionExpression:
        return NotionExpression(caption=caption, context=context or [])

    def test_get_beliefs_out(self):
        human = self.notion_service.find_or_create(
            self.user.id, self.agent.id, self._create_expression("human_rel_out"))
        mortal = self.notion_service.find_or_create(
            self.user.id, self.agent.id, self._create_expression("mortal_rel_out"))
        self.config.statement_service.create(
            user_id=self.user.id, agent_id=self.agent.id,
            subject_notion=human, complement_notion=mortal,
            relation=Relation.from_string("is"), probability=1.0, validity=1.0)
        out = self.belief_service.search_by_subject_notion_and_relation(
            human,
            Relation.from_string("is"),
        )
        self.assertEqual(len(out), 1)
        self.assertEqual(out[0].subject_notion.id, human.id)
        self.assertEqual(out[0].complement_notion.id, mortal.id)
        self.assertEqual(out[0].relation, Relation.from_string("is"))

    def test_get_beliefs_in(self):
        human = self.notion_service.find_or_create(
            self.user.id, self.agent.id, self._create_expression("human_rel_in"))
        mortal = self.notion_service.find_or_create(
            self.user.id, self.agent.id, self._create_expression("mortal_rel_in"))
        self.config.statement_service.create(
            user_id=self.user.id, agent_id=self.agent.id,
            subject_notion=human, complement_notion=mortal,
            relation=Relation.from_string("is"), probability=1.0, validity=1.0)
        inbound = self.belief_service.search_by_complement_notion_and_relation(
            mortal,
            Relation.from_string("is"),
        )
        self.assertEqual(len(inbound), 1)
        self.assertEqual(inbound[0].subject_notion.id, human.id)
        self.assertEqual(inbound[0].complement_notion.id, mortal.id)
        self.assertEqual(inbound[0].relation, Relation.from_string("is"))

    def test_get_beliefs_in_empty_when_no_beliefs(self):
        notion = self.notion_service.find_or_create(
            self.user.id, self.agent.id, self._create_expression("orphan_notion"))
        result = self.belief_service.search_by_complement_notion_and_relation(
            notion,
            Relation.from_string("is"),
        )
        self.assertEqual(result, [])

    def test_get_statements_in(self):
        company = self.notion_service.find_or_create(
            self.user.id, self.agent.id, self._create_expression("company_act"))
        sell = self.notion_service.find_or_create(
            self.user.id, self.agent.id, self._create_expression("sell"))
        iphone = self.notion_service.find_or_create(
            self.user.id, self.agent.id, self._create_expression("iphone_act"))
        self.config.statement_service.create(
            user_id=self.user.id, agent_id=self.agent.id,
            subject_notion=company, complement_notion=sell,
            relation=Relation.from_string("do"), probability=0.8, validity=0.9)
        self.config.statement_service.create(
            user_id=self.user.id, agent_id=self.agent.id,
            subject_notion=iphone, complement_notion=sell,
            relation=Relation.from_string("used for"), probability=0.8, validity=0.9)
        self.config.statement_service.create(
            user_id=self.user.id,
            agent_id=self.agent.id,
            subject_notion=company,
            relation=Relation.from_string("sell"),
            complement_notion=iphone,
            quantity=2.0,
        )
        inbound = self.config.statement_service.find_by_object_and_relation(iphone, Relation.from_string("sell"))
        self.assertEqual(len(inbound), 1)
        self.assertEqual(inbound[0].relation, Relation.from_string("sell"))
        self.assertEqual(inbound[0].complement_notion.id, iphone.id)
        self.assertEqual(inbound[0].subject_notion.id, company.id)
        self.assertEqual(inbound[0].quantity, 2.0)

    def test_get_statements_out(self):
        company = self.notion_service.find_or_create(
            self.user.id, self.agent.id, self._create_expression("company_act_out"))
        sell = self.notion_service.find_or_create(
            self.user.id, self.agent.id, self._create_expression("sell_out"))
        iphone = self.notion_service.find_or_create(
            self.user.id, self.agent.id, self._create_expression("iphone_act_out"))
        self.config.statement_service.create(
            user_id=self.user.id, agent_id=self.agent.id,
            subject_notion=company, complement_notion=sell,
            relation=Relation.from_string("do"), probability=0.8, validity=0.9)
        self.config.statement_service.create(
            user_id=self.user.id, agent_id=self.agent.id,
            subject_notion=iphone, complement_notion=sell,
            relation=Relation.from_string("used for"), probability=0.8, validity=0.9)
        self.config.statement_service.create(
            user_id=self.user.id,
            agent_id=self.agent.id,
            subject_notion=company,
            relation=Relation.from_string("sell_out"),
            complement_notion=iphone,
        )
        outbound = self.config.statement_service.find_by_subject_and_relation(company, Relation.from_string("sell_out"))
        self.assertEqual(len(outbound), 1)
        self.assertEqual(outbound[0].relation, Relation.from_string("sell_out"))
        self.assertEqual(outbound[0].subject_notion.id, company.id)
        self.assertEqual(outbound[0].complement_notion.id, iphone.id)


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