from plone import api
from ploneintranet.search.interfaces import ISiteSearch
from ploneintranet.workspace.tests.base import BaseTestCase
from ploneintranet.workspace.tests.base import WorkflowBaseTestCase
from zope.component import getUtility

VIEW = "View"
DELETE = "Delete objects"
MODIFY = "Modify portal content"

"""
This tests the workflow of a folder in a workspacefolder.
Workflow of the workspace itself is tested elsewhere
in test_workflow_workspace.
"""


class TestFolderWorkflow(BaseTestCase):
    def test_default_workflow(self):
        """
        the folder_in_workspace_workflow should be listed as a workflow
        """
        self.assertIn(
            "folder_in_workspace_workflow", self.portal_workflow.listWorkflows()
        )
        # Default workflow should be set
        self.assertTupleEqual(
            self.portal_workflow.getDefaultChain(), ("simple_publication_workflow",)
        )

    def test_default_workflow_for_folders(self):
        """Test that a folder created in the portal
        has the default workflow applied
        """
        self.login_as_portal_owner()
        wftool = self.portal_workflow

        # Check that a folder has the default workflow outside workspaces
        folder_in_portal = api.content.create(
            self.portal, "Folder", "test-folder-in-portal"
        )
        self.assertListEqual(
            ["simple_publication_workflow"],
            [x.getId() for x in wftool.getWorkflowsFor(folder_in_portal)],
        )

    def test_placeful_workflow(self):
        """
        The folder_in_workspace_workflow should be applied automatically to
        folders created inside a workspace
        """
        self.login_as_portal_owner()
        wftool = self.portal_workflow
        # A folder in the workspace should have
        # the folder_in_workspace_workflow
        workspace = api.content.create(
            self.workspace_container,
            "ploneintranet.workspace.workspacefolder",
            "example-workspace",
        )
        folder_in_workspace = api.content.create(
            workspace, "Folder", "test-folder-in-ws"
        )
        self.assertListEqual(
            ["folder_in_workspace_workflow"],
            [x.getId() for x in wftool.getWorkflowsFor(folder_in_workspace)],
        )


class TestSecretWorkspaceContentWorkflow(WorkflowBaseTestCase):
    """
    The name ContentWorkflow is a bit of a misnomer.
    This tests the workflow on a content folder in a workspacefolder.
    For content contained in a folder, see the sister test suite in
    test_workflow_workspace_content.py.

    However, here we also test the proper propagation view permissions
    after workflow state changes on the workspace itself,
    both for contained folders and for content within those folders.
    These changes are not so much acquired, as result from locally
    granting or revoking the Guest role to the group of all intranet users.
    """

    # properly set this ones to change workspacefolder behavior
    _participant_policy = "producers"

    # These are the general permission that are almost
    # common to every test case
    _base_permissions_by_userid = {
        "wsadmin": {VIEW, MODIFY, DELETE},
        "wsowner": {VIEW, MODIFY},
        "wsmember": {VIEW, MODIFY},
        "intranet": set(),
        "extranet": set(),
        None: set(),  # anonymous
    }
    # but your test case may override this
    _test_specific_permissions_by_userid = {}

    def setUp(self):
        super().setUp()
        self.folder_in_workspace = api.content.create(
            self.workspace_folder, "Folder", "owner-folder"
        )

        # Add a published document just for some solr view acquisition testing
        # You'll find full document tests in test_workflow_workspace_content
        self.document = api.content.create(
            self.folder_in_workspace, "Document", "document1"
        )

        self.login_as_portal_owner()
        api.content.transition(self.document, "submit")
        api.content.transition(self.document, "publish")

        # Finally change the workspace folder policy and (eventually) its state
        self.workspace_folder.participant_policy = self._participant_policy
        self.solr_commit()

    def test_setup_workflow_state(self):
        self.assertEqual(
            api.content.get_state(self.workspace_folder),
            "secret",
            "workspace is in incorrect state",
        )

    def get_expected_permissions_for(self, userid):
        """Get the permission for the given userid (None => anonymous)

        First look in _test_specific_permissions_by_userid then in
        _base_permissions_by_userid.
        """
        return self._test_specific_permissions_by_userid.get(
            userid, self._base_permissions_by_userid[userid]
        )

    def check_permissions_for(self, userid):
        """Verify object-level permissions."""
        permissions = api.user.get_permissions(
            username=userid, obj=self.folder_in_workspace
        )
        observed = {key for key in [VIEW, DELETE, MODIFY] if permissions[key]}
        expected = self.get_expected_permissions_for(userid)
        self.assertSetEqual(observed, expected)

    def check_solr_view_for(self, userid):
        """Verify SOLR view security index."""
        self.login(userid)
        path = ("/".join(self.workspace_folder.getPhysicalPath()),)
        search_util = getUtility(ISiteSearch)
        query = {"path": path}
        visible = [x.getObject() for x in search_util.query(filters=query)]
        self._check_visible(userid, visible)

    def check_zcatalog_view_for(self, userid):
        """Verify ZCatalog view security index."""
        self.login(userid)
        path = ("/".join(self.workspace_folder.getPhysicalPath()),)
        catalog = api.portal.get_tool("portal_catalog")
        visible = [x.getObject() for x in catalog.searchResults(path=path)]
        self._check_visible(userid, visible)

    def _check_visible(self, userid, visible):
        can_view_folder = self.folder_in_workspace in visible
        can_view_document = self.document in visible

        if VIEW in self.get_expected_permissions_for(userid):
            self.assertTrue(can_view_folder, f"{userid} should see folder")
            self.assertTrue(can_view_document, f"{userid} should see document")
        else:
            self.assertFalse(can_view_folder, f"{userid} should NOT see folder")
            self.assertFalse(can_view_document, f"{userid} should NOT see folder")

    def test_admin_permissions(self):
        self.check_permissions_for("wsadmin"),

    def test_owner_permissions(self):
        self.check_permissions_for("wsowner"),

    def test_member_permissions(self):
        self.check_permissions_for("wsmember"),

    def test_intranet_permissions(self):
        self.check_permissions_for("intranet"),

    def test_extranet_permissions(self):
        self.check_permissions_for("extranet"),

    def test_anonymous_permissions(self):
        self.logout()
        self.check_permissions_for(None),

    def test_member_solr(self):
        self.check_solr_view_for("wsmember")

    def test_member_zcatalog(self):
        self.check_zcatalog_view_for("wsmember")

    def test_intranet_solr(self):
        self.check_solr_view_for("intranet")

    def test_intranet_zcatalog(self):
        self.check_zcatalog_view_for("intranet")

    def test_extranet_solr(self):
        self.check_solr_view_for("extranet")

    def test_extranet_zcatalog(self):
        self.check_zcatalog_view_for("extranet")


class TestPrivateWorkspaceContentWorkflow(TestSecretWorkspaceContentWorkflow):
    """
    Content in a private workspace should have the same security
    View protections as content in a secret workspace.
    Inherit the test.
    """


class TestWorkspacePolicyConsumers(TestSecretWorkspaceContentWorkflow):
    """Test modify permissions for policy: consumers"""

    _participant_policy = "consumers"
    _test_specific_permissions_by_userid = {
        "owner": set(),
        "wsowner": {VIEW},
        "wsmember": {VIEW},
    }


class TestWorkspacePolicyProducers(TestSecretWorkspaceContentWorkflow):
    """Modify permission is identical to Consumers"""

    _participant_policy = "producers"


class TestWorkspacePolicyPublishers(TestSecretWorkspaceContentWorkflow):
    """Modify permission is identical to Consumers"""

    _participant_policy = "publishers"


class TestWorkspacePolicyModerators(TestSecretWorkspaceContentWorkflow):
    """
    Moderators allows teammembers to edit other people's content.
    """

    _participant_policy = "moderators"
    _test_specific_permissions_by_userid = {
        "wsowner": {VIEW, MODIFY, DELETE},
        "wsmember": {VIEW, MODIFY, DELETE},
    }
