from io import BytesIO
from plone import api
from plone.app.testing import login
from plone.app.testing import logout
from plone.app.testing import setRoles
from plone.app.testing import TEST_USER_ID
from plone.app.testing import TEST_USER_NAME
from ploneintranet.layout.interfaces import IPloneintranetLayoutLayer
from ploneintranet.layout.testing import IntegrationTestCase
from ploneintranet.testing import temporary_registry_record
from unittest import mock
from zope.interface import alsoProvides
from ZPublisher.HTTPRequest import FileUpload

import lxml


class TestTextEditor(IntegrationTestCase):
    def setUp(self):
        self.portal = self.layer["portal"]
        self.request = self.layer["request"].clone()
        alsoProvides(self.request, IPloneintranetLayoutLayer)

    def test_tiptap_shown_when_registry_set(self):
        doc = api.content.create(
            type="Document",
            container=self.portal,
            id="doc",
            title="doc",
        )
        with temporary_registry_record("ploneintranet.layout.texteditor", "tiptap"):
            # Editor
            traverser_editor = api.content.get_view("texteditor", doc, self.request)
            view_editor = traverser_editor.publishTraverse(self.request, "text")
            rendered_editor = view_editor()
            self.assertIn("pat-tiptap", rendered_editor)
            self.assertNotIn("redactor", rendered_editor)

            # Toolbar
            traverser_toolbar = api.content.get_view(
                "texteditor-toolbar", doc, self.request
            )
            view_toolbar = traverser_toolbar.publishTraverse(self.request, "text")
            rendered_toolbar = view_toolbar()
            self.assertIn("editor-toolbar tiptap", rendered_toolbar)
            self.assertIn("button-heading-level-1", rendered_toolbar)
            self.assertIn("button-italic", rendered_toolbar)
            self.assertIn("table-menu", rendered_toolbar)
            self.assertIn("button-image", rendered_toolbar)
            self.assertIn("button-embed", rendered_toolbar)
            self.assertIn("button-undo", rendered_toolbar)
            self.assertIn("button-redo", rendered_toolbar)
            self.assertNotIn("redactor", rendered_editor)

    def test_tiptap_mini_variant(self):
        doc = api.content.create(
            type="Document",
            container=self.portal,
            id="doc",
            title="doc",
        )
        with temporary_registry_record("ploneintranet.layout.texteditor", "tiptap"):
            # Editor
            traverser_editor = api.content.get_view(
                "texteditor-mini", doc, self.request
            )
            view_editor = traverser_editor.publishTraverse(self.request, "text")
            rendered_editor = view_editor()
            self.assertIn("pat-tiptap", rendered_editor)

            # Toolbar
            traverser_toolbar = api.content.get_view(
                "texteditor-toolbar-mini", doc, self.request
            )
            view_toolbar = traverser_toolbar.publishTraverse(self.request, "text")
            rendered_toolbar = view_toolbar()
            self.assertIn("editor-toolbar tiptap", rendered_toolbar)
            self.assertIn("button-heading-level-1", rendered_toolbar)
            self.assertNotIn("button-italic", rendered_toolbar)
            self.assertNotIn("table-menu", rendered_toolbar)
            self.assertNotIn("button-image", rendered_toolbar)
            self.assertNotIn("button-embed", rendered_toolbar)
            self.assertNotIn("button-undo", rendered_toolbar)
            self.assertNotIn("button-redo", rendered_toolbar)

    def test_redactor_shown_when_registry_set(self):
        doc = api.content.create(
            type="Document",
            container=self.portal,
            id="doc",
            title="doc",
        )
        with temporary_registry_record("ploneintranet.layout.texteditor", "redactor"):
            # Editor
            traverser_editor = api.content.get_view("texteditor", doc, self.request)
            view_editor = traverser_editor.publishTraverse(self.request, "text")
            rendered_editor = view_editor()
            self.assertIn("pat-redactor", rendered_editor)
            self.assertNotIn("tiptap", rendered_editor)

            # Toolbar
            traverser_toolbar = api.content.get_view(
                "texteditor-toolbar", doc, self.request
            )
            view_toolbar = traverser_toolbar.publishTraverse(self.request, "text")
            rendered_toolbar = view_toolbar()
            self.assertIn("editor-toolbar redactor", rendered_toolbar)
            self.assertNotIn("tiptap", rendered_editor)

    def test_get_set_text(self):
        from plone.app.textfield.value import RichTextValue

        doc = api.content.create(
            type="Document",
            container=self.portal,
            id="doc",
            title="doc",
        )
        with temporary_registry_record("ploneintranet.layout.texteditor", "tiptap"):
            doc.plaintext = "Zita Swoon"
            doc.richtext = RichTextValue("Moondog Jr.")
            self.request.form["request_text"] = "A Beatband"

            traverser = api.content.get_view("texteditor", doc, self.request)
            view = traverser.publishTraverse(self.request, "plaintext")
            self.assertEqual(view.get_text(), "Zita Swoon")

            traverser = api.content.get_view("texteditor", doc, self.request)
            view = traverser.publishTraverse(self.request, "richtext")
            self.assertEqual(view.get_text(), "Moondog Jr.")

            traverser = api.content.get_view("texteditor", doc, self.request)
            view = traverser.publishTraverse(self.request, "request_text")
            self.assertEqual(view.get_text(), "A Beatband")

            traverser = api.content.get_view("texteditor", doc, self.request)
            view = traverser.publishTraverse(self.request, "no_text")
            self.assertEqual(view.get_text(), "")

            traverser = api.content.get_view("texteditor", doc, self.request)
            view = traverser.publishTraverse(self.request, "plaintext")
            view.set_text("Zita Swoon Group")
            self.assertEqual(view.get_text(), "Zita Swoon Group")

    def test_get_set_placeholder(self):
        with temporary_registry_record("ploneintranet.layout.texteditor", "tiptap"):
            view = self.portal.restrictedTraverse("@@texteditor").publishTraverse(
                self.request, "text"
            )
            self.assertEqual(view.get_placeholder(), "texteditor_default_placeholder")

            view.set_placeholder("Nothing to say...")
            self.assertEqual(view.get_placeholder(), "Nothing to say...")

    def test_source_button(self):
        with temporary_registry_record("ploneintranet.layout.texteditor", "tiptap"):
            view = self.portal.restrictedTraverse("@@texteditor").publishTraverse(
                self.request, "text"
            )
            view_toolbar = self.portal.restrictedTraverse(
                "@@texteditor-toolbar"
            ).publishTraverse(self.request, "text")

            # We're logged in as TEST_USER_ID

            # Per default, show source button for Managers
            setRoles(self.portal, TEST_USER_ID, ["Manager"])
            self.assertEqual(view.show_source_button, True)
            self.assertIn("button-source", view_toolbar())

            # Per default, no source button for normal Members
            setRoles(self.portal, TEST_USER_ID, ["Member"])
            self.assertEqual(view.show_source_button, False)
            self.assertNotIn("button-source", view_toolbar())

            # Except if user is in registry
            with temporary_registry_record(
                "ploneintranet.layout.editor_html_allowed_users", (TEST_USER_NAME,)
            ):
                self.assertEqual(view.show_source_button, True)
                self.assertIn("button-source", view_toolbar())

    def test_tiptap_url_protocols(self):
        """Test that the tiptap editor supports custom URL protocol options."""

        doc = api.content.create(
            type="Document",
            container=self.portal,
            id="doc",
            title="doc",
        )
        with temporary_registry_record("ploneintranet.layout.texteditor", "tiptap"):
            with temporary_registry_record(
                "ploneintranet.layout.texteditor_url_protocols", ("https", "fantasy")
            ):
                # Editor
                traverser_editor = api.content.get_view("texteditor", doc, self.request)
                view_editor = traverser_editor.publishTraverse(self.request, "text")
                rendered_editor = view_editor()

                tree = lxml.etree.HTML(rendered_editor)
                editor_el = tree.cssselect("[data-pat-tiptap]")[0]
                editor_options = editor_el.attrib["data-pat-tiptap"]

                self.assertIn("link-extra-protocols: https, fantasy;", editor_options)


class TestTextEditorUpload(IntegrationTestCase):
    def setUp(self):
        self.portal = self.layer["portal"]
        self.request = self.layer["request"].clone()
        self.request.REQUEST_METHOD = "POST"
        alsoProvides(self.request, IPloneintranetLayoutLayer)
        self.folder = api.content.create(
            type="Folder",
            container=self.portal,
            id="upload",
            title="Upload",
        )

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

    def test_texteditor_upload_unauthorized(self):
        logout()
        self.request.form["file"] = self.create_file_upload("image1.jpg")
        image_upload = api.content.get_view(
            context=self.folder,
            request=self.request,
            name="panel-upload-image",
        )
        tree = lxml.etree.HTML(image_upload())

        self.assertFalse(
            tree.xpath(
                "//*[@id='image-panel-content']//input[contains(@value, 'image1.jpg')]"
            )
        )
        self.assertTrue(image_upload.error)
        self.assertNotIn("image1.jpg", self.folder)

        login(self.portal, TEST_USER_NAME)

    def test_texteditor_upload_success(self):
        with api.env.adopt_roles(["Contributor"]):
            image_upload = self.folder.restrictedTraverse("@@panel-upload-image")
            image_upload.request.REQUEST_METHOD = "POST"
            image_upload.request.form["file"] = self.create_file_upload("image1.jpg")
            alsoProvides(image_upload.request, IPloneintranetLayoutLayer)
            tree1 = lxml.etree.HTML(image_upload())
            self.assertIn("image1.jpg", self.folder)
            obj = self.folder.get("image1.jpg")
            self.assertTrue(
                tree1.xpath(
                    "//*[@id='image-panel-content']//input[contains(@value, '{}')]".format(  # noqa: E501
                        obj.absolute_url()
                    )
                )
            )

    def test_old_upload_cleared(self):
        self.request.form["file"] = self.create_file_upload("image1.jpg")
        image_upload = api.content.get_view(
            context=self.folder,
            request=self.request,
            name="panel-upload-image",
        )
        tree1 = lxml.etree.HTML(image_upload())
        self.assertTrue(
            tree1.xpath(
                "//*[@id='image-panel-content']//input[contains(@value, 'image1.jpg')]"
            )
        )

        self.request.form["file"] = self.create_file_upload("image2.jpg")
        image_upload = api.content.get_view(
            context=self.folder,
            request=self.request,
            name="panel-upload-image",
        )
        tree2 = lxml.etree.HTML(image_upload())
        self.assertTrue(
            tree2.xpath(
                "//*[@id='image-panel-content']//input[contains(@value, 'image2.jpg')]"
            )
        )
        self.assertFalse(
            tree2.xpath(
                "//*[@id='image-panel-content']//input[contains(@value, 'image1.jpg')]"
            ),
            msg="Old image found in upload panel",
        )
