from BTrees.OOBTree import OOBTree
from plone import api
from plone.app.testing import helpers
from plone.app.testing import IntegrationTesting
from plone.app.testing import PloneSandboxLayer
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 ploneintranet.notifications.tests import NotificationTestCase
from ploneintranet.workspace.testing import PLONEINTRANET_WORKSPACE_WORKFLOW_FIXTURE


class PloneintranetWorkspaceNotificationsLayer(PloneSandboxLayer):
    defaultBases = (SQL_FIXTURE, PLONEINTRANET_WORKSPACE_WORKFLOW_FIXTURE)

    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")


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


class TestWorkspaceNotification(NotificationTestCase):
    layer = LAYER

    def setUp(self):
        super().setUp()
        self.portal = self.layer["portal"]
        self.request = self.layer["request"]
        self.workspace_folder = self.portal.workspaces["demo-workspace"]

    def test_notifications_sent_for_members(self):
        with api.env.adopt_user("wsadmin"):
            obj = api.content.create(
                title="Document 1", type="Document", container=self.workspace_folder
            )
            self.assertEqual(session.query(LoggedEvent).count(), 1)
            logged_event = session.query(LoggedEvent).one()
            self.assertEqual(logged_event.actor_id, "wsadmin")

        logged_event.notify_all()
        self.assertEqual(session.query(Notification).count(), 0)
        session.query(LoggedEvent).delete()

        # When an object is published, all members receive a notification
        with api.env.adopt_user("wsadmin"):
            api.content.transition(obj, to_state="published")
            logged_event = session.query(LoggedEvent).one()

        logged_event.notify_all()
        self.assertEqual(session.query(Notification).count(), 4)
        self.assertUserReceivesNotificationsOnChannels(
            "wsmember", {"desktop", "record"}, "workspace_member"
        )
        self.assertUserReceivesNotificationsOnChannels(
            "wsowner", {"desktop", "record"}, "workspace_member"
        )
        self.assertInUserNotification(
            "wsmember",
            "wsadmin published the document “Document 1” in Demo Workspace",
        )
        self.assertInUserNotification(
            "wsowner",
            "wsadmin published the document “Document 1” in Demo Workspace",
        )

    def test_notifications_sent_for_open_workspace(self):
        with api.env.adopt_user("wsadmin"):
            self.workspace_folder.accessible_to_intranet_users = True
            obj = api.content.create(
                title="Document 1", type="Document", container=self.workspace_folder
            )
            self.assertEqual(session.query(LoggedEvent).count(), 1)
            logged_event = session.query(LoggedEvent).one()
            self.assertEqual(logged_event.actor_id, "wsadmin")

        logged_event.notify_all()
        self.assertEqual(session.query(Notification).count(), 0)
        session.query(LoggedEvent).delete()

        # When an object is published, all members receive a notification
        with api.env.adopt_user("wsadmin"):
            api.content.transition(obj, to_state="published")
            logged_event = session.query(LoggedEvent).one()

        logged_event.notify_all()
        self.assertEqual(session.query(Notification).count(), 6)

        self.assertUserReceivesNotificationsOnChannels(
            "wsmember", {"desktop", "record"}, "workspace_member"
        )
        self.assertUserReceivesNotificationsOnChannels(
            "wsowner", {"desktop", "record"}, "workspace_member"
        )
        self.assertUserReceivesNotificationsOnChannels(
            "intranet", {"record"}, "workspace_open"
        )
        self.assertUserReceivesNotificationsOnChannels(
            "wseditor", {"record"}, "workspace_open"
        )
        self.assertInUserNotification(
            "wsmember",
            "wsadmin published the document “Document 1” in Demo Workspace",
        )
        self.assertInUserNotification(
            "wsowner",
            "wsadmin published the document “Document 1” in Demo Workspace",
        )
        self.assertInUserNotification(
            "intranet",
            "wsadmin published the document “Document 1” in Demo Workspace",
        )
        self.assertInUserNotification(
            "wseditor",
            "wsadmin published the document “Document 1” in Demo Workspace",
        )

    def test_notifications_sent_for_followers(self):
        # First, suppress the member notifications
        notifications_tool = api.portal.get_tool("ploneintranet_notifications")
        notification_settings = notifications_tool._notification_settings
        for member_id in {"wsmember", "wsowner"}:
            notification_settings[member_id] = OOBTree()
            notification_settings[member_id]["workspace_member"] = set()

        # Let's make wsmember follow our demo workspace
        ploneintranet_network = api.portal.get_tool("ploneintranet_network")
        with api.env.adopt_user("wsmember"):
            ploneintranet_network.watch("workspace", self.workspace_folder.UID())

        # When an object is created he will get no notifications
        # (he cannot view it yet)
        with api.env.adopt_user("wsadmin"):
            obj = api.content.create(
                title="Document 1", type="Document", container=self.workspace_folder
            )
            self.assertEqual(session.query(LoggedEvent).count(), 1)
            logged_event = session.query(LoggedEvent).one()
            self.assertEqual(logged_event.actor_id, "wsadmin")

        logged_event.notify_all()
        self.assertEqual(session.query(Notification).count(), 0)
        session.query(LoggedEvent).delete()

        # When an object is published he will get a notification
        with api.env.adopt_user("wsadmin"):
            api.content.transition(obj, to_state="published")
            logged_event = session.query(LoggedEvent).one()

        logged_event.notify_all()
        self.assertEqual(session.query(Notification).count(), 3)

        # The record channel cannot be switched off
        self.assertUserReceivesNotificationsOnChannels(
            "wsowner", {"record"}, "workspace_member"
        )
        self.assertUserReceivesNotificationsOnChannels(
            "wsmember", {"desktop", "record"}, "following"
        )
        self.assertInUserNotification(
            "wsmember",
            "wsadmin published the document “Document 1” in Demo Workspace",
        )
