from AccessControl import Unauthorized
from plone import api
from plone.api.exc import InvalidParameterError
from ploneintranet import api as pi_api
from ploneintranet.workspace.tests.base import BaseTestCase
from zope.component import getMultiAdapter
from zope.event import notify
from zope.lifecycleevent import ObjectModifiedEvent


class TestPermissions(BaseTestCase):
    """
    Test the abilities of users within a workspace
    """

    def setUp(self):
        super().setUp()
        self.login_as_portal_owner()
        # set up some users
        pi_api.userprofile.create(username="wsadmin", email="admin@test.com")
        pi_api.userprofile.create(username="user1", email="test@test.com")
        pi_api.userprofile.create(username="user2", email="test2@test.com")
        self.portal.groups.intranet.members.update({"wsadmin", "user1", "user2"})
        notify(ObjectModifiedEvent(self.portal.groups.intranet))
        self.workspace = api.content.create(
            self.workspace_container,
            "ploneintranet.workspace.workspacefolder",
            "example-workspace",
        )
        self.add_user_to_workspace("wsadmin", self.workspace, {"Admins"})
        self.add_user_to_workspace("user1", self.workspace)
        self.logout()

    def traverse_to_item(self, item):
        """helper method to travers to an item by path"""
        return api.content.get(path="/".join(item.getPhysicalPath()))

    def test_consumers_can_view(self):
        """Consumers can only view published content in the workspace"""
        self.login_as_portal_owner()
        self.workspace.participant_policy = "consumers"
        doc_private = api.content.create(self.workspace, "Document", "test-page")
        doc_published = api.content.create(self.workspace, "Document", "test-page-2")
        api.content.transition(doc_published, to_state="published")

        # Should not be able to view this doc
        self.login("user1")
        with self.assertRaises(Unauthorized):
            self.traverse_to_item(doc_private)
        # Published doc is OK
        self.traverse_to_item(doc_published)

    def test_consumers_cannot_add(self):
        """Consumers cannot add content to the workspace"""
        self.login_as_portal_owner()
        self.workspace.participant_policy = "consumers"
        self.login("user1")
        with self.assertRaises(Unauthorized):
            api.content.create(self.workspace, "Document", "my-test-page")

    def test_consumers_cannot_add_after_change(self):
        """Consumers cannot add content to the workspace"""
        self.login_as_portal_owner()
        self.workspace.participant_policy = "producers"
        self.workspace.participant_policy = "consumers"
        self.login("user1")
        with self.assertRaises(Unauthorized):
            api.content.create(self.workspace, "Document", "my-test-page")

    def test_producers_can_add(self):
        """
        Producers can add content to the workspace, and
        submit it for review
        """
        self.login_as_portal_owner()
        self.workspace.participant_policy = "Producers"

        self.login("user1")
        doc = api.content.create(self.workspace, "Document", "my-test-page")
        api.content.transition(doc, transition="submit")
        # We cannot publish our own content
        with self.assertRaises(InvalidParameterError):
            api.content.transition(doc, transition="publish")

        # But an admin can
        self.login("wsadmin")
        api.content.transition(doc, transition="publish")

    def test_publishers_can_publish(self):
        """
        Publishers can add and publish their own content
        """
        self.login_as_portal_owner()
        admin_doc = api.content.create(self.workspace, "Document", "my-admin-page")
        self.workspace.participant_policy = "Publishers"

        self.login("user1")
        doc = api.content.create(self.workspace, "Document", "my-test-page")
        # Can publish my own content
        api.content.transition(doc, transition="submit")
        api.content.transition(doc, transition="publish")

        # We cannot view or publish someone else's content
        with self.assertRaises(Unauthorized):
            self.traverse_to_item(admin_doc)
        with self.assertRaises(InvalidParameterError):
            api.content.transition(admin_doc, transition="submit")

    def test_moderators_can_publish_all(self):
        """
        Moderators can add and publish any content
        """
        self.login_as_portal_owner()
        admin_doc = api.content.create(self.workspace, "Document", "my-admin-page")
        self.workspace.participant_policy = "Moderators"

        self.login("user1")
        doc = api.content.create(self.workspace, "Document", "my-test-page")
        # Can publish my own content
        api.content.transition(doc, transition="submit")
        api.content.transition(doc, transition="publish")

        # We can also view and publish someone else's document
        self.traverse_to_item(admin_doc)
        api.content.transition(admin_doc, transition="submit")
        api.content.transition(admin_doc, transition="publish")

    def test_creator_loses_ownership_as_non_member(self):
        """
        The creator of a WS, when removed as member, no longer has any
        Owenership or local Owner roles on any of its contents.
        """
        self.login("user1")
        my_workspace = api.content.create(
            self.workspace_container,
            "ploneintranet.workspace.workspacefolder",
            "user1-workspace",
        )
        self.assertEqual(api.content.get_state(my_workspace), "secret")
        self.add_user_to_workspace("wsadmin", my_workspace, {"Admins"})
        my_doc = api.content.create(my_workspace, "Document", "my-page")
        self.assertIn("Owner", api.user.get_roles(username="user1", obj=my_doc))
        self.assertEqual(my_workspace._owner, (["plone", "acl_users"], "user1"))
        self.logout()

        self.login("wsadmin")
        params = {"user_id": "user1", "batch-function": "remove"}
        request = self.layer["request"].clone()
        request.form.update(params)
        view = getMultiAdapter((my_workspace, request), name="sidebar.settings.members")

        view.execute_batch_function()
        self.assertEqual(my_doc.getOwnerTuple()[1], "wsadmin")
        self.assertNotIn("Owner", api.user.get_roles(username="user1", obj=my_doc))
        self.assertEqual(my_workspace._owner, (["plone", "acl_users"], "wsadmin"))

    def test_last_admin_stays_owner(self):
        """
        The creator of a WS, when removed as member, keeps Ownership if
        there is no other user to take their place.
        """
        self.login("user1")
        my_workspace = api.content.create(
            self.workspace_container,
            "ploneintranet.workspace.workspacefolder",
            "user1-workspace",
        )
        self.assertEqual(api.content.get_state(my_workspace), "secret")
        my_doc = api.content.create(my_workspace, "Document", "my-page")
        self.assertIn("Owner", api.user.get_roles(username="user1", obj=my_doc))
        self.assertEqual(my_workspace._owner, (["plone", "acl_users"], "user1"))

        params = {"user_id": "user1", "batch-function": "remove"}
        request = self.layer["request"].clone()
        request.form.update(params)
        view = getMultiAdapter((my_workspace, request), name="sidebar.settings.members")
        view.execute_batch_function()

        self.assertEqual(my_doc.getOwnerTuple()[1], "user1")
        # the local Owner role has been kept
        self.assertIn("Owner", api.user.get_roles(username="user1", obj=my_doc))
        # the Zope Owenership has been kept
        self.assertEqual(my_workspace._owner, (["plone", "acl_users"], "user1"))
