from AccessControl import Unauthorized
from plone import api
from plone.app.testing import logout
from ploneintranet import api as pi_api
from ploneintranet.userprofile.exc import CannotDeleteGroup
from ploneintranet.userprofile.interfaces import INeverDeletedMemberGroup
from ploneintranet.userprofile.testing import (
    PLONEINTRANET_USERPROFILE_INTEGRATION_TESTING_DUMMY_CONTENT,
)
from unittest import TestCase
from zope.event import notify
from zope.interface import alsoProvides
from zope.interface import noLongerProvides
from zope.lifecycleevent import ObjectModifiedEvent


class TestWorkgroup(TestCase):

    layer = PLONEINTRANET_USERPROFILE_INTEGRATION_TESTING_DUMMY_CONTENT

    def setUp(self):
        super().setUp()
        self.portal = self.layer["portal"]
        self.request = self.layer["request"]
        self.group = pi_api.workgroup.get("Test group canonical name")

    def assertInGroup(self, username):
        self.assertIn(
            self.group.canonical,
            {x.getGroupId() for x in api.group.get_groups(username=username)},
        )

    def assertNotInGroup(self, username):
        self.assertNotIn(
            self.group.canonical,
            {x.getGroupId() for x in api.group.get_groups(username=username)},
        )

    def test_workgroup_is_a_group(self):
        """Basic test that checks that the workgroup is found as a group"""
        # Membrane is aware of it
        self.assertIn(
            "Test group canonical name",
            [g._id for g in self.portal.acl_users.membrane_groups.getGroups()],
        )
        # Also plone api (i.e. portal_groups.listGroups())
        self.assertIn(
            "Test group canonical name", [g.id for g in api.group.get_groups()]
        )
        # We can also get the group
        self.assertEqual(
            api.group.get("Test group canonical name").id, "Test group canonical name"
        )

    def test_workgroup_properties(self):
        """Test we can pick up the properties as expected"""
        group = api.group.get("Test group canonical name")
        self.assertEqual(group.getGroupTitleOrName(), "Test group")
        self.assertEqual(group.getProperty("email"), self.group.email)
        self.assertEqual(group.getProperty("description"), self.group.Description())
        self.assertEqual(group.getProperty("title"), self.group.Title())
        self.assertEqual(group.getProperty("canonical"), self.group.canonical)

    def test_initial_state_no_assignments(self):
        self.assertSetEqual(set(), self.group.members)
        self.assertNotInGroup("johndoe")
        self.assertNotInGroup("johndoe2")

    def test_workgroup_members(self):
        """Test we can add and remove members to workgroups."""
        self.assertNotInGroup("johndoe")
        self.assertNotInGroup("johndoe2")

        self.group.members = {"johndoe", "johndoe2"}
        notify(ObjectModifiedEvent(self.group))
        self.assertInGroup("johndoe")
        self.assertInGroup("johndoe2")

        self.group.members.remove("johndoe")
        notify(ObjectModifiedEvent(self.group))
        self.assertNotInGroup("johndoe")
        self.assertInGroup("johndoe2")

        self.group.members.add("johndoe")
        notify(ObjectModifiedEvent(self.group))
        self.assertInGroup("johndoe")
        self.assertInGroup("johndoe2")

    def test_workgroup_workflow(self):
        self.assertEqual(api.content.get_state(self.group), "enabled")
        self.assertEqual(
            self.portal.restrictedTraverse("groups/test-group"), self.group
        )
        logout()
        # Anonymous users cannot access groups
        with self.assertRaises(Unauthorized):
            self.portal.restrictedTraverse("groups/test-group")

    def test_never_deleted_group(self):
        alsoProvides(self.group, INeverDeletedMemberGroup)
        with self.assertRaises(CannotDeleteGroup):
            api.content.delete(self.group)
        self.assertIn("test-group", list(self.portal.groups.keys()))

        noLongerProvides(self.group, INeverDeletedMemberGroup)
        api.content.delete(self.group)
        self.assertNotIn("test-group", list(self.portal.groups.keys()))

    def test_workgroup_source(self):
        self.assertEqual(self.group.source, "plone")
        new = pi_api.workgroup.create(
            "Group with a custom source", properties={"source": "ldap"}
        )
        self.assertEqual(new.source, "ldap")

    def test_getGroupName(self):
        self.assertEqual(self.group.getGroupName(), "Test group")
