from collections import defaultdict
from collective.workspace.interfaces import IWorkspace
from plone import api
from ploneintranet import api as pi_api
from ploneintranet.userprofile.tests.test_graph import BaseGraphTestCase
from ploneintranet.workspace.config import INTRANET_USERS_GROUP_ID
from ploneintranet.workspace.testing import PLONEINTRANET_WORKSPACE_INTEGRATION_TESTING
from zope.event import notify
from zope.lifecycleevent import ObjectModifiedEvent


class TestWorkspaceGraph(BaseGraphTestCase):

    layer = PLONEINTRANET_WORKSPACE_INTEGRATION_TESTING

    def _activate_quaive_group(self):
        """Activate the quaive.group behavior that adds a membrane group
        to the collective.workspace ones
        """
        pt = api.portal.get_tool("portal_types")
        fti = pt["ploneintranet.workspace.workspacefolder"]
        fti._updateProperty("behaviors", fti.behaviors[:] + ("quaive.group",))

    def test_pas_view_with_minimal_setup(self):
        pas_view = self.get_view("pas_view")
        self.init_graph()
        # 7 collective.workspace groups (7 roles)
        # 1 workgroup (All intranet users)
        nodes = pas_view.graph.nodes()
        self.assertEqual(len(nodes), 8)
        node_type_map = defaultdict(int)
        for node in nodes:
            node_type_map[node.partition(":")[0]] += 1

        self.assertEqual(node_type_map["collective.workspace"], 7)
        self.assertEqual(node_type_map["workgroup"], 1)

    def test_pas_view_with_quaive_group(self):
        pas_view = self.get_view("pas_view")
        self.init_graph()
        self._activate_quaive_group()
        # 8 collective.workspace groups (1 membrane + 7 roles)
        # 1 workgroup (All intranet users)
        nodes = pas_view.graph.nodes()
        self.assertEqual(len(nodes), 9)
        node_type_map = defaultdict(int)
        for node in nodes:
            node_type_map[node.partition(":")[0]] += 1

        self.assertEqual(node_type_map["collective.workspace"], 8)
        self.assertEqual(node_type_map["workgroup"], 1)

    def test_collective_workspace_pas_graph(self):
        obj = self.portal.workspaces["demo-workspace"]
        view = self.get_pas_graph(obj)
        objuid = obj.UID()
        objpath = "/plone/workspaces/demo-workspace"
        # We expect to have the 7 collective.workspace groups
        expected = {
            (
                f"collective.workspace:{key}:{objuid}",
                f"collective.workspace:{key}:{objuid}",
                f"collective.workspace:{key}:{objpath}",
            )
            for key in (
                "Admins",
                "Consumers",
                "Guests",
                "Members",
                "Moderators",
                "Producers",
                "Publishers",
            )
        }

        self.assertSetEqual(view.get_node_info(), expected)

    def test_collective_workspace_pas_graph_with_quaive_group(self):
        self._activate_quaive_group()
        obj = self.portal.workspaces["demo-workspace"]
        view = self.get_pas_graph(obj)
        objuid = obj.UID()
        objpath = "/plone/workspaces/demo-workspace"
        # We expect to have the 7 collective.workspace groups ...
        expected = {
            (
                f"collective.workspace:{key}:{objuid}",
                f"collective.workspace:{key}:{objuid}",
                f"collective.workspace:{key}:{objpath}",
            )
            for key in (
                "Admins",
                "Consumers",
                "Guests",
                "Members",
                "Moderators",
                "Producers",
                "Publishers",
            )
        }
        # ... plus the membrane one
        expected.add(
            (
                f"collective.workspace:{objuid}",
                "collective.workspace:demo-workspace",
                f"collective.workspace:{objpath}",
            )
        )
        self.assertSetEqual(view.get_node_info(), expected)

    def test_pas_view_graph_for_collective_workspaces(self):
        """The PAS graph understands collective.workspace groups"""
        obj = pi_api.userprofile.create("johndoe")

        pas_view = self.get_view("pas_view")
        useruid = pas_view.principalname2uid("johndoe")
        self.assertSetEqual(pas_view.ancestors(useruid), set())

        ws = self.portal.workspaces["demo-workspace"]
        adapter = IWorkspace(ws)
        adapter.add_to_team(obj.username)
        useruid = pas_view.principalname2uid("johndoe")
        pas_view.request = self.request.clone()
        self.assertSetEqual(
            pas_view.ancestors(useruid),
            {
                f"collective.workspace:Consumers:{ws.UID()}",
                f"collective.workspace:Members:{ws.UID()}",
            },
        )
        self.portal.groups.intranet.members.add("johndoe")
        notify(ObjectModifiedEvent(self.portal.groups.intranet))
        pas_view.request = self.request.clone()
        self.assertSetEqual(
            pas_view.ancestors(useruid),
            {
                f"collective.workspace:Consumers:{ws.UID()}",
                f"collective.workspace:Members:{ws.UID()}",
                f"workgroup:{self.portal.groups.intranet.UID()}",
            },
        )
        adapter.add_to_team(INTRANET_USERS_GROUP_ID, groups={"Admins"})
        pas_view.request = self.request.clone()
        self.assertSetEqual(
            pas_view.ancestors(useruid),
            {
                f"collective.workspace:Admins:{ws.UID()}",
                f"collective.workspace:Consumers:{ws.UID()}",
                f"collective.workspace:Members:{ws.UID()}",
                f"workgroup:{self.portal.groups.intranet.UID()}",
            },
        )

    def test_pas_view_graph_for_collective_workspaces_with_quaive_group(self):
        """The PAS graph understands collective.workspace groups
        If we activate the quaive.group the user is also member
        of the associated membrane group if he also happen
        to be in the collective.workspace Members group
        """
        self._activate_quaive_group()

        obj = pi_api.userprofile.create("johndoe")

        pas_view = self.get_view("pas_view")
        useruid = pas_view.principalname2uid("johndoe")
        self.assertSetEqual(pas_view.ancestors(useruid), set())

        ws = self.portal.workspaces["demo-workspace"]
        adapter = IWorkspace(ws)
        adapter.add_to_team(obj.username)
        useruid = pas_view.principalname2uid("johndoe")
        pas_view.request = self.request.clone()
        self.assertSetEqual(
            pas_view.ancestors(useruid),
            {
                f"collective.workspace:{ws.UID()}",
                f"collective.workspace:Consumers:{ws.UID()}",
                f"collective.workspace:Members:{ws.UID()}",
            },
        )
        self.portal.groups.intranet.members.add("johndoe")
        notify(ObjectModifiedEvent(self.portal.groups.intranet))
        pas_view.request = self.request.clone()
        self.assertSetEqual(
            pas_view.ancestors(useruid),
            {
                f"collective.workspace:{ws.UID()}",
                f"collective.workspace:Consumers:{ws.UID()}",
                f"collective.workspace:Members:{ws.UID()}",
                f"workgroup:{self.portal.groups.intranet.UID()}",
            },
        )
        adapter.add_to_team(INTRANET_USERS_GROUP_ID, groups={"Admins"})
        pas_view.request = self.request.clone()
        self.assertSetEqual(
            pas_view.ancestors(useruid),
            {
                f"collective.workspace:{ws.UID()}",
                f"collective.workspace:Admins:{ws.UID()}",
                f"collective.workspace:Consumers:{ws.UID()}",
                f"collective.workspace:Members:{ws.UID()}",
                f"workgroup:{self.portal.groups.intranet.UID()}",
            },
        )
