import unittest
from unittest.mock import Mock

import nio

from autorecorderbot.chat_functions import send_text_to_room
from autorecorderbot.callbacks import Callbacks
from autorecorderbot.storage import Storage

from tests.utils import make_awaitable, run_coroutine


class CallbacksTestCase(unittest.TestCase):
    def setUp(self) -> None:
        # Create a Callbacks object and give it some Mock'd objects to use
        self.fake_client = Mock(spec=nio.AsyncClient)
        self.fake_client.user = "@fake_user:example.com"

        self.fake_storage = Mock(spec=Storage)

        # We don't spec config, as it doesn't currently have well defined attributes
        self.fake_config = Mock()
        self.fake_config.sequence_model_path = "/home/lorenz/UKP/TexPrax/sequence_classification_model"
        self.fake_config.token_model_path = "/home/lorenz/UKP/TexPrax/token_classification_model"
        self.fake_config.command_prefix = "!"
        self.fake_config.user_id = "@fake_user:example.com"

        self.callbacks = Callbacks(
            self.fake_client, self.fake_storage, self.fake_config
        )

    def test_invite(self):
        """Tests the callback for InviteMemberEvents"""
        # Tests that the bot attempts to join a room after being invited to it

        # Create a fake room and invite event to call the 'invite' callback with
        fake_room = Mock(spec=nio.MatrixRoom)
        fake_room_id = "!abcdefg:example.com"
        fake_room.room_id = fake_room_id

        fake_invite_event = Mock(spec=nio.InviteMemberEvent)
        fake_invite_event.sender = "@some_other_fake_user:example.com"

        # Mock awaited methods
        self.fake_client.join.return_value = make_awaitable(None)
        self.fake_client.sync.return_value = make_awaitable(None)
        self.fake_client.room_send.return_value = make_awaitable(None)

        # Pretend that we received an invite event
        run_coroutine(self.callbacks.invite(fake_room, fake_invite_event))

        # Check that we attempted to join the room
        self.fake_client.join.assert_called_once_with(fake_room_id)

    def test_message(self):         
        # Create fake room
        fake_room = Mock(spec=nio.MatrixRoom)
        fake_room_id = "!abcdefg:example.com"
        fake_room.room_id = fake_room_id

        # Mock awaited methods
        self.fake_client.join.return_value = make_awaitable(None)
        self.fake_client.sync.return_value = make_awaitable(None)
        self.fake_client.room_send.return_value = make_awaitable(None)

        # Create fake message event
        fake_message_event = Mock(spec=nio.RoomMessageText)
        fake_message_event.body = "Fake Message"
        fake_message_event.sender = "@some_other_fake_user:example.com"
        fake_message_event.server_timestamp = 123456789

        # Pretend to send the fake message
        run_coroutine(self.callbacks.message(fake_room, fake_message_event))

        # Make sure that the bot responded
        self.fake_client.room_send.assert_called_once()

    def test_reaction(self):
        # Create fake room
        fake_room = Mock(spec=nio.MatrixRoom)
        fake_room_id = "!abcdefg:example.com"
        fake_room.room_id = fake_room_id

        # Mock awaited methods
        self.fake_storage.get_event_worked.return_value = False
        self.fake_client.join.return_value = make_awaitable(None)
        self.fake_client.sync.return_value = make_awaitable(None)
        self.fake_client.room_send.return_value = make_awaitable(None)
        get_event_response = nio.RoomGetEventResponse()
        get_event_response.event = nio.Event({"sender": "@fake_user:example.com", 
                                              "event_id": "another_fake_id",
                                              "origin_server_ts": 123456789})
        get_event_response.event.sender = "@fake_user:example.com"
        self.fake_client.room_get_event.return_value = make_awaitable(get_event_response)

        # Create fake reaction event
        fake_reaction_event = Mock(spec=nio.UnknownEvent)
        fake_reaction_event.sender = "@some_other_fake_user:example.com"
        fake_reaction_event.source = {"content": {"m.relates_to": {"key": "Problem"}}}

        fake_reacted_to_id = "fake_id"

        # Pretend to send the fake message
        run_coroutine(self.callbacks._reaction(fake_room, fake_reaction_event, fake_reacted_to_id))

        # Make sure the bot tries to store something to the teamboard
        self.fake_storage.store_new_event.assert_called_once()



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