from plone import api
from ploneintranet.api.tests.mocks import FakeUserprofile
from ploneintranet.news.interfaces import IBannerWithIcon
from ploneintranet.news.testing import FunctionalTestCase
from ploneintranet.news.testing import IntegrationTestCase
from ploneintranet.search.solr.adapters import SearchResponse
from ploneintranet.testing import dummy_image
from unittest import mock
from zope.globalrequest import getRequest
from zope.interface import alsoProvides

import lxml


class TestViews(IntegrationTestCase):
    def mark_as_banner_with_icon(self, obj):
        """Mark this object as a banner with icon"""
        alsoProvides(obj, IBannerWithIcon)
        obj.reindexObject()

    def fiddle_modification_date(self, obj, date):
        obj.setModificationDate(date)
        obj.reindexObject(idxs=["modified"])

    def test_tip_of_the_day(self):
        view = api.content.get_view("tip-of-the-day", self.portal, self.request.clone())
        self.assertEqual(view.tip, None)
        news = self.portal.news["press-mentions"]["fusce-fames"]

        view.request = self.request.clone()
        self.mark_as_banner_with_icon(news)
        self.assertEqual(view.tip, news)

        another_news = self.portal.news["company-news"]["fames-vitae"]
        self.mark_as_banner_with_icon(another_news)

        # The tip is the last modified one
        self.fiddle_modification_date(news, "2016-01-01")
        view.request = self.request.clone()
        self.assertListEqual(
            [another_news, news], [x.getObject() for x in view.search_tips()]
        )
        self.assertEqual(view.tip, another_news)

        self.fiddle_modification_date(another_news, "2015-01-01")
        view.request = self.request.clone()
        self.assertListEqual(
            [news, another_news], [x.getObject() for x in view.search_tips()]
        )
        self.assertEqual(view.tip, news)

        with mock.patch(
            "ploneintranet.api.userprofile.get_current", return_value=FakeUserprofile()
        ):
            view.request = self.request.clone()
            view.request.form["read"] = news.UID()
            view()
            self.assertEqual(
                "http://nohost/plone/news/press-mentions/fusce-fames",
                view.request.response.getHeader("location"),
            )

        # When posting we close the panel and mark as read all the news
        with mock.patch(
            "ploneintranet.api.userprofile.get_current", return_value=FakeUserprofile()
        ):
            view.request = self.request.clone()
            view.request.method = "POST"
            view()
            self.assertEqual(view.tip, None)

    def test_mygroups_section(self):
        """Test the creation of a "Department news" section"""
        # Check that the publisher can create a "Department news" smart section
        publisher = api.content.get_view(
            "publisher", self.portal.news, self.request.clone()
        )
        publisher.request.form.update(
            {
                "form.id": "create",
                "type": "section",
                "view_name": "mygroups",
                "title": "News from my groups",
            }
        )
        publisher.request.method = "POST"
        publisher()
        section = self.portal.news["news-from-my-groups"]
        self.assertEqual(section.title, "News from my groups")
        self.assertEqual(section.view_name, "mygroups")

        # Check we display the news from my groups
        section_view = api.content.get_view("view", section, self.request.clone())
        self.assertEqual(len(section_view.news_items()), 0)
        self.assertListEqual(list(section_view.news_items()), [])

    def test_trending_section(self):
        """Test the creation of a "Trending news items" section"""
        # Check that the publisher can create a "Trending news items" smart section
        publisher = api.content.get_view(
            "publisher", self.portal.news, self.request.clone()
        )
        publisher.request.form.update(
            {
                "form.id": "create",
                "type": "section",
                "view_name": "trending",
                "title": "Trending News",
            }
        )
        publisher.request.method = "POST"
        publisher()
        section = self.portal.news["trending-news"]
        self.assertEqual(section.title, "Trending News")
        self.assertEqual(section.view_name, "trending")

        # Check we display the news from my groups
        section_view = api.content.get_view("view", section, self.request.clone())
        self.assertEqual(len(section_view.news_items()), 0)
        self.assertListEqual(list(section_view.news_items()), [])

    def test_events_section(self):
        """Test the creation of an "Events" section"""
        # Check that the publisher can create an "Events" smart section
        publisher = api.content.get_view(
            "publisher", self.portal.news, self.request.clone()
        )
        publisher.request.form.update(
            {
                "form.id": "create",
                "type": "section",
                "view_name": "events-as-news",
                "title": "Events",
            }
        )
        publisher.request.method = "POST"
        publisher()
        section = self.portal.news["events"]
        self.assertEqual(section.title, "Events")
        self.assertEqual(section.view_name, "events-as-news")

        # Check we display the news from my groups
        section_view = api.content.get_view("view", section, self.request.clone())
        self.assertEqual(len(section_view.news_items()), 0)
        self.assertListEqual(list(section_view.news_items()), [])

    def test_newsitem_image(self):
        """Test if a image in a newsitem is used and overrides the placeholder.
        The background-image is shown instead of the src image.
        """
        # if creating newsitems in portal root ever fails, simply change it.
        news = api.content.create(
            type="News Item",
            id="testnews",
            title="Test News",
            image=dummy_image(),
            container=self.portal,
        )
        view = api.content.get_view("feeditem", news, self.request.clone())

        tree = lxml.html.fromstring(view())
        els = tree.xpath('//img[@class="illustration"]')

        self.assertEqual(len(els), 1)
        self.assertIn("placeholder", els[0].attrib["src"])
        self.assertIn("background-image", els[0].attrib["style"])

    def test_newsitem_image_placeholder(self):
        """Test if the placeholder image in a newsitem is used if no image is
        defined.
        """
        # if creating newsitems in portal root ever fails, simply change it.
        news = api.content.create(
            type="News Item", id="testnews", title="Test News", container=self.portal
        )
        view = api.content.get_view("feeditem", news, self.request.clone())

        tree = lxml.html.fromstring(view())
        els = tree.xpath('//img[@class="illustration"]')

        self.assertEqual(len(els), 1)
        self.assertIn("placeholder", els[0].attrib["src"])
        self.assertNotIn("style", els[0].attrib)


class MagazineTest(FunctionalTestCase):
    """
    Because sqlalchemy gives threading errors when hit via ZServer,
    we fake browser interactions directly on the testing thread itself.

    This is just a minimal smoke test to prove that the view does not error.
    """

    def hit(self, section, news):
        """Simulate a user reading a news"""
        view = api.content.get_view(
            "mustread-hit", self.portal.news[section][news], self.request.clone()
        )
        view()

    def test_magazine_trending(self):
        view = api.content.get_view("view", self.portal.news, self.request.clone())
        self.assertEqual(0, len(view.trending_items()))
        self.hit("company-news", "etiam-augue")
        # Clean up memoize
        view.request = self.request.clone()
        self.assertEqual(1, len(view.trending_items()))

    def test_magazine_view(self):
        from ploneintranet.layout.pinning import IPinned
        from ploneintranet.layout.pinning import IPinningBehavior
        from ploneintranet.news.behaviors import INewsMeta
        from ploneintranet.news.interfaces import IMagazineHome

        section = self.portal.news["company-news"]
        item1 = section["etiam-augue"]
        item2 = section["fames-vitae"]

        # First, re-set all to magazine_home = False
        news_items = api.content.find(context=section, portal_type="News Item")
        for item in news_items:
            ob = item.getObject()
            INewsMeta(ob).magazine_home = False
            ob.reindexObject(idxs=["object_provides"])
        self.solr_commit()

        request = getRequest()
        view = api.content.get_view("view", self.portal.news, request)

        # Set
        _news_items = view._news_items(section=section)
        news_items = view.news_items(section=section)
        request.__annotations__.clear()
        # Test
        self.assertTrue(isinstance(_news_items, SearchResponse))
        self.assertTrue(isinstance(news_items, SearchResponse))
        self.assertEqual(_news_items.total_results, 0)
        self.assertEqual(len(_news_items), 0)

        # Set
        INewsMeta(item1).magazine_home = True
        item1.reindexObject(idxs=["object_provides"])
        self.solr_commit()
        news_items = view.news_items(section=section)
        request.__annotations__.clear()
        # Test
        self.assertEqual(IMagazineHome.providedBy(item1), True)
        self.assertEqual(len(news_items), 1)

        # Set
        INewsMeta(item2).magazine_home = True
        item2.reindexObject(idxs=["object_provides"])
        self.solr_commit()
        news_items = view.news_items(section=section)
        request.__annotations__.clear()
        # Test
        self.assertEqual(IMagazineHome.providedBy(item2), True)
        self.assertEqual(len(news_items), 2)
        # Items should be in order as created - item 2 created after item 1
        self.assertListEqual([it.getObject() for it in news_items], [item2, item1])

        # Set
        IPinningBehavior(item1).pinned = True
        item1.reindexObject(idxs=["object_provides", "pinned"])
        self.solr_commit()
        news_items = view.news_items(section=section)
        request.__annotations__.clear()
        # Test
        self.assertEqual(IPinned.providedBy(item1), True)
        self.assertEqual(IMagazineHome.providedBy(item1), True)  # still
        self.assertEqual(len(news_items), 2)
        # Pinned items appear first.
        self.assertListEqual([it.getObject() for it in news_items], [item1, item2])

        # ``pinned`` but not ``magazine_home`` should not appear on start page.
        # Set
        INewsMeta(item1).magazine_home = False
        item1.reindexObject(idxs=["object_provides"])
        self.solr_commit()
        news_items = view.news_items(section=section)
        request.__annotations__.clear()
        # Test
        self.assertEqual(IPinned.providedBy(item1), True)  # still
        self.assertEqual(IMagazineHome.providedBy(item1), False)
        self.assertEqual(len(news_items), 1)
        self.assertListEqual([it.getObject() for it in news_items], [item2])

    def test_news_section_view__pinning(self):
        from ploneintranet.layout.pinning import IPinningBehavior

        section = self.portal.news["company-news"]

        request = getRequest()
        view = api.content.get_view("view", section, request)

        # Should show all news articles in section.
        # Set
        news_items = view.news_items()
        request.__annotations__.clear()
        # Test
        self.assertTrue(isinstance(news_items, SearchResponse))
        self.assertEqual(news_items.total_results, 5)
        self.assertEqual(len(news_items), 5)

        # Should show pinned news articles on top.
        # Set
        item_will_be_pinned = news_items[-1].getObject()
        item_will_be_last = news_items[-2].getObject()
        IPinningBehavior(item_will_be_pinned).pinned = True
        item_will_be_pinned.reindexObject(idxs=["object_provides", "pinned"])
        self.solr_commit()
        news_items = view.news_items()
        request.__annotations__.clear()
        # Test
        self.assertEqual(news_items.total_results, 5)
        self.assertEqual(len(news_items), 5)
        self.assertEqual(news_items[0].getObject(), item_will_be_pinned)
        self.assertEqual(news_items[-1].getObject(), item_will_be_last)

    def test_news_section_view__batching(self):
        section = self.portal.news["company-news"]

        request = getRequest()
        view = api.content.get_view("view", section, request)
        news_items = view.news_items()
        request.__annotations__.clear()

        self.assertTrue(isinstance(news_items, SearchResponse))
        self.assertEqual(news_items.total_results, 5)
        self.assertEqual(len(news_items), 5)

        news_items = tuple(news_items)  # Unluckily SearchResponse is not subscriptable
        item_1 = news_items[1].getObject()
        item_2 = news_items[2].getObject()

        request = getRequest()
        request.form["b_start"] = 1
        request.form["b_size"] = 2
        view = api.content.get_view("view", section, request)

        self.assertEqual(view.b_start, 1)
        self.assertEqual(view.b_size, 2)

        news_items = view.news_items()
        request.__annotations__.clear()
        self.assertEqual(news_items.total_results, 5)
        self.assertListEqual(
            [item.getObject() for item in news_items], [item_1, item_2]
        )
        self.assertTrue(view.has_more)

    def test_trending_section(self):
        """Test the creation of a trending section"""
        # Check that the publisher can create a "Trending" section
        publisher = api.content.get_view(
            "publisher", self.portal.news, self.request.clone()
        )
        publisher.request.form.update(
            {
                "form.id": "create",
                "type": "section",
                "view_name": "trending",
                "title": "Trending News",
            }
        )
        publisher.request.method = "POST"
        publisher()
        section = self.portal.news["trending-news"]
        self.assertEqual(section.title, "Trending News")
        self.assertEqual(section.view_name, "trending")

        # Check we display the trending news
        section_view = api.content.get_view("view", section, self.request.clone())
        self.assertListEqual(list(section_view.news_items()), [])

        self.hit("company-news", "etiam-augue")
        section_view.request = self.request.clone()
        self.assertListEqual(
            [x.getId() for x in section_view.news_items()], ["etiam-augue"]
        )

        self.hit("press-mentions", "proin-netus")
        section_view.request = self.request.clone()
        self.assertSetEqual(
            {x.getId() for x in section_view.news_items()},
            {"proin-netus", "etiam-augue"},
        )


class PublisherTest(IntegrationTestCase):
    """
    Because sqlalchemy gives threading errors when hit via ZServer,
    we fake browser interactions directly on the testing thread itself.

    This is just a minimal smoke test to prove that the view does not error.
    """

    def setUp(self):
        super().setUp()
        self.view = view = api.content.get_view(
            "publisher", self.portal.news, self.request.clone()
        )
        self.tree = lxml.html.fromstring(view())

    def test_sidebar_section(self):
        heading = self.tree.cssselect("form#listing-company-news h4")[0]
        self.assertEqual(heading.text.strip(), "Company News")

    def test_news_sidebar_item(self):
        """The sidebar displays the items sorted by effective date"""
        section = self.portal.news["company-news"]
        section_news_view = api.content.get_view(
            "section-load-more", section, self.request.clone()
        )
        news_items: SearchResponse = section_news_view.news_items()
        self.assertListEqual(
            [search_result.Title for search_result in news_items],
            [
                "Vitae ipsum metus nonummy tempus lorem netus vivamus dui ad cursus leo orci inceptos eni fames.",  # noqa: E501
                "Class lacus semper erat laoreet et accumsan gravida aliquet mauris luctus mi eros.",  # noqa: E501
                "Dolor etiam mauris lobortis nibh torquent dis natoque torquent cubilia.",  # noqa: E501
                "Fames vitae.",
                "Etiam augue.",
            ],
        )
        tree = lxml.html.fromstring(section_news_view())
        heading = tree.cssselect("#news-articles label h4")[-1]
        self.assertEqual(heading.text.strip(), "Etiam augue.")


class EditingTest(IntegrationTestCase):
    """
    Because sqlalchemy gives threading errors when hit via ZServer,
    we fake browser interactions directly on the testing thread itself.

    This is just a minimal smoke test to prove that the view does not error.
    """

    def setUp(self):
        super().setUp()
        self.item = item = self.portal.news["company-news"]["etiam-augue"]
        self.view = view = api.content.get_view("edit.html", item, self.request.clone())
        self.tree = lxml.html.fromstring(view())

    def test_image_visibility(self):
        checkbox = self.tree.xpath('//input[@name="article_image"]')[0]
        self.assertEqual(checkbox.value, "selected")

    def test_sidebar_section(self):
        """Verify that sidebar is loaded in edit view"""
        heading = self.tree.cssselect("form#listing-company-news h4")[0]
        self.assertEqual(heading.text.strip(), "Company News")
