from plone import api
from plone.app.testing import applyProfile
from plone.app.testing import setRoles
from plone.app.testing import TEST_USER_ID
from ploneintranet.api.tests.mocks import FakeUserprofile
from ploneintranet.library.testing import SolrIntegrationTestCase
from ploneintranet.testing import temporary_registry_record
from unittest import mock


class TestLibraryContent(SolrIntegrationTestCase):
    def setUp(self):
        super().setUp()
        self.portal = self.layer["portal"]
        self.request = self.layer["request"]
        setRoles(self.portal, TEST_USER_ID, ("Manager",))

    def _items_to_tree(self, items):
        """This is utility method transforms the items of the pdf views
        into a sort of tree structure like:
        [
            (
                u'node path', [
                    (
                        u'node path', [
                            (u'leaf path', []),
                            (u'leaf path', []),
                            ...
                        ]
                    ),
            ),
            ...
        ]
        """
        return [
            (item.path, self._items_to_tree(getattr(item, "children", [])))
            for item in items
        ]

    def test_library_created(self):
        self.assertTrue("library" in self.portal)
        self.assertEqual("ploneintranet.library.app", self.portal.library.portal_type)

    def test_library_created_once(self):
        self.assertTrue("library" in self.portal)
        api.content.rename(self.portal["library"], new_id="handbook")
        applyProfile(self.portal, "ploneintranet.library:default")
        self.assertFalse("library" in self.portal)

    def test_sections_not_nested(self):
        ob1 = self.portal.library["human-resources"]
        with self.assertRaises(api.exc.InvalidParameterError):
            api.content.create(
                type="ploneintranet.library.section",
                title="Human Resources 2",
                container=ob1,
            )

    def test_folder_not_toplevel(self):
        with self.assertRaises(api.exc.InvalidParameterError):
            api.content.create(
                type="ploneintranet.library.folder",
                title="a folder",
                container=self.portal.library,
            )

    def test_large_section(self):
        """Test we can see al the documents in the section

        Before this patch, the solr query returned just the first 10 elements
        of the query.

        Now we want to be sure we have all of them
        """
        section = api.content.create(
            type="ploneintranet.library.section",
            title="Lots of folders",
            container=self.portal.library,
        )
        NUM_OF_FOLDERS = 11
        for idx in range(NUM_OF_FOLDERS):
            api.content.create(
                type="ploneintranet.library.folder", title=str(idx), container=section
            )
        self.solr_commit()
        view = api.content.get_view(
            "view", context=section, request=self.request.clone()
        )
        self.assertEqual(len(view.children()), NUM_OF_FOLDERS)

    def download_library_content_panel_tree(self):
        with mock.patch(
            "ploneintranet.api.userprofile.get_current", return_value=FakeUserprofile()
        ):
            view = api.content.get_view(
                "panel-download-library-content",
                self.portal.library,
                self.request.clone(),
            )
            self.assertTrue(view.download_many().startswith(b"%PDF"))

            self.assertListEqual(
                self._items_to_tree(view.items),
                [
                    (
                        "/plone/library/human-resources",
                        [
                            (
                                "/plone/library/human-resources/holidays",
                                [
                                    ("/plone/library/human-resources/holidays/0", []),
                                    ("/plone/library/human-resources/holidays/1", []),
                                    ("/plone/library/human-resources/holidays/2", []),
                                    ("/plone/library/human-resources/holidays/3", []),
                                    ("/plone/library/human-resources/holidays/4", []),
                                ],
                            ),
                            (
                                "/plone/library/human-resources/generic",
                                [
                                    ("/plone/library/human-resources/generic/0", []),
                                    ("/plone/library/human-resources/generic/1", []),
                                    ("/plone/library/human-resources/generic/2", []),
                                    ("/plone/library/human-resources/generic/3", []),
                                    ("/plone/library/human-resources/generic/4", []),
                                ],
                            ),
                        ],
                    )
                ],
            )

            # Reorder things around
            hr = self.portal.library["human-resources"]
            hr.moveObjectsUp(["generic"])
            hr["generic"].moveObjectsUp(["4"])
            self.solr_commit()

            # Clear the memoize cache
            view.request = self.request.clone()
            self.assertListEqual(
                self._items_to_tree(view.items),
                [
                    (
                        "/plone/library/human-resources",
                        [
                            (
                                "/plone/library/human-resources/generic",
                                [
                                    ("/plone/library/human-resources/generic/0", []),
                                    ("/plone/library/human-resources/generic/1", []),
                                    ("/plone/library/human-resources/generic/2", []),
                                    ("/plone/library/human-resources/generic/4", []),
                                    ("/plone/library/human-resources/generic/3", []),
                                ],
                            ),
                            (
                                "/plone/library/human-resources/holidays",
                                [
                                    ("/plone/library/human-resources/holidays/0", []),
                                    ("/plone/library/human-resources/holidays/1", []),
                                    ("/plone/library/human-resources/holidays/2", []),
                                    ("/plone/library/human-resources/holidays/3", []),
                                    ("/plone/library/human-resources/holidays/4", []),
                                ],
                            ),
                        ],
                    )
                ],
            )

    def test_download_library_content_panel(self):
        holidays = self.portal.library["human-resources"]["holidays"]

        with mock.patch(
            "ploneintranet.api.userprofile.get_current", return_value=FakeUserprofile()
        ):
            view = api.content.get_view(
                "panel-download-library-content",
                self.portal.library,
                self.request.clone(),
            )
            # Submitting a UID will trigger the download of related document
            view.request.set("uid", holidays["0"].UID())
            self.assertListEqual(
                [x.path for x in view.get_candidate_pdf_items()],
                ["/plone/library/human-resources/holidays/0"],
            )

            # Calling the download_many method returns a PDF
            self.assertTrue(view.download_many().startswith(b"%PDF"))

            # Also stores the results UID in the cart
            self.assertSetEqual(set(view.cart), {holidays["0"].UID()})

            # we can also submit a list of UIDS
            view.request.set("uid", [holidays["3"].UID(), holidays["0"].UID()])
            # the list uids controls also the sorting of the joined pdfs
            self.assertListEqual(
                [x.path for x in view.get_candidate_pdf_items()],
                [
                    "/plone/library/human-resources/holidays/3",
                    "/plone/library/human-resources/holidays/0",
                ],
            )

            # Calling the download_many method returns a PDF
            self.assertTrue(view.download_many().startswith(b"%PDF"))

            # Also stores the results UID in the cart
            self.assertSetEqual(
                set(view.cart), {holidays["0"].UID(), holidays["3"].UID()}
            )

    def test_download_topic_as_pdf(self):
        with mock.patch(
            "ploneintranet.api.userprofile.get_current", return_value=FakeUserprofile()
        ):
            view = api.content.get_view(
                "download-topic-as-pdf",
                self.portal.library["human-resources"],
                self.request.clone(),
            )
            # When downloading the topic we filter the stuff
            # contained in the context
            response = view.query_items(view.download_many_query)
            self.assertDictEqual(
                view.download_many_query, {"path": "/plone/library/human-resources"}
            )

            # We have a way to split the response result by their parent path
            items_by_container_path = view.structure_items_by_container_path(response)
            self.assertSetEqual(
                set(items_by_container_path.keys()),
                {
                    "/plone/library",
                    "/plone/library/human-resources",
                    "/plone/library/human-resources/generic",
                    "/plone/library/human-resources/holidays",
                },
            )

            # We can flatten this structure to have the items properly sorted
            items = view.get_candidate_pdf_items()
            self.assertListEqual(
                [item.path for item in items],
                [
                    "/plone/library/human-resources/holidays/0",
                    "/plone/library/human-resources/holidays/1",
                    "/plone/library/human-resources/holidays/2",
                    "/plone/library/human-resources/holidays/3",
                    "/plone/library/human-resources/holidays/4",
                    "/plone/library/human-resources/generic/0",
                    "/plone/library/human-resources/generic/1",
                    "/plone/library/human-resources/generic/2",
                    "/plone/library/human-resources/generic/3",
                    "/plone/library/human-resources/generic/4",
                ],
            )

            downloaded = view.download_many()
            # Calling the download_many method returns a PDF
            self.assertTrue(downloaded.startswith(b"%PDF"))
            # If we fail to get the individual PDFs we may still get a topic PDF but it
            # will be very short
            self.assertGreater(len(downloaded), 500, msg="Topic PDF too short")

            # Also stores the results UID in the cart
            self.assertSetEqual(set(view.cart), {item.UID for item in items})

    def test_enable_pdf_download_ui(self):
        view = api.content.get_view("view", self.portal.library, self.request.clone())
        tag_view = api.content.get_view(
            "tag", self.portal.library, self.request.clone()
        )
        self.assertFalse(view.pdf_enabled)
        self.assertFalse(tag_view.pdf_enabled)

        # Clear the memoize cache
        view.request = self.request.clone()
        tag_view.request = self.request.clone()
        with temporary_registry_record(
            "ploneintranet.library.enable_pdf_download_ui", True
        ):
            self.assertTrue(view.pdf_enabled)
            # On the tag view the UI is never allowed
            self.assertFalse(tag_view.pdf_enabled)

    def test_library_sections(self):
        obj = api.content.create(
            type="ploneintranet.library.section",
            title="Another section",
            container=self.portal.library,
        )
        self.solr_commit()
        view = api.content.get_view("view", self.portal.library, self.request.clone())
        # All topics is always first
        self.assertEqual(
            [section["title"] for section in view.sections()],
            ["All topics", "Human Resources", "Another section"],
        )
        # Sorting is based on position in parent, moving the object
        # will change the results list
        self.portal.library.moveObjectsUp([obj.getId()])
        self.solr_commit()
        view.request = self.request.clone()
        self.assertEqual(
            [section["title"] for section in view.sections()],
            ["All topics", "Another section", "Human Resources"],
        )
