"""Move to ploneintranet.theme asap"""

from plone import api
from plone.app.contenttypes.behaviors.richtext import IRichTextBehavior
from plone.app.contenttypes.interfaces import IPloneAppContenttypesLayer
from plone.app.dexterity.behaviors.metadata import IBasic
from plone.app.textfield.value import RichTextValue
from ploneintranet import api as pi_api
from ploneintranet.layout.interfaces import INoBarcelonetaLayer
from ploneintranet.layout.interfaces import IPloneintranetContentLayer
from ploneintranet.layout.interfaces import IPloneintranetLayoutLayer
from ploneintranet.testing import dummy_file
from ploneintranet.testing import dummy_image
from ploneintranet.testing import temporary_registry_record
from ploneintranet.theme.interfaces import IThemeSpecific
from ploneintranet.todo.behaviors import ITodo
from ploneintranet.workspace.basecontent.baseviews import ContentView
from ploneintranet.workspace.basecontent.utils import dexterity_update
from ploneintranet.workspace.interfaces import IWorkspaceAppContentLayer
from ploneintranet.workspace.tests.base import BaseTestCase
from zope.component import getSiteManager
from zope.interface import alsoProvides
from zope.lifecycleevent.interfaces import IAttributes
from zope.lifecycleevent.interfaces import IObjectModifiedEvent

import lxml

html_input = """Intro of Plone Intranet
<h1>Overview</h1>
<p><span class="random">Quaive</span> is über-cool.
<ul>
<li>Red</li>
<li>Yellow
<li>Green</li>
</ul>
<blockquote>It's the awesomest thing.</blockquote>
<strong>Get your copy today!</strong>
"""

# All tags are properly closed. All text is properly wrapped.
# Unwanted tags get removed.
html_cleaned = """<p>Intro of Plone Intranet</p>
<h1>Overview</h1>
<p>Quaive is über-cool.</p>
<ul>
<li>Red</li>
<li>Yellow</li>
<li>Green</li>
</ul>
<blockquote>It's the awesomest thing.</blockquote>
<p><strong>Get your copy today!</strong></p>"""


class TestContentViews(BaseTestCase):
    def setUp(self):
        super().setUp()
        self.login_as_portal_owner()
        self.workspace = self.portal.workspaces["demo-workspace"]
        self.request = self.layer["request"]
        alsoProvides(self.request, IThemeSpecific)
        alsoProvides(self.request, INoBarcelonetaLayer)
        alsoProvides(self.request, IPloneintranetLayoutLayer)
        alsoProvides(self.request, IPloneintranetContentLayer)
        alsoProvides(self.request, IWorkspaceAppContentLayer)
        alsoProvides(self.request, IPloneAppContenttypesLayer)

    def has_preview_injection_link(self, html, html_obj=None):
        """Check the given html for the link that injects the file previews

        Returns a boolean
        """
        if html_obj is None:
            html_obj = lxml.html.fromstring(html)
        try:
            href = html_obj.cssselect("div.page-renders a.pat-inject")[0].attrib["href"]
        except BaseException:
            href = ""

        return "@@previews" in href

    def test_document_view(self):
        doc1 = api.content.create(
            container=self.workspace, type="Document", title="My Døcümént"
        )
        doc1.description = "Déscription"
        raw_text = "<p>iuhdiuahsd</p>"
        mime_type = "text/plain"
        richtext = RichTextValue(
            raw=raw_text, mimeType=mime_type, outputMimeType="text/x-html-safe"
        )
        doc1.text = richtext
        view = doc1.restrictedTraverse("document_view")
        self.assertIn("document_view.pt", view.index.filename)

        html = view()
        self.assertIn("My Døcümént", html)
        html_obj = lxml.html.fromstring(html)
        self.assertIsNotNone(
            html_obj.find(".//article[@class='document rich pat-rich']")
        )
        self.assertFalse(self.has_preview_injection_link(html, html_obj))
        app_body_elem = html_obj.find(".//div[@id='application-body']")

        self.assertIn(
            "sidebar-left-open",
            app_body_elem.get("class"),
            msg="Class for open sidebar not found in body tag",
        )
        self.assertIn(
            "focus-document",
            app_body_elem.get("class"),
            msg="Class for focus on document not found in body tag",
        )

        # In case of an error, only print custom message but not whole html
        self.longMessage = False

        # Test usage of redactor editor in output.
        with temporary_registry_record("ploneintranet.layout.texteditor", "redactor"):
            html_2 = view()
            self.assertIn("pat-redactor", html_2, '"pat-redactor" not in output.')
            self.assertIn(
                "editor-toolbar redactor",
                html_2,
                '"editor-toolbar redactor" not in output.',
            )
            # Not testing for tiptap-not-in-output because we're using it for comments.

        # Test usage of tiptap editor in output.
        with temporary_registry_record("ploneintranet.layout.texteditor", "tiptap"):
            html_3 = view()
            self.assertIn("pat-tiptap", html_3, '"pat-tiptap" not in output.')
            self.assertIn(
                "editor-toolbar tiptap",
                html_3,
                '"editor-toolbar tiptap" not in output.',
            )
            self.assertNotIn("redactor", html_3, '"pat-redactor" not in output.')

        self.longMessage = True

    def test_image_view(self):
        img1 = api.content.create(
            container=self.workspace,
            type="Image",
            title="My îmage",
            image=dummy_image(),
        )
        img1.description = "Déscription"
        view_image = img1.restrictedTraverse("image_view")
        self.assertIn("document_view.pt", view_image.index.filename)
        html = view_image()
        self.assertIn("My îmage", html)
        self.assertIn("my-image/@@images/image", html)

    def test_file_view(self):
        # Previews are disabled in the test layer
        pi_api.events.enable_previews()
        file1 = api.content.create(
            container=self.workspace,
            type="File",
            title="My Fîle",
            file=dummy_file(filename="test.docx"),
        )
        file1.description = "Déscription"
        view = file1.restrictedTraverse("file_view")
        self.assertIn("document_view.pt", view.index.filename)

        self.assertTrue(view.allow_external_editor)

        html = view()
        self.assertIn("My Fîle", html)
        html_obj = lxml.html.fromstring(html)

        # page1 is a preloaded page
        page1 = html_obj.find(".//figure[@id='page-1-page-compound']")
        page1_inject = page1.find("./a[@class='pat-inject image-loader']")
        self.assertIsNone(page1_inject)
        page1_img = page1.find("./a[@class='gallery-item']//img")
        self.assertIn("render.svg?page=1", page1_img.attrib["src"])

        # page2 is preloaded also, no use to check that.

        # page3 has an injection loader
        page3 = html_obj.find(".//figure[@id='page-3-page-compound']")
        page3_inject = page3.find("./a[@class='pat-inject image-loader']")
        self.assertIn(
            "previews-page-compound-rendered?page=3", page3_inject.attrib["href"]
        )
        page3_img = page3.find("./a[@class='gallery-item']//img")
        self.assertIsNone(page3_img)

        # now render and test the injection helper view
        view = file1.restrictedTraverse("@@previews-page-compound-rendered")
        view.request.form["page"] = 3
        html = view()
        html_obj = lxml.html.fromstring(html)

        page3 = html_obj.find(".//figure[@id='page-3-page-compound-rendered']")
        page3_inject = page3.find("./a[@class='pat-inject image-loader']")
        self.assertIsNone(page3_inject)
        page3_img = page3.find("./a[@class='gallery-item']//img")
        self.assertIn("render.svg?page=3", page3_img.attrib["src"])

    def test_file_view__audio(self):
        """External editor and only office should be disabled in "quick-functions"."""
        file1 = api.content.create(
            container=self.workspace,
            type="File",
            title="My Fîle",
            file=dummy_file(filename="test.mp3"),
        )
        view = file1.restrictedTraverse("file_view")
        self.assertFalse(view.allow_external_editor)

    def test_file_view__video(self):
        """External editor and only office should be disabled in "quick-functions"."""
        file1 = api.content.create(
            container=self.workspace,
            type="File",
            title="My Fîle",
            file=dummy_file(filename="test.mp4"),
        )
        view = file1.restrictedTraverse("file_view")
        self.assertFalse(view.allow_external_editor)


class TestDexterityUpdate(BaseTestCase):
    def setUp(self):
        super().setUp()
        self.login_as_portal_owner()
        self.workspace = self.portal.workspaces["demo-workspace"]
        self.todo1 = api.content.create(
            container=self.workspace, type="todo", title="Todo1"
        )
        self.doc1 = api.content.create(
            container=self.workspace, type="Document", title="My Døcümént"
        )
        self.request = self.layer["request"]

    def test_not_modified(self):
        modified, errors = dexterity_update(self.todo1)
        self.assertEqual(errors, [])
        self.assertFalse(modified)

    def test_description_modified(self):
        self.todo1.REQUEST.form = {"description": "test"}
        modified, errors = dexterity_update(self.todo1)
        self.assertEqual(errors, [])
        self.assertEqual(modified, {IBasic: {"description"}})

    def test_multiple_fields_modified(self):
        self.todo1.REQUEST.form = {
            "title": "Todo 2000",
            "description": "test",
            "due": "2015-10-24",
        }
        modified, errors = dexterity_update(self.todo1)
        self.assertEqual(errors, [])
        self.assertEqual(modified, {IBasic: {"description", "title"}, ITodo: {"due"}})

    def test_sanitize_in_action(self):
        self.doc1.REQUEST.form = {"text": html_input}
        modified, errors = dexterity_update(self.doc1)
        self.assertEqual(errors, [])
        self.assertEqual(modified, {IRichTextBehavior: {"text"}})
        # Ignore differences in line breaks, only the semantics matter.
        self.assertEqual(
            self.doc1.text.raw.replace("\n", ""), html_cleaned.replace("\n", "")
        )

    def test_sanitize_does_not_self_close_iframe(self):
        text_input = """<p><iframe src="https://www.youtube.com/watch?v=GO1mRJkGXTA"></iframe></p>"""  # noqa: E501
        self.doc1.REQUEST.form = {"text": text_input}
        modified, errors = dexterity_update(self.doc1)
        self.assertEqual(errors, [])
        self.assertEqual(modified, {IRichTextBehavior: {"text"}})
        # iframe should not be self-closed
        self.assertEqual(self.doc1.text.raw, text_input)

    def test_sanitize_can_be_switched_off(self):
        api.portal.set_registry_record("ploneintranet.workspace.sanitize_html", False)
        self.doc1.REQUEST.form = {"text": html_input}
        modified, errors = dexterity_update(self.doc1)
        self.assertEqual(errors, [])
        self.assertEqual(modified, {IRichTextBehavior: {"text"}})
        self.assertEqual(self.doc1.text.raw, html_input)


class TestContentViewUpdate(BaseTestCase):
    def setUp(self):
        super().setUp()
        self.login_as_portal_owner()
        self.workspace = self.portal.workspaces["demo-workspace"]
        self.todo1 = api.content.create(
            container=self.workspace, type="todo", title="Todo1"
        )
        self.request = self.layer["request"]

        def catch_event(obj, event):
            self.event = event

        self.event = None
        self.catch_event = catch_event
        sm = getSiteManager()
        sm.registerHandler(self.catch_event, [ITodo, IObjectModifiedEvent])

    def tearDown(self):
        sm = getSiteManager()
        sm.unregisterHandler(self.catch_event, [ITodo, IObjectModifiedEvent])

    def test_event_thrown_on_field_change(self):
        self.request.form = {"description": "test"}
        view = ContentView(self.todo1, self.request)
        view.update()
        self.assertTrue(IObjectModifiedEvent.providedBy(self.event))

    def test_no_event_thrown_on_workflow_change(self):
        self.request.form = {"workflow_action": "finish"}
        view = ContentView(self.todo1, self.request)
        view.update()
        self.assertTrue(self.event is None)

    def test_event_descriptions(self):
        self.request.form = {"description": "test"}
        view = ContentView(self.todo1, self.request)
        view.update()
        self.assertTrue(IObjectModifiedEvent.providedBy(self.event))
        self.assertTrue(IAttributes.providedBy(self.event.descriptions[0]))
        self.assertEqual(self.event.descriptions[0].interface, IBasic)
        self.assertEqual(self.event.descriptions[0].attributes, ("description",))

    def test_multiple_event_descriptions(self):
        self.request.form = {
            "title": "Todo 2000",
            "description": "test",
            "due": "2015-10-24",
        }
        view = ContentView(self.todo1, self.request)
        view.update()
        self.assertTrue(IObjectModifiedEvent.providedBy(self.event))
        self.assertTrue(IAttributes.providedBy(self.event.descriptions[0]))
        self.assertTrue(IAttributes.providedBy(self.event.descriptions[1]))
        descs = {d.interface: set(d.attributes) for d in self.event.descriptions}
        self.assertSetEqual(descs[IBasic], {"description", "title"})
        self.assertSetEqual(descs[ITodo], {"due"})
