import datetime

from django.conf import settings
from django.contrib.contenttypes.models import ContentType
from django.contrib.sites.shortcuts import get_current_site
from django.test import RequestFactory, TestCase, override_settings
from django.utils import timezone

from wagtail.models import Page, PageViewRestriction, Site
from wagtail.test.testapp.models import EventIndex, SimplePage

from .sitemap_generator import Sitemap


class TestSitemapGenerator(TestCase):
    def setUp(self):
        self.home_page = Page.objects.get(id=2)

        self.child_page = self.home_page.add_child(
            instance=SimplePage(
                title="Hello world!",
                slug="hello-world",
                content="hello",
                live=True,
                last_published_at=datetime.datetime(
                    2017, 1, 1, 12, 0, 0, tzinfo=datetime.timezone.utc
                ),
                latest_revision_created_at=datetime.datetime(
                    2017, 2, 1, 12, 0, 0, tzinfo=datetime.timezone.utc
                ),
            )
        )

        self.unpublished_child_page = self.home_page.add_child(
            instance=SimplePage(
                title="Unpublished",
                slug="unpublished",
                content="hello",
                live=False,
            )
        )

        self.protected_child_page = self.home_page.add_child(
            instance=SimplePage(
                title="Protected",
                slug="protected",
                content="hello",
                live=True,
            )
        )
        PageViewRestriction.objects.create(
            page=self.protected_child_page, password="hello"
        )

        self.page_with_no_last_publish_date = self.home_page.add_child(
            instance=SimplePage(
                title="I have no last publish date :-(",
                slug="no-last-publish-date",
                content="hello",
                live=True,
                latest_revision_created_at=datetime.datetime(
                    2017, 2, 1, 12, 0, 0, tzinfo=datetime.timezone.utc
                ),
            )
        )

        self.site = Site.objects.get(is_default_site=True)

        root_page = Page.objects.get(depth=1)
        self.other_site_homepage = root_page.add_child(
            instance=SimplePage(
                title="Another site", slug="another-site", content="bonjour", live=True
            )
        )
        Site.objects.create(
            hostname="other.example.com", port=80, root_page=self.other_site_homepage
        )

        # Clear the cache to that runs are deterministic regarding the sql count
        ContentType.objects.clear_cache()

    def get_request_and_django_site(self, url):
        request = RequestFactory().get(url)
        request.META["HTTP_HOST"] = self.site.hostname
        request.META["SERVER_PORT"] = self.site.port
        return request, get_current_site(request)

    def assertDatesEqual(self, actual, expected):
        # Compare dates as naive or timezone-aware according to USE_TZ
        if not settings.USE_TZ:
            expected = timezone.make_naive(expected)
        return self.assertEqual(actual, expected)

    def test_items(self):
        request, django_site = self.get_request_and_django_site("/sitemap.xml")

        sitemap = Sitemap(request)
        pages = sitemap.items()

        self.assertIn(self.child_page.page_ptr.specific, pages)
        self.assertNotIn(self.unpublished_child_page.page_ptr.specific, pages)
        self.assertNotIn(self.protected_child_page.page_ptr.specific, pages)

    def test_get_urls_without_request(self):
        request, django_site = self.get_request_and_django_site("/sitemap.xml")
        req_protocol = request.scheme

        sitemap = Sitemap()
        with self.assertNumQueries(17):
            urls = [
                url["location"]
                for url in sitemap.get_urls(1, django_site, req_protocol)
            ]

        self.assertIn("http://localhost/", urls)  # Homepage
        self.assertIn("http://localhost/hello-world/", urls)  # Child page

    def test_get_urls_with_request_site_cache(self):
        request, django_site = self.get_request_and_django_site("/sitemap.xml")
        req_protocol = request.scheme

        sitemap = Sitemap(request)

        # pre-seed find_for_request cache, so that it's not counted towards the query count
        Site.find_for_request(request)

        with self.assertNumQueries(14):
            urls = [
                url["location"]
                for url in sitemap.get_urls(1, django_site, req_protocol)
            ]

        self.assertIn("http://localhost/", urls)  # Homepage
        self.assertIn("http://localhost/hello-world/", urls)  # Child page

    @override_settings(WAGTAIL_I18N_ENABLED=True)
    def test_get_urls_without_request_with_i18n(self):
        request, django_site = self.get_request_and_django_site("/sitemap.xml")
        req_protocol = request.scheme

        sitemap = Sitemap()
        with self.assertNumQueries(19):
            urls = [
                url["location"]
                for url in sitemap.get_urls(1, django_site, req_protocol)
            ]

        self.assertIn("http://localhost/", urls)  # Homepage
        self.assertIn("http://localhost/hello-world/", urls)  # Child page

    @override_settings(WAGTAIL_I18N_ENABLED=True)
    def test_get_urls_with_request_site_cache_with_i18n(self):
        request, django_site = self.get_request_and_django_site("/sitemap.xml")
        req_protocol = request.scheme

        sitemap = Sitemap(request)

        # pre-seed find_for_request cache, so that it's not counted towards the query count
        Site.find_for_request(request)

        with self.assertNumQueries(16):
            urls = [
                url["location"]
                for url in sitemap.get_urls(1, django_site, req_protocol)
            ]

        self.assertIn("http://localhost/", urls)  # Homepage
        self.assertIn("http://localhost/hello-world/", urls)  # Child page

    def test_get_urls_uses_specific(self):
        request, django_site = self.get_request_and_django_site("/sitemap.xml")
        req_protocol = request.scheme

        # Add an event page which has an extra url in the sitemap
        self.home_page.add_child(
            instance=EventIndex(
                title="Events",
                slug="events",
                live=True,
            )
        )

        sitemap = Sitemap(request)
        urls = [
            url["location"] for url in sitemap.get_urls(1, django_site, req_protocol)
        ]

        self.assertIn("http://localhost/events/", urls)  # Main view
        self.assertIn("http://localhost/events/past/", urls)  # Sub view

    def test_lastmod_uses_last_published_date(self):
        request, django_site = self.get_request_and_django_site("/sitemap.xml")
        req_protocol = request.scheme

        sitemap = Sitemap(request)
        urls = sitemap.get_urls(1, django_site, req_protocol)

        child_page_lastmod = [
            url["lastmod"]
            for url in urls
            if url["location"] == "http://localhost/hello-world/"
        ][0]
        self.assertDatesEqual(
            child_page_lastmod,
            datetime.datetime(2017, 1, 1, 12, 0, 0, tzinfo=datetime.timezone.utc),
        )

        # if no last_publish_date is defined, use latest revision date
        child_page_lastmod = [
            url["lastmod"]
            for url in urls
            if url["location"] == "http://localhost/no-last-publish-date/"
        ][0]
        self.assertDatesEqual(
            child_page_lastmod,
            datetime.datetime(2017, 2, 1, 12, 0, 0, tzinfo=datetime.timezone.utc),
        )

    def test_latest_lastmod(self):
        # give the homepage a lastmod
        self.home_page.last_published_at = datetime.datetime(
            2017, 3, 1, 12, 0, 0, tzinfo=datetime.timezone.utc
        )
        self.home_page.save()

        request, django_site = self.get_request_and_django_site("/sitemap.xml")
        req_protocol = request.scheme

        sitemap = Sitemap(request)
        sitemap.get_urls(1, django_site, req_protocol)

        self.assertDatesEqual(
            sitemap.latest_lastmod,
            datetime.datetime(2017, 3, 1, 12, 0, 0, tzinfo=datetime.timezone.utc),
        )

    def test_latest_lastmod_missing(self):
        # ensure homepage does not have lastmod
        self.home_page.last_published_at = None
        self.home_page.save()

        request, django_site = self.get_request_and_django_site("/sitemap.xml")
        req_protocol = request.scheme

        sitemap = Sitemap(request)
        sitemap.get_urls(1, django_site, req_protocol)

        self.assertFalse(hasattr(sitemap, "latest_lastmod"))

    def test_non_default_site(self):
        request = RequestFactory().get("/sitemap.xml")
        request.META["HTTP_HOST"] = "other.example.com"
        request.META["SERVER_PORT"] = 80

        sitemap = Sitemap(request)
        pages = sitemap.items()

        self.assertIn(self.other_site_homepage.page_ptr.specific, pages)
        self.assertNotIn(self.child_page.page_ptr.specific, pages)


class TestIndexView(TestCase):
    def test_index_view(self):
        response = self.client.get("/sitemap-index.xml")

        self.assertEqual(response.status_code, 200)
        self.assertEqual(response["Content-Type"], "application/xml")


class TestSitemapView(TestCase):
    def test_sitemap_view(self):
        response = self.client.get("/sitemap.xml")

        self.assertEqual(response.status_code, 200)
        self.assertEqual(response["Content-Type"], "application/xml")

    def test_sitemap_view_with_current_site_middleware(self):
        with self.modify_settings(
            MIDDLEWARE={
                "append": "django.contrib.sites.middleware.CurrentSiteMiddleware",
            }
        ):
            response = self.client.get("/sitemap.xml")

        self.assertEqual(response.status_code, 200)
        self.assertEqual(response["Content-Type"], "application/xml")
