import unittest
import omni.client
import omni.kit.test
import omni.kit.app
import omni.kit.collaboration.channel_manager as cm

from omni.kit.collaboration.channel_manager.manager import _build_message_in_bytes
from .test_base import enable_server_tests


class TestChannelManager(omni.kit.test.AsyncTestCase):  # pragma: no cover
    BASE_URL = "omniverse://localhost/Projects/tests/omni.kit.collaboration.channel_manger/"

    # Before running each test
    async def setUp(self):
        self.app = omni.kit.app.get_app()
        self.current_message = None

    async def tearDown(self):
        pass

    async def _wait(self, frames=10):
        for i in range(frames):
            await self.app.next_update_async()

    async def test_join_invalid_omniverse_url(self):
        channel = await cm.join_channel_async("file://c/invalid_url.channel")
        self.assertFalse(channel)

        channel = await cm.join_channel_async("omniverse://invalid-server/invalid_url.channel")
        self.assertFalse(channel)

    async def test_peer_user_and_message_api(self):
        peer_user = cm.PeerUser("test_id", "test", "Create")
        self.assertEqual(peer_user.user_id, "test_id")
        self.assertEqual(peer_user.user_name, "test")
        self.assertEqual(peer_user.from_app, "Create")

        content = {"key" : "value"}
        message = cm.Message(peer_user, cm.MessageType.HELLO, content=content)
        self.assertEqual(message.from_user, peer_user)
        self.assertEqual(message.message_type, cm.MessageType.HELLO)
        self.assertEqual(message.content, content)

    @unittest.skipIf(not enable_server_tests(), "")
    async def test_api(self):
        test_url = self.BASE_URL + "test.channel"

        def subscriber(message: cm.Message):
            self.current_message = message

        channel = await cm.join_channel_async(test_url)
        handle = channel.add_subscriber(subscriber)

        self.assertTrue(channel is not None, f"Failed to connect channel.")
        self.assertTrue(channel.url is not None)
        self.assertTrue(channel.logged_user_name is not None)
        self.assertTrue(channel.logged_user_id is not None)
        self.assertTrue(channel.stopped is False)

        # Send empty message
        result = await channel.send_message_async({})
        self.assertTrue(result == omni.client.Result.OK)

        # Send more
        result = await channel.send_message_async({"test": "message_content"})
        self.assertTrue(result == omni.client.Result.OK)

        # Simulates multi-users
        user_id = 0
        for message_type in [cm.MessageType.JOIN, cm.MessageType.HELLO, cm.MessageType.LEFT]:
            self.current_message = None
            content = _build_message_in_bytes("test", message_type, {})
            channel._handler()._handle_message(omni.client.ChannelEvent.MESSAGE, str(user_id), content)
            self.assertTrue(self.current_message is not None)
            self.assertEqual(self.current_message.message_type, message_type)
            self.assertEqual(self.current_message.from_user.user_id, str(user_id))
            self.assertEqual(self.current_message.from_user.user_name, "test")

            # Don't increment user id for LEFT message as LEFT message will not be handled if user is not logged.
            if message_type == cm.MessageType.JOIN:
                user_id += 1

        # Simulates customized message
        self.current_message = None
        content = {"test_key": "content"}
        message = _build_message_in_bytes("test", cm.MessageType.MESSAGE, content)
        channel._handler()._handle_message(omni.client.ChannelEvent.MESSAGE, str(user_id), message)

        self.assertTrue(self.current_message)
        self.assertEqual(self.current_message.message_type, cm.MessageType.MESSAGE)
        self.assertEqual(self.current_message.content, content)

        # Channel's LEFT message will be treated as left too.
        self.current_message = None
        channel._handler()._handle_message(omni.client.ChannelEvent.LEFT, str(user_id), None)
        self.assertEqual(self.current_message.message_type, cm.MessageType.LEFT)

        handle.unsubscribe()

    @unittest.skipIf(not enable_server_tests(), "")
    async def test_synchronization(self):
        test_url = self.BASE_URL + "test.channel"
        for i in range(20):
            channel = await cm.join_channel_async(test_url)
            self.assertTrue(not not channel)
