from plone import api
from ploneintranet import api as pi_api
from ploneintranet.userprofile.testing import (
    PLONEINTRANET_USERPROFILE_INTEGRATION_TESTING,
)
from ploneintranet.userprofile.testing import (
    PLONEINTRANET_USERPROFILE_INTEGRATION_TESTING_DUMMY_CONTENT,
)
from unittest import TestCase
from zope.event import notify
from zope.lifecycleevent import ObjectModifiedEvent


class BaseGraphTestCase(TestCase):
    def setUp(self):
        super().setUp()
        self.portal = self.layer["portal"]
        self.request = self.layer["request"]

    def get_view(self, name, obj=None):
        return api.content.get_view(name, obj or self.portal, self.request.clone())

    def get_pas_view(self):
        return self.get_view("pas_view")

    def get_pas_graph(self, obj):
        return self.get_view("pas-graph", obj)

    def init_graph(self):
        view = self.get_view("pas-graph-init")
        view()

    def clear_graph(self):
        view = self.get_view("pas-graph-init")
        view.request.form["clear"] = "1"
        view()


class TestGraphNoContent(BaseGraphTestCase):

    layer = PLONEINTRANET_USERPROFILE_INTEGRATION_TESTING

    def test_pas_view_with_minimal_setup(self):
        pas_view = self.get_view("pas_view")
        self.init_graph()
        # 1 workgroup (intranet)
        self.assertEqual(len(pas_view.graph.nodes()), 1)
        self.assertEqual(len(pas_view.graph.edges()), 0)


class TestGraphDummyContent(BaseGraphTestCase):

    layer = PLONEINTRANET_USERPROFILE_INTEGRATION_TESTING_DUMMY_CONTENT

    def test_user_pas_graph(self):
        obj = self.portal.profiles["johndoe"]
        view = self.get_pas_graph(obj)
        self.assertSetEqual(
            view.get_node_info(),
            {
                (
                    f"userprofile:{obj.UID()}",
                    "userprofile:johndoe",
                    "userprofile:/plone/profiles/johndoe",
                )
            },
        )

    def test_workgroup_pas_graph(self):
        obj = self.portal.groups["intranet"]
        view = self.get_pas_graph(obj)
        self.assertSetEqual(
            view.get_node_info(),
            {
                (
                    f"workgroup:{obj.UID()}",
                    "workgroup:All Intranet Users",
                    "workgroup:/plone/groups/intranet",
                )
            },
        )

    def test_workgroup_pas_graph_with_canonical_name(self):
        obj = pi_api.workgroup.get("test-group")
        view = self.get_pas_graph(obj)
        self.assertSetEqual(
            view.get_node_info(),
            {
                (
                    f"workgroup:{obj.UID()}",
                    "workgroup:Test group canonical name",
                    "workgroup:/plone/groups/test-group",
                )
            },
        )

    def test_workgroup_pas_graph_with_unicode_name(self):
        obj_id = "test-unicode-group-degdeg"
        obj = pi_api.workgroup.get(obj_id)
        view = self.get_pas_graph(obj)
        self.assertSetEqual(
            view.get_node_info(),
            {
                (
                    f"workgroup:{obj.UID()}",
                    "workgroup:Test unicode group canonical name (°⌣°)",
                    f"workgroup:/plone/groups/{obj_id}",
                )
            },
        )

    def test_pas_graph_with_dummy_setup(self):
        pas_view = self.get_view("pas_view")
        self.init_graph()
        self.assertEqual(len(pas_view.graph.nodes()), 5)
        self.assertEqual(len(pas_view.graph.edges()), 2)

    def test_principalname2uid_with_unicode_encoded_name(self):
        pas_view = self.get_view("pas_view")
        self.init_graph()
        unicodeuid = pas_view.principalname2uid(
            "Test unicode group canonical name (°⌣°)".encode()
        )
        self.assertEqual(type(unicodeuid), str)

    def test_get_recursive_group_names(self):
        from ploneintranet.userprofile.pas.pi_userfactory import PIUser

        pas_view = self.get_view("pas_view")
        self.init_graph()

        # default situation: userid == login name
        johndoe = PIUser("johndoe")
        self.assertEqual("johndoe", johndoe.getUserId())
        self.assertEqual("johndoe", johndoe.getUserName())

        # old situation: get_recursive_group_names fed the actual principal
        # into principalname2uid.
        # new situation: use the userid as arg. Prove this is equivalent.
        self.assertNotEqual(pas_view.principalname2uid(johndoe), "")
        self.assertEqual(
            pas_view.principalname2uid(johndoe),
            pas_view.principalname2uid(johndoe.getUserId()),
        )

        # the actual test
        self.assertEqual(
            pas_view.get_recursive_group_names(johndoe), ("All Intranet Users",)
        )

        # this used to bug out. verify the fix.
        johndoe = PIUser("johndoe", "johnnyboy")
        self.assertEqual("johndoe", johndoe.getUserId())
        self.assertEqual("johnnyboy", johndoe.getUserName())

        self.assertEqual(
            pas_view.get_recursive_group_names(johndoe), ("All Intranet Users",)
        )

    def test_pas_view_ancestors(self):
        pas_view = self.get_view("pas_view")
        self.init_graph()
        useruid = pas_view.principalname2uid("johndoe")
        intranetuid = pas_view.principalname2uid("All Intranet Users")
        self.assertSetEqual(pas_view.predecessors(useruid), {intranetuid})
        self.assertSetEqual(pas_view.ancestors(useruid), {intranetuid})

        # Now we create a nested structure
        children_group = pi_api.workgroup.create("children-group", members={"johndoe"})
        parent_group = self.portal.groups["test-group"]
        parent_group.members.add(children_group.getGroupId())
        notify(ObjectModifiedEvent(parent_group))
        # and refresh the graph
        self.init_graph()
        pas_view.request = self.request.clone()

        parentuid = pas_view.principalname2uid(parent_group.getGroupId())
        childrenuid = pas_view.principalname2uid(children_group.getGroupId())
        self.assertSetEqual(pas_view.predecessors(useruid), {childrenuid, intranetuid})
        self.assertSetEqual(
            pas_view.ancestors(useruid), {childrenuid, intranetuid, parentuid}
        )

    def test_pas_view_descendants(self):
        pas_view = self.get_view("pas_view")
        self.init_graph()
        useruid = pas_view.principalname2uid("johndoe")
        useruid2 = pas_view.principalname2uid("johndoe2")
        intranetuid = pas_view.principalname2uid("All Intranet Users")
        self.assertSetEqual(pas_view.successors(intranetuid), {useruid, useruid2})
        self.assertSetEqual(pas_view.descendants(intranetuid), {useruid, useruid2})

        # Now we create a nested structure
        children_group = pi_api.workgroup.create("children-group", members={"johndoe"})
        parent_group = self.portal.groups["test-group"]
        parent_group.members.add(children_group.getGroupId())
        notify(ObjectModifiedEvent(parent_group))
        # and refresh the graph
        self.init_graph()
        pas_view.request.__annotations__.clear()
        parentuid = pas_view.principalname2uid(parent_group.getGroupId())
        childrenuid = pas_view.principalname2uid(children_group.getGroupId())
        self.assertSetEqual(pas_view.successors(parentuid), {childrenuid})
        self.assertSetEqual(pas_view.successors(childrenuid), {useruid})
        self.assertSetEqual(
            pas_view.successors_for_uids([childrenuid, parentuid]),
            {childrenuid, useruid},
        )
        self.assertSetEqual(
            pas_view.successors_for_uids([intranetuid, parentuid]),
            {childrenuid, useruid, useruid2},
        )
        self.assertEqual(pas_view.descendants(parentuid), {childrenuid, useruid})
