from plone import api
from plone.dexterity.browser.view import DefaultView
from ploneintranet import api as pi_api
from ploneintranet.layout.interfaces import INoBarcelonetaLayer
from ploneintranet.userprofile.browser.userprofilecontainer import View
from ploneintranet.userprofile.interfaces import IMemberGroup
from ploneintranet.userprofile.interfaces import IMemberGroupMembershipChangedEvent
from ploneintranet.userprofile.tests.base import BaseTestCase
from unittest.mock import Mock
from zope.component import provideHandler
from zope.interface import noLongerProvides

TEST_AVATAR_FILENAME = "test_avatar.jpg"


class TestViews(BaseTestCase):
    def setUp(self):
        super().setUp()
        self.login_as_portal_owner()

    def test_profiles_view(self):
        """Check the default view for the profile container redirects the user
        when we are using Quaive, but not when we are on the CMS."""
        view = api.content.get_view("view", self.profiles, self.request.clone())
        self.assertIsInstance(view, View)

        # We redirect the user to the Plone site
        self.assertEqual(view(), "http://nohost/plone/@@dashboard.html")

        # Now we fake a request from the CMS
        request = self.request.clone()
        noLongerProvides(request, INoBarcelonetaLayer)
        view = api.content.get_view("view", self.profiles, request)
        # When this happens the dexterity default view is used
        self.assertIsInstance(view, DefaultView)

    def test_edit_workgroup_update_local_roles(self):
        group = self.portal.groups.intranet
        user = pi_api.userprofile.create("johndoe")
        view = api.content.get_view("edit", group, self.request.clone())
        # Initially we have no maintainer
        self.assertListEqual(view.maintainers, [])
        self.assertSetEqual(
            set(api.user.get_roles(user=user, obj=group)), {"Authenticated"}
        )
        # But we can edit it
        view.request.method = "POST"
        view.request.form["maintainers"] = "johndoe"
        view.request.form["submit"] = "1"
        self.assertEqual(view(), view.context.absolute_url())
        # Now we see we have a maintainer
        view.request = self.request.clone()
        self.assertListEqual(view.maintainers, ["johndoe"])
        self.assertSetEqual(
            set(api.user.get_roles(user=user, obj=group)), {"Authenticated", "Editor"}
        )
        # But we can also remove hime
        view.request.method = "POST"
        view.request.form["submit"] = "1"
        self.assertEqual(view(), view.context.absolute_url())
        view.request = self.request.clone()
        self.assertListEqual(view.maintainers, [])
        self.assertSetEqual(
            set(api.user.get_roles(user=user, obj=group)), {"Authenticated"}
        )
        # Of course permissions are inherited
        view.request.method = "POST"
        view.request.form["maintainers"] = "AuthenticatedUsers"
        view.request.form["submit"] = "1"
        view()
        view.request = self.request.clone()
        self.assertListEqual(view.maintainers, ["AuthenticatedUsers"])
        self.assertSetEqual(
            set(api.user.get_roles(user=user, obj=group)), {"Authenticated", "Editor"}
        )


class TestBreadcrumbs(BaseTestCase):
    def test_user_name_in_profile_view(self):
        user = pi_api.userprofile.create("janedoe", properties={"fullname": "Jane Doe"})
        view = api.content.get_view(
            context=user, request=self.request, name="breadcrumbs_view"
        )
        self.assertEqual(
            view.breadcrumbs(),
            ({"absolute_url": user.absolute_url(), "Title": user.Title()},),
        )


class TestWorkgroupView(BaseTestCase):
    def setUp(self):
        super().setUp()
        self.login_as_portal_owner()
        pi_api.userprofile.create(
            "johndoe",
            email="john@doe.com",
            properties={
                "first_name": "John",
                "last_name": "Doe",
                "department": "Sömé Dèpartmènt",
            },
        )
        pi_api.userprofile.create(
            "maryjane",
            email="mary@jane.net",
            properties={
                "first_name": "Mary",
                "last_name": "Jane",
                "department": "Öthër Dèpartmènt",
            },
        )
        captainobvious = pi_api.userprofile.create(
            "captainobvious",
            email="captain@obvious.net",
            properties={
                "first_name": "Charles",
                "last_name": "Obvious",
                "department": "Öthër Dèpartmènt",
            },
        )
        api.content.transition(obj=captainobvious, to_state="disabled")
        pi_api.workgroup.create(
            canonical="testgroup",
            title="testgroup",
            description="",
            email="testgroup@example.com",
        )

    def test_edit_workgroup_membership_event(self):
        subscriber = Mock()
        provideHandler(subscriber, [IMemberGroup, IMemberGroupMembershipChangedEvent])
        group = self.portal.groups.intranet

        view = api.content.get_view("edit", group, self.request.clone())
        view.request.method = "POST"
        view.request.form["members"] = "johndoe"
        view.request.form["submit"] = "1"
        view()
        subscriber.assert_called_once()
        self.assertEqual(subscriber.mock_calls[0].args[0], group)
        self.assertSetEqual(subscriber.mock_calls[0].args[1].old_members, set())
        self.assertSetEqual(subscriber.mock_calls[0].args[1].new_members, {"johndoe"})

        subscriber.reset_mock()
        view.request.__annotations__.clear()
        view.request.form["members"] = "johndoe,maryjane"
        view()
        subscriber.assert_called_once()
        self.assertEqual(subscriber.mock_calls[0].args[0], group)
        self.assertSetEqual(subscriber.mock_calls[0].args[1].old_members, {"johndoe"})
        self.assertSetEqual(
            subscriber.mock_calls[0].args[1].new_members, {"johndoe", "maryjane"}
        )

        subscriber.reset_mock()
        view.request.__annotations__.clear()
        view.request.form["members"] = "johndoe,maryjane,testgroup"
        view()
        subscriber.assert_called_once()
        self.assertEqual(subscriber.mock_calls[0].args[0], group)
        self.assertSetEqual(
            subscriber.mock_calls[0].args[1].old_members, {"johndoe", "maryjane"}
        )
        self.assertSetEqual(
            subscriber.mock_calls[0].args[1].new_members,
            {"johndoe", "maryjane", "testgroup"},
        )

        subscriber.reset_mock()
        view.request.__annotations__.clear()
        view.request.form["members"] = "maryjane"
        view()
        subscriber.assert_called_once()
        self.assertEqual(subscriber.mock_calls[0].args[0], group)
        self.assertSetEqual(
            subscriber.mock_calls[0].args[1].old_members,
            {"johndoe", "maryjane", "testgroup"},
        )
        self.assertSetEqual(subscriber.mock_calls[0].args[1].new_members, {"maryjane"})

    def test_workgroup_membership_report__availability(self):
        from AccessControl.unauthorized import Unauthorized

        group = self.portal.groups.testgroup
        group.members = {
            "johndoe",
            "captainobvious",
        }
        api.group.grant_roles("maryjane", obj=group, roles=["Editor"])

        with api.env.adopt_user("maryjane"):
            group_view = api.content.get_view("view", group, self.request.clone())

            # The show_more_menu is shown for group admins on the group view.
            self.assertTrue(group_view.show_more_menu)

            out_xlsx = group.restrictedTraverse("workgroup-membership-report.xlsx")
            self.assertIsNotNone(out_xlsx)

            out_csv = group.restrictedTraverse("workgroup-membership-report.csv")
            self.assertIsNotNone(out_csv)

        with api.env.adopt_user("johndoe"):
            group_view = api.content.get_view("view", group, self.request.clone())

            # The show_more_menu is not shown for normal members on the group view.
            self.assertFalse(group_view.show_more_menu)

            with self.assertRaises(Unauthorized):
                group.restrictedTraverse("workgroup-membership-report.xlsx")

            with self.assertRaises(Unauthorized):
                group.restrictedTraverse("workgroup-membership-report.csv")

    def test_workgroup_membership_report__xlsx(self):
        from io import BytesIO
        from openpyxl import load_workbook

        group = self.portal.groups.testgroup
        group.members = {
            "johndoe",
            "captainobvious",
        }
        api.group.grant_roles("maryjane", obj=group, roles=["Editor"])

        with api.env.adopt_user("maryjane"):
            xlsx_view = api.content.get_view(
                "workgroup-membership-report.xlsx", group, self.request.clone()
            )
            out = xlsx_view()

            # Test response headers
            response = xlsx_view.request.response
            self.assertEqual(response.status, 200)
            self.assertEqual(
                response.getHeader("Content-Type"),
                "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
            )
            self.assertEqual(
                response.getHeader("Content-Disposition"),
                "attachment; filename=workgroup-testgroup-membership-report.xlsx",
            )

            xlsx_file = BytesIO(out)

            wb = load_workbook(filename=xlsx_file)
            sheet = wb.worksheets[0]
            rows = sheet.rows

            # header
            row_1 = next(rows)
            self.assertEqual(row_1[0].value, "Workgroup")
            self.assertEqual(row_1[1].value, "First Name")
            self.assertEqual(row_1[2].value, "Last Name")
            self.assertEqual(row_1[3].value, "Username")
            self.assertEqual(row_1[4].value, "Email")
            self.assertEqual(row_1[5].value, "Source")
            self.assertEqual(row_1[6].value, "Department")
            self.assertEqual(row_1[7].value, "Role in Group")

            # first row (output is sorted)
            row_2 = next(rows)
            self.assertEqual(row_2[0].value, "testgroup")
            self.assertEqual(row_2[1].value, "John")
            self.assertEqual(row_2[2].value, "Doe")
            self.assertEqual(row_2[3].value, "johndoe")
            self.assertEqual(row_2[4].value, "john@doe.com")
            self.assertEqual(row_2[5].value, "plone")
            self.assertEqual(row_2[6].value, "Sömé Dèpartmènt")
            self.assertEqual(row_2[7].value, "Group member")

            # first row (output is sorted)
            row_3 = next(rows)
            self.assertEqual(row_3[0].value, "testgroup")
            self.assertEqual(row_3[1].value, "Mary")
            self.assertEqual(row_3[2].value, "Jane")
            self.assertEqual(row_3[3].value, "maryjane")
            self.assertEqual(row_3[4].value, "mary@jane.net")
            self.assertEqual(row_3[5].value, "plone")
            self.assertEqual(row_3[6].value, "Öthër Dèpartmènt")
            self.assertEqual(row_3[7].value, "Group administrator")

            self.assertRaises(StopIteration, next, rows)

    def test_workgroup_membership_report__csv(self):
        from io import StringIO

        import csv

        group = self.portal.groups.testgroup
        group.members = {
            "johndoe",
            "captainobvious",
        }
        api.group.grant_roles("maryjane", obj=group, roles=["Editor"])

        with api.env.adopt_user("maryjane"):
            csv_view = api.content.get_view(
                "workgroup-membership-report.csv", group, self.request.clone()
            )
            out = csv_view()

            # Test response headers
            response = csv_view.request.response
            self.assertEqual(response.status, 200)
            self.assertEqual(
                response.getHeader("Content-Type"), "text/csv; charset=utf-8"
            )
            self.assertEqual(
                response.getHeader("Content-Disposition"),
                "attachment; filename=workgroup-testgroup-membership-report.csv",
            )

            csv_file = StringIO(out)
            csv_reader = csv.reader(csv_file)

            # header
            self.assertEqual(
                next(csv_reader),
                [
                    "workgroup",
                    "first_name",
                    "last_name",
                    "username",
                    "email",
                    "source",
                    "department",
                    "role_in_group",
                ],
            )

            # first row (output is sorted)
            self.assertEqual(
                next(csv_reader),
                [
                    "testgroup",
                    "John",
                    "Doe",
                    "johndoe",
                    "john@doe.com",
                    "plone",
                    "Sömé Dèpartmènt",
                    "Group member",
                ],
            )

            # second row (output is sorted)
            self.assertEqual(
                next(csv_reader),
                [
                    "testgroup",
                    "Mary",
                    "Jane",
                    "maryjane",
                    "mary@jane.net",
                    "plone",
                    "Öthër Dèpartmènt",
                    "Group administrator",
                ],
            )
            self.assertRaises(StopIteration, next, csv_reader)
