from plone import api
from ploneintranet import api as pi_api
from ploneintranet.messaging.testing import FunctionalTestCase
from ploneintranet.messaging.testing import IntegrationTestCase
from time import sleep
from transaction import commit
from unittest import mock

import pika
import threading


class TestPushIntegration(IntegrationTestCase):
    def test_push_default_settings(self):
        """With default settings we send a push notification to update the counter"""
        with api.env.adopt_user("admin"):
            with mock.patch("ploneintranet.api.push._send") as mocked:
                pi_api.messaging.send_message("maryjane", "Hi Mary")
                # Push messages sent to "maryjane"
                self.assertListEqual(
                    [call.args for call in mocked.mock_calls],
                    [
                        ("maryjane", "incoming_message"),
                    ],
                )

    def test_push_settings_chat_messages_desktop_disabled(self):
        """When in the settings we do not select the desktop option, we send:
        - a push notification to update the counter
        """
        tool = api.portal.get_tool("ploneintranet_notifications")
        tool._notification_settings["maryjane"] = {"chat_messages": {"foo"}}

        with api.env.adopt_user("admin"):
            with mock.patch("ploneintranet.api.push._send") as mocked:
                pi_api.messaging.send_message("maryjane", "Hi Mary")
                # Push messages sent to "maryjane"
                self.assertListEqual(
                    [call.args for call in mocked.mock_calls],
                    [
                        ("maryjane", "incoming_message"),
                    ],
                )


class TestPushFunctional(FunctionalTestCase):
    def test_push_functional(self):
        connection = pika.BlockingConnection(
            pika.ConnectionParameters(host="localhost")
        )
        channel = connection.channel()
        exchange = api.portal.get_registry_record("ploneintranet.layout.push_exchange")
        channel.exchange_declare(exchange=exchange, exchange_type="topic", durable=True)
        result = channel.queue_declare("", exclusive=True)
        queue_name = result.method.queue
        channel.queue_bind(exchange=exchange, queue=queue_name, routing_key="maryjane")

        def callback(*args):
            callback.called_with_args = args
            channel = args[0]
            channel.stop_consuming()

        channel.basic_consume(
            queue=queue_name, on_message_callback=callback, auto_ack=True
        )

        thread = threading.Thread(target=channel.start_consuming, daemon=True)
        thread.start()

        pi_api.messaging.send_message("maryjane", "Hi Mary")

        self.assertRaises(AttributeError, getattr, callback, "called_with_args")
        # The push hook will happen when the transaction is committed
        commit()

        counter = 0
        while thread.is_alive():
            sleep(0.1)
            counter += 1
            self.assertLess(
                counter, 10, "It seems we did not received the message from rabbitmq"
            )

        self.assertEqual(callback.called_with_args[-1], b"incoming_message")
