from datetime import timedelta
from plone import api
from plone.app.testing import helpers
from plone.app.testing import IntegrationTesting
from plone.app.testing import PloneSandboxLayer
from ploneintranet import api as pi_api
from ploneintranet.api.env import system_timezone_CET
from ploneintranet.messaging.testing import INTEGRATION_TESTING
from ploneintranet.notifications.sql.saconfig import LoggedEvent
from ploneintranet.notifications.sql.saconfig import Notification
from ploneintranet.notifications.sql.saconfig import session
from ploneintranet.notifications.testing import SQL_FIXTURE
from zope.i18nmessageid import Message

import unittest


class PloneintranetMessagingNotificationsLayer(PloneSandboxLayer):
    defaultBases = (SQL_FIXTURE, INTEGRATION_TESTING)

    def setUpZope(self, app, configurationContext):
        import ploneintranet.notifications.tests

        self.loadZCML(package=ploneintranet.notifications.tests)

    def setUpPloneSite(self, portal):
        helpers.applyProfile(portal, "ploneintranet.notifications.tests:testing")
        with api.env.adopt_user("admin"):
            pi_api.userprofile.create(
                "peterparker",
                "pp@example.com",
                properties={"first_name": "Peter", "last_name": "Parker"},
                approve=True,
            )
            pi_api.userprofile.create(
                "maryjane",
                "mj@example.com",
                properties={"first_name": "Mary", "last_name": "Jane"},
                approve=True,
            )


FIXTURE = PloneintranetMessagingNotificationsLayer()
LAYER = IntegrationTesting(
    bases=(FIXTURE,), name="PloneintranetMessagingNotificationsLayer:Integration"
)


class TestMessagingNotifications(unittest.TestCase):
    layer = LAYER

    def setUp(self):
        self.portal = self.layer["portal"]
        self.tool = api.portal.get_tool("ploneintranet_messaging")

    def assertUserReceivesNotificationsOnChannels(self, user_id, channels):
        """Check that the user has the right number of notifications"""
        notifications = session.query(Notification).filter_by(recipient_id=user_id)
        self.assertSetEqual(
            {notification.recipient_id for notification in notifications},
            {user_id},
        )
        self.assertEqual(notifications.count(), len(channels))
        self.assertSetEqual(
            {notification.channel for notification in notifications},
            channels,
        )

    def assertUserGetsNotificationText(self, user_id, text):
        """Check that the user gets the right notification text"""
        notifications = session.query(Notification).filter_by(recipient_id=user_id)
        notification = notifications.first()

        self.assertEqual(len(notification.logged_event.category_provider.categories), 1)
        category = notification.logged_event.category_provider.categories[0]
        observed = category.get_message_for(notification)
        if isinstance(observed, Message):
            observed = api.portal.translate(observed)
        return self.assertEqual(text, observed)

    @system_timezone_CET()
    def test_notification_events(self):
        """Test subscriber backoff"""
        with api.env.adopt_user("peterparker"):
            pi_api.messaging.send_message("maryjane", "Hi Mary")
            logged_event = session.query(LoggedEvent).one()
            self.assertEqual(logged_event.actor_id, "peterparker")

        logged_event.notify_all()
        self.assertUserReceivesNotificationsOnChannels("maryjane", {"desktop", "email"})

        self.assertUserGetsNotificationText(
            "maryjane", "Peter Parker sent you a chat message."
        )

        # Adding another message should not create another event
        with api.env.adopt_user("peterparker"):
            pi_api.messaging.send_message("maryjane", "Are you there?")
            new_logged_event = session.query(LoggedEvent).one()
            self.assertEqual(logged_event.uid, new_logged_event.uid)

        # Unless the message is sent by another user
        with api.env.adopt_user("maryjane"):
            pi_api.messaging.send_message("peterparker", "Leave me alone!")
            new_logged_event = (
                session.query(LoggedEvent)
                .filter(LoggedEvent.uid != logged_event.uid)
                .one()
            )
            self.assertEqual(new_logged_event.actor_id, "maryjane")

        # Or some time passes
        logged_event.time = logged_event.time - timedelta(minutes=20)
        with api.env.adopt_user("peterparker"):
            pi_api.messaging.send_message("maryjane", "You broke my heart!")
            new_logged_event = (
                session.query(LoggedEvent)
                .filter(
                    LoggedEvent.uid != logged_event.uid,
                    LoggedEvent.uid != new_logged_event.uid,
                )
                .one()
            )
            self.assertEqual(new_logged_event.actor_id, "peterparker")
