from django.contrib import admin
from django.contrib.admin.tests import AdminSeleniumTestCase
from django.contrib.auth.models import User
from django.test import TestCase, override_settings
from django.urls import path, reverse

from .models import Héllo


class AdminSiteWithSidebar(admin.AdminSite):
    pass


class AdminSiteWithoutSidebar(admin.AdminSite):
    enable_nav_sidebar = False


site_with_sidebar = AdminSiteWithSidebar(name="test_with_sidebar")
site_without_sidebar = AdminSiteWithoutSidebar(name="test_without_sidebar")

site_with_sidebar.register(User)
site_with_sidebar.register(Héllo)

urlpatterns = [
    path("test_sidebar/admin/", site_with_sidebar.urls),
    path("test_wihout_sidebar/admin/", site_without_sidebar.urls),
]


@override_settings(ROOT_URLCONF="admin_views.test_nav_sidebar")
class AdminSidebarTests(TestCase):
    @classmethod
    def setUpTestData(cls):
        cls.superuser = User.objects.create_superuser(
            username="super",
            password="secret",
            email="super@example.com",
        )

    def setUp(self):
        self.client.force_login(self.superuser)

    def test_sidebar_not_on_index(self):
        response = self.client.get(reverse("test_with_sidebar:index"))
        self.assertContains(response, '<div class="main" id="main">')
        self.assertNotContains(
            response, '<nav class="sticky" id="nav-sidebar" aria-label="Sidebar">'
        )

    def test_sidebar_disabled(self):
        response = self.client.get(reverse("test_without_sidebar:index"))
        self.assertNotContains(
            response, '<nav class="sticky" id="nav-sidebar" aria-label="Sidebar">'
        )

    def test_sidebar_unauthenticated(self):
        self.client.logout()
        response = self.client.get(reverse("test_with_sidebar:login"))
        self.assertNotContains(
            response, '<nav class="sticky" id="nav-sidebar" aria-label="Sidebar">'
        )

    def test_sidebar_aria_current_page(self):
        url = reverse("test_with_sidebar:auth_user_changelist")
        response = self.client.get(url)
        self.assertContains(
            response, '<nav class="sticky" id="nav-sidebar" aria-label="Sidebar">'
        )
        self.assertContains(
            response, '<a href="%s" aria-current="page">Users</a>' % url
        )

    @override_settings(
        TEMPLATES=[
            {
                "BACKEND": "django.template.backends.django.DjangoTemplates",
                "DIRS": [],
                "APP_DIRS": True,
                "OPTIONS": {
                    "context_processors": [
                        "django.contrib.auth.context_processors.auth",
                        "django.contrib.messages.context_processors.messages",
                    ],
                },
            }
        ]
    )
    def test_sidebar_aria_current_page_missing_without_request_context_processor(self):
        url = reverse("test_with_sidebar:auth_user_changelist")
        response = self.client.get(url)
        self.assertContains(
            response, '<nav class="sticky" id="nav-sidebar" aria-label="Sidebar">'
        )
        # Does not include aria-current attribute.
        self.assertContains(response, '<a href="%s">Users</a>' % url)
        self.assertNotContains(response, "aria-current")

    @override_settings(DEBUG=True)
    def test_included_app_list_template_context_fully_set(self):
        # All context variables should be set when rendering the sidebar.
        url = reverse("test_with_sidebar:auth_user_changelist")
        with self.assertNoLogs("django.template", "DEBUG"):
            self.client.get(url)

    def test_sidebar_model_name_non_ascii(self):
        url = reverse("test_with_sidebar:admin_views_héllo_changelist")
        response = self.client.get(url)
        self.assertContains(
            response, '<div class="app-admin_views module current-app">'
        )
        self.assertContains(response, '<tr class="model-héllo current-model">')
        self.assertContains(
            response,
            '<th scope="row">'
            '<a href="/test_sidebar/admin/admin_views/h%C3%A9llo/" aria-current="page">'
            "Héllos</a></th>",
        )


@override_settings(ROOT_URLCONF="admin_views.test_nav_sidebar")
class SeleniumTests(AdminSeleniumTestCase):
    available_apps = ["admin_views"] + AdminSeleniumTestCase.available_apps

    def setUp(self):
        self.superuser = User.objects.create_superuser(
            username="super",
            password="secret",
            email="super@example.com",
        )
        self.admin_login(
            username="super",
            password="secret",
            login_url=reverse("test_with_sidebar:index"),
        )
        self.selenium.execute_script(
            "localStorage.removeItem('django.admin.navSidebarIsOpen')"
        )

    def test_sidebar_starts_open(self):
        from selenium.webdriver.common.by import By

        self.selenium.get(
            self.live_server_url + reverse("test_with_sidebar:auth_user_changelist")
        )
        main_element = self.selenium.find_element(By.CSS_SELECTOR, "#main")
        self.assertIn("shifted", main_element.get_attribute("class").split())

    def test_sidebar_can_be_closed(self):
        from selenium.webdriver.common.by import By

        self.selenium.get(
            self.live_server_url + reverse("test_with_sidebar:auth_user_changelist")
        )
        toggle_button = self.selenium.find_element(
            By.CSS_SELECTOR, "#toggle-nav-sidebar"
        )
        self.assertEqual(toggle_button.tag_name, "button")
        self.assertEqual(toggle_button.get_attribute("aria-label"), "Toggle navigation")
        nav_sidebar = self.selenium.find_element(By.ID, "nav-sidebar")
        self.assertEqual(nav_sidebar.get_attribute("aria-expanded"), "true")
        self.assertTrue(nav_sidebar.is_displayed())
        toggle_button.click()

        # Hidden sidebar is not visible.
        nav_sidebar = self.selenium.find_element(By.ID, "nav-sidebar")
        self.assertEqual(nav_sidebar.get_attribute("aria-expanded"), "false")
        self.assertFalse(nav_sidebar.is_displayed())
        main_element = self.selenium.find_element(By.CSS_SELECTOR, "#main")
        self.assertNotIn("shifted", main_element.get_attribute("class").split())

    def test_sidebar_state_persists(self):
        from selenium.webdriver.common.by import By

        self.selenium.get(
            self.live_server_url + reverse("test_with_sidebar:auth_user_changelist")
        )
        self.assertIsNone(
            self.selenium.execute_script(
                "return localStorage.getItem('django.admin.navSidebarIsOpen')"
            )
        )
        toggle_button = self.selenium.find_element(
            By.CSS_SELECTOR, "#toggle-nav-sidebar"
        )
        toggle_button.click()
        self.assertEqual(
            self.selenium.execute_script(
                "return localStorage.getItem('django.admin.navSidebarIsOpen')"
            ),
            "false",
        )
        self.selenium.get(
            self.live_server_url + reverse("test_with_sidebar:auth_user_changelist")
        )
        main_element = self.selenium.find_element(By.CSS_SELECTOR, "#main")
        self.assertNotIn("shifted", main_element.get_attribute("class").split())

        toggle_button = self.selenium.find_element(
            By.CSS_SELECTOR, "#toggle-nav-sidebar"
        )
        # Hidden sidebar is not visible.
        nav_sidebar = self.selenium.find_element(By.ID, "nav-sidebar")
        self.assertEqual(nav_sidebar.get_attribute("aria-expanded"), "false")
        self.assertFalse(nav_sidebar.is_displayed())
        toggle_button.click()
        nav_sidebar = self.selenium.find_element(By.ID, "nav-sidebar")
        self.assertEqual(nav_sidebar.get_attribute("aria-expanded"), "true")
        self.assertTrue(nav_sidebar.is_displayed())
        self.assertEqual(
            self.selenium.execute_script(
                "return localStorage.getItem('django.admin.navSidebarIsOpen')"
            ),
            "true",
        )
        self.selenium.get(
            self.live_server_url + reverse("test_with_sidebar:auth_user_changelist")
        )
        main_element = self.selenium.find_element(By.CSS_SELECTOR, "#main")
        self.assertIn("shifted", main_element.get_attribute("class").split())

    def test_sidebar_filter_persists(self):
        from selenium.webdriver.common.by import By

        self.selenium.get(
            self.live_server_url + reverse("test_with_sidebar:auth_user_changelist")
        )
        filter_value_script = (
            "return sessionStorage.getItem('django.admin.navSidebarFilterValue')"
        )
        self.assertIsNone(self.selenium.execute_script(filter_value_script))
        filter_input = self.selenium.find_element(By.CSS_SELECTOR, "#nav-filter")
        filter_input.send_keys("users")
        self.assertEqual(self.selenium.execute_script(filter_value_script), "users")