from io import BytesIO
from plone import api
from ploneintranet import api as pi_api
from ploneintranet.workspace.interfaces import IWorkspaceAppContentLayer
from ploneintranet.workspace.testing import PLONEINTRANET_WORKSPACE_INTEGRATION_TESTING
from ploneintranet.workspace.tests.base import BaseTestCase
from Products.statusmessages.interfaces import IStatusMessage
from unittest import mock
from z3c.relationfield.relation import RelationValue
from zope.component import getUtility
from zope.interface import alsoProvides
from zope.intid.interfaces import IIntIds
from ZPublisher.HTTPRequest import FileUpload


class TestEventForm(BaseTestCase):
    """Test the Event form"""

    layer = PLONEINTRANET_WORKSPACE_INTEGRATION_TESTING

    def _create_test_file_field(self, filename):
        return FileUpload(
            mock.Mock(
                filename=filename,
                file=BytesIO(b"test"),
                headers={},
                name="foo",
            )
        )

    def get_request(self):
        request = self.request.clone()
        alsoProvides(request, IWorkspaceAppContentLayer)
        return request

    def test_add_related_item(self):
        """Check that we can manipulate the related items"""
        ws = api.content.create(
            type="ploneintranet.workspace.workspacefolder",
            title="Demo",
            container=self.workspace_container,
        )
        event = api.content.create(type="Event", title="Party", container=ws)

        # We have no attachments
        self.assertListEqual(event.relatedItems, [])

        # Add a first attachment
        flyer = api.content.create(type="File", title="Flyer", container=self.portal)
        view = api.content.get_view("event_view", event, self.get_request())
        view.request.form["new-related-item"] = flyer.UID()
        view.update()

        self.assertListEqual(
            [relation.to_object.id for relation in event.relatedItems], ["flyer"]
        )
        self.assertListEqual(
            [item.message for item in IStatusMessage(view.request).show()],
            ["Attachment added"],
        )

        # Add another attachment
        view.request.form["new-related-item"] = self.portal.apps.UID()
        view.update()

        self.assertListEqual(
            [relation.to_object.id for relation in event.relatedItems],
            ["flyer", "apps"],
        )
        self.assertListEqual(
            [item.message for item in IStatusMessage(view.request).show()],
            ["Attachment added"],
        )

        # Now try to readd this attachment
        view.update()

        self.assertListEqual(
            [relation.to_object.id for relation in event.relatedItems],
            ["flyer", "apps"],
        )
        self.assertListEqual(
            [item.message for item in IStatusMessage(view.request).show()],
            ["Attachment already present"],
        )

        # Now try to add a non existent attachment
        view.request.form["new-related-item"] = "foobar"
        view.update()

        self.assertListEqual(
            [relation.to_object.id for relation in event.relatedItems],
            ["flyer", "apps"],
        )
        self.assertListEqual(
            [item.message for item in IStatusMessage(view.request).show()],
            ["Attachment cannot be added"],
        )

        # Now clear the request and test removal
        view.request = self.get_request()

        # Passing a wrong UID will not remove anything
        view.request.form["obsolete-related-item"] = "foobar"
        view.update()

        self.assertListEqual(
            [relation.to_object.id for relation in event.relatedItems],
            ["flyer", "apps"],
        )
        self.assertListEqual(
            [item.message for item in IStatusMessage(view.request).show()],
            ["No attachment was removed"],
        )

        # Passing the right UID will not remove the proper related item
        view.request.form["obsolete-related-item"] = flyer.UID()
        view.update()

        self.assertListEqual(
            [relation.to_object.id for relation in event.relatedItems], ["apps"]
        )
        self.assertListEqual(
            [item.message for item in IStatusMessage(view.request).show()],
            ["Attachment removed"],
        )

        # Repeating the call will not remove anything
        view.update()
        self.assertListEqual(
            [relation.to_object.id for relation in event.relatedItems], ["apps"]
        )
        self.assertListEqual(
            [item.message for item in IStatusMessage(view.request).show()],
            ["No attachment was removed"],
        )

    def test_image_edit(self):
        event = api.content.create(type="Event", title="Party", container=self.portal)
        view = api.content.get_view("event_view", event, self.get_request())
        self.assertIsNone(event.image)
        # We can add an image
        view.request.form["image"] = self._create_test_file_field("foo.jpg")
        view.update()
        self.assertEqual(event.image.filename, "foo.jpg")
        # We can change the image
        view.request.form["image"] = self._create_test_file_field("bar.jpg")
        view.update()
        self.assertEqual(event.image.filename, "bar.jpg")
        # We can remove the image
        view.request.form["image"] = None
        view.request.form["image.action"] = "remove"
        view.update()
        self.assertIsNone(event.image)

    def test_video_edit(self):
        event = api.content.create(type="Event", title="Party", container=self.portal)
        view = api.content.get_view("event_view", event, self.get_request())
        self.assertIsNone(event.video)
        # We can add an image
        view.request.form["video"] = self._create_test_file_field("foo.mp4")
        view.update()
        self.assertEqual(event.video.filename, "foo.mp4")
        # We can change the video
        view.request.form["video"] = self._create_test_file_field("bar.mp4")
        view.update()
        self.assertEqual(event.video.filename, "bar.mp4")
        # We can remove the video
        view.request.form["video"] = None
        view.request.form["video.action"] = "remove"
        view.update()
        self.assertIsNone(event.video)

    def test_spaces_edit(self):
        event = api.content.create(type="Event", title="Party", container=self.portal)
        view = api.content.get_view("event_view", event, self.get_request())
        self.assertListEqual(event.spaces, [])
        view.request.form["spaces"] = "foo"
        view.update()
        self.assertListEqual(event.spaces, ["foo"])
        view.request.form["spaces"] = ["bar"]
        view.update()
        self.assertListEqual(event.spaces, ["bar"])
        view.request.form["spaces"] = ["foo", "bar"]
        view.update()
        self.assertListEqual(event.spaces, ["foo", "bar"])
        view.request.form["spaces"] = ["bar", "foo"]
        view.update()
        self.assertListEqual(event.spaces, ["bar", "foo"])
        view.request.form["spaces"] = ["", "foo", "", "bar"]
        view.update()
        self.assertListEqual(event.spaces, ["foo", "bar"])
        view.request.form["spaces"] = ""
        view.update()
        self.assertListEqual(event.spaces, [])

    def test_participants_promo_voting_bar(self):
        event = api.content.create(type="Event", title="Party", container=self.portal)
        self.assertListEqual(event.participants_yes, [])
        self.assertListEqual(event.participants_maybe, [])
        self.assertListEqual(event.participants_no, [])

        view = api.content.get_view("promo-voting-bar", event, self.get_request())

        # Calling the view does nothing if we are not a PI user
        view()

        self.assertListEqual(event.participants_yes, [])
        self.assertListEqual(event.participants_maybe, [])
        self.assertListEqual(event.participants_no, [])

        view.request.__annotations__.clear()
        # option-1 => attend
        view.request.set("attending", "option-1")
        view()

        self.assertListEqual(event.participants_yes, [])
        self.assertListEqual(event.participants_maybe, [])
        self.assertListEqual(event.participants_no, [])

        # For a PI user the preference will be stored
        view.request.__annotations__.clear()

        foo = pi_api.userprofile.create("foo")
        with api.env.adopt_user("foo"):
            view()

        self.assertListEqual([x.to_object for x in event.participants_yes], [foo])
        self.assertListEqual(event.participants_maybe, [])
        self.assertListEqual(event.participants_no, [])

        # User can change is mind
        view.request.__annotations__.clear()
        # option-2 => maybe
        view.request.set("attending", "option-2")
        with api.env.adopt_user("foo"):
            view()

        self.assertListEqual(event.participants_yes, [])
        self.assertListEqual([x.to_object for x in event.participants_maybe], [foo])
        self.assertListEqual(event.participants_no, [])

        # also other user can jump in
        bar = pi_api.userprofile.create("bar")
        view.request.__annotations__.clear()

        with api.env.adopt_user("bar"):
            view()

        self.assertListEqual(event.participants_yes, [])
        self.assertListEqual(
            [x.to_object for x in event.participants_maybe], [foo, bar]
        )
        self.assertListEqual(event.participants_no, [])

        # and change their mind
        view.request.__annotations__.clear()
        # option-3 => no
        view.request.set("attending", "option-3")
        with api.env.adopt_user("bar"):
            view()
        self.assertListEqual(event.participants_yes, [])
        self.assertListEqual([x.to_object for x in event.participants_maybe], [foo])
        self.assertListEqual([x.to_object for x in event.participants_no], [bar])

    def test_participants_promo_rsvp_csv(self):
        user_foo = pi_api.userprofile.create(
            "foo", "foo@foo.org", properties=dict(fullname="F. Oéo")
        )
        user_bar = pi_api.userprofile.create(
            "bar", "bar@bar.net", properties=dict(fullname="Bart Bar")
        )
        user_baz = pi_api.userprofile.create(
            "baz", "baz@baz.com", properties=dict(fullname="Bas Boss")
        )
        intids = getUtility(IIntIds)
        foo = RelationValue(intids.getId(user_foo))
        bar = RelationValue(intids.getId(user_bar))
        baz = RelationValue(intids.getId(user_baz))

        event = api.content.create(type="Event", title="Party", container=self.portal)
        self.assertListEqual(event.participants_yes, [])
        self.assertListEqual(event.participants_maybe, [])
        self.assertListEqual(event.participants_no, [])

        view = api.content.get_view("rsvp.csv", event, self.get_request())
        csv = view()
        self.assertListEqual(
            csv.splitlines(), ['"username","email","fullname","yes","maybe","no"']
        )

        event.participants_yes = [foo]
        event.participants_maybe = [bar]
        event.participants_no = [baz]
        csv = view()
        self.assertListEqual(
            csv.splitlines(),
            [
                '"username","email","fullname","yes","maybe","no"',
                '"foo","foo@foo.org","F. Oéo","1","0","0"',
                '"bar","bar@bar.net","Bart Bar","0","1","0"',
                '"baz","baz@baz.com","Bas Boss","0","0","1"',
            ],
        )

        event.participants_yes = [bar, baz]
        event.participants_maybe = []
        event.participants_no = [foo]
        csv = view()
        self.assertListEqual(
            csv.splitlines(),
            [
                '"username","email","fullname","yes","maybe","no"',
                '"bar","bar@bar.net","Bart Bar","1","0","0"',
                '"baz","baz@baz.com","Bas Boss","1","0","0"',
                '"foo","foo@foo.org","F. Oéo","0","0","1"',
            ],
        )
