from django.core.exceptions import ValidationError
from django.http.request import HttpRequest
from django.test import TestCase, override_settings

from wagtail.models import Page, Site


class TestSiteNaturalKey(TestCase):
    def test_natural_key(self):
        site = Site(hostname="example.com", port=8080)
        self.assertEqual(site.natural_key(), ("example.com", 8080))

    def test_get_by_natural_key(self):
        site = Site.objects.create(
            hostname="example.com", port=8080, root_page=Page.objects.get(pk=2)
        )
        self.assertEqual(Site.objects.get_by_natural_key("example.com", 8080), site)


class TestSiteUrl(TestCase):
    def test_root_url_http(self):
        site = Site(hostname="example.com", port=80)
        self.assertEqual(site.root_url, "http://example.com")

    def test_root_url_https(self):
        site = Site(hostname="example.com", port=443)
        self.assertEqual(site.root_url, "https://example.com")

    def test_root_url_custom_port(self):
        site = Site(hostname="example.com", port=8000)
        self.assertEqual(site.root_url, "http://example.com:8000")


class TestSiteNameDisplay(TestCase):
    def test_site_name_not_default(self):
        site = Site(
            hostname="example.com",
            port=80,
            site_name="example dot com",
            is_default_site=False,
        )
        self.assertEqual(site.__str__(), "example dot com")

    def test_site_name_default(self):
        site = Site(
            hostname="example.com",
            port=80,
            site_name="example dot com",
            is_default_site=True,
        )
        self.assertEqual(site.__str__(), "example dot com [default]")

    def test_no_site_name_not_default_port_80(self):
        site = Site(hostname="example.com", port=80, is_default_site=False)
        self.assertEqual(site.__str__(), "example.com")

    def test_no_site_name_default_port_80(self):
        site = Site(hostname="example.com", port=80, is_default_site=True)
        self.assertEqual(site.__str__(), "example.com [default]")

    def test_no_site_name_not_default_port_n(self):
        site = Site(hostname="example.com", port=8080, is_default_site=False)
        self.assertEqual(site.__str__(), "example.com:8080")

    def test_no_site_name_default_port_n(self):
        site = Site(hostname="example.com", port=8080, is_default_site=True)
        self.assertEqual(site.__str__(), "example.com:8080 [default]")


class TestSiteOrdering(TestCase):
    def setUp(self):
        self.root_page = Page.objects.get(pk=2)
        Site.objects.all().delete()  # Drop the initial site.

    def test_site_order_by_hostname(self):
        site_1 = Site.objects.create(hostname="charly.com", root_page=self.root_page)
        site_2 = Site.objects.create(hostname="bravo.com", root_page=self.root_page)
        site_3 = Site.objects.create(hostname="alfa.com", root_page=self.root_page)
        self.assertEqual(
            list(Site.objects.all().values_list("id", flat=True)),
            [site_3.id, site_2.id, site_1.id],
        )

    def test_site_order_by_hostname_upper(self):
        site_1 = Site.objects.create(hostname="charly.com", root_page=self.root_page)
        site_2 = Site.objects.create(hostname="Bravo.com", root_page=self.root_page)
        site_3 = Site.objects.create(hostname="alfa.com", root_page=self.root_page)
        self.assertEqual(
            list(Site.objects.all().values_list("id", flat=True)),
            [site_3.id, site_2.id, site_1.id],
        )

    def test_site_order_by_hostname_site_name_irrelevant(self):
        site_1 = Site.objects.create(
            hostname="charly.com", site_name="X-ray", root_page=self.root_page
        )
        site_2 = Site.objects.create(
            hostname="bravo.com", site_name="Yankee", root_page=self.root_page
        )
        site_3 = Site.objects.create(
            hostname="alfa.com", site_name="Zulu", root_page=self.root_page
        )
        self.assertEqual(
            list(Site.objects.all().values_list("id", flat=True)),
            [site_3.id, site_2.id, site_1.id],
        )


@override_settings(ALLOWED_HOSTS=["example.com", "unknown.com", "127.0.0.1", "[::1]"])
class TestFindSiteForRequest(TestCase):
    def setUp(self):
        self.default_site = Site.objects.get()
        self.site = Site.objects.create(
            hostname="example.com", port=80, root_page=Page.objects.get(pk=2)
        )

    def test_with_host(self):
        request = HttpRequest()
        request.META = {"HTTP_HOST": "example.com", "SERVER_PORT": 80}
        self.assertEqual(Site.find_for_request(request), self.site)

    def test_with_unknown_host(self):
        request = HttpRequest()
        request.META = {"HTTP_HOST": "unknown.com", "SERVER_PORT": 80}
        self.assertEqual(Site.find_for_request(request), self.default_site)

    def test_with_server_name(self):
        request = HttpRequest()
        request.META = {"SERVER_NAME": "example.com", "SERVER_PORT": 80}
        self.assertEqual(Site.find_for_request(request), self.site)

    def test_with_x_forwarded_host(self):
        with self.settings(USE_X_FORWARDED_HOST=True):
            request = HttpRequest()
            request.META = {"HTTP_X_FORWARDED_HOST": "example.com", "SERVER_PORT": 80}
            self.assertEqual(Site.find_for_request(request), self.site)

    def test_ipv4_host(self):
        request = HttpRequest()
        request.META = {"SERVER_NAME": "127.0.0.1", "SERVER_PORT": 80}
        self.assertEqual(Site.find_for_request(request), self.default_site)

    def test_ipv6_host(self):
        request = HttpRequest()
        request.META = {"SERVER_NAME": "[::1]", "SERVER_PORT": 80}
        self.assertEqual(Site.find_for_request(request), self.default_site)


class TestDefaultSite(TestCase):
    def test_create_default_site(self):
        Site.objects.all().delete()
        Site.objects.create(
            hostname="test.com", is_default_site=True, root_page=Page.objects.get(pk=2)
        )
        self.assertTrue(Site.objects.filter(is_default_site=True).exists())

    def test_change_default_site(self):
        default = Site.objects.get(is_default_site=True)
        default.is_default_site = False
        default.save()

        Site.objects.create(
            hostname="test.com", is_default_site=True, root_page=Page.objects.get(pk=2)
        )
        self.assertTrue(Site.objects.filter(is_default_site=True).exists())

    def test_there_can_only_be_one(self):
        site = Site(
            hostname="test.com", is_default_site=True, root_page=Page.objects.get(pk=2)
        )
        with self.assertRaises(ValidationError):
            site.clean_fields()

    def test_oops_there_is_more_than_one(self):
        Site.objects.create(
            hostname="example.com",
            is_default_site=True,
            root_page=Page.objects.get(pk=2),
        )

        site = Site(
            hostname="test.com", is_default_site=True, root_page=Page.objects.get(pk=2)
        )
        with self.assertRaises(Site.MultipleObjectsReturned):
            # If there already are multiple default sites, you're in trouble
            site.clean_fields()


class TestGetSiteRootPaths(TestCase):
    def setUp(self):
        self.default_site = Site.objects.get()
        self.abc_site = Site.objects.create(
            hostname="abc.com", root_page=self.default_site.root_page
        )
        self.def_site = Site.objects.create(
            hostname="def.com", root_page=self.default_site.root_page
        )

        # Changing the hostname to show that being the default site takes
        # promotes a site over the alphabetical ordering of hostname
        self.default_site.hostname = "xyz.com"
        self.default_site.save()

    def test_result_order_when_multiple_sites_share_the_same_root_page(self):
        result = Site.get_site_root_paths()

        # An entry for the default site should come first
        self.assertEqual(result[0][0], self.default_site.id)

        # Followed by entries for others in 'host' alphabetical order
        self.assertEqual(result[1][0], self.abc_site.id)
        self.assertEqual(result[2][0], self.def_site.id)
