from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.by import By

from apps.applets.models import Applet
from apps.lyric.models import User

from .base import FunctionalTest


def _setup_palette_applet():
    Applet.objects.get_or_create(
        slug="palette",
        defaults={"name": "Palette", "context": "dashboard"},
    )


class PalettePreviewTest(FunctionalTest):
    """Clicking a swatch previews the palette on the whole body."""

    def setUp(self):
        super().setUp()
        _setup_palette_applet()
        User.objects.get_or_create(email="preview@test.io")
        self.create_pre_authenticated_session("preview@test.io")
        self.browser.get(self.live_server_url)

    def _click_non_active_swatch(self):
        swatch = self.wait_for(
            lambda: self.browser.find_element(
                By.CSS_SELECTOR,
                ".palette-item:not(:has(.swatch.active)) .swatch",
            )
        )
        swatch.click()
        return swatch

    def test_clicking_swatch_adds_preview_class_to_body(self):
        swatch = self._click_non_active_swatch()
        palette_name = swatch.get_attribute("data-palette")
        self.wait_for(
            lambda: self.assertIn(
                palette_name,
                self.browser.find_element(By.TAG_NAME, "body").get_attribute("class"),
            )
        )

    def test_body_has_only_preview_palette_during_preview(self):
        # Original palette class is swapped out — only the preview class is on body
        swatch = self._click_non_active_swatch()
        palette_name = swatch.get_attribute("data-palette")
        self.wait_for(
            lambda: self.assertIn(
                palette_name,
                self.browser.find_element(By.TAG_NAME, "body").get_attribute("class"),
            )
        )
        self.wait_for(
            lambda: self.assertNotIn(
                "palette-default",
                self.browser.find_element(By.TAG_NAME, "body").get_attribute("class"),
            )
        )

    def test_clicking_elsewhere_reverts_body_to_original_palette(self):
        swatch = self._click_non_active_swatch()
        palette_name = swatch.get_attribute("data-palette")
        self.wait_for(
            lambda: self.assertIn(
                palette_name,
                self.browser.find_element(By.TAG_NAME, "body").get_attribute("class"),
            )
        )
        # Click outside the applet
        self.browser.find_element(By.CSS_SELECTOR, "h1, h2").click()
        self.wait_for(
            lambda: self.assertNotIn(
                palette_name,
                self.browser.find_element(By.TAG_NAME, "body").get_attribute("class"),
            )
        )
        # Original palette restored
        self.assertIn(
            "palette-default",
            self.browser.find_element(By.TAG_NAME, "body").get_attribute("class"),
        )

    def test_swatch_gets_previewing_class_on_click(self):
        self._click_non_active_swatch()
        self.wait_for(
            lambda: self.browser.find_element(By.CSS_SELECTOR, ".swatch.previewing")
        )

    def test_previewing_class_removed_on_dismiss(self):
        self._click_non_active_swatch()
        self.wait_for(
            lambda: self.browser.find_element(By.CSS_SELECTOR, ".swatch.previewing")
        )
        self.browser.find_element(By.CSS_SELECTOR, "h1, h2").click()
        self.wait_for(
            lambda: self.assertEqual(
                len(self.browser.find_elements(By.CSS_SELECTOR, ".swatch.previewing")),
                0,
            )
        )

    def test_auto_dismiss_after_ten_seconds(self):
        swatch = self._click_non_active_swatch()
        palette_name = swatch.get_attribute("data-palette")
        self.wait_for(
            lambda: self.assertIn(
                palette_name,
                self.browser.find_element(By.TAG_NAME, "body").get_attribute("class"),
            )
        )
        # After 10s the preview should clear automatically
        self.wait_for_slow(
            lambda: self.assertNotIn(
                palette_name,
                self.browser.find_element(By.TAG_NAME, "body").get_attribute("class"),
            ),
            timeout=15,
        )


class PaletteOkButtonTest(FunctionalTest):
    """OK btn (unlocked) / × btn (locked) appear centered on clicked swatch."""

    def setUp(self):
        super().setUp()
        _setup_palette_applet()
        User.objects.get_or_create(email="okbtn@test.io")
        self.create_pre_authenticated_session("okbtn@test.io")
        self.browser.get(self.live_server_url)

    def test_ok_btn_absent_before_click(self):
        btns = self.browser.find_elements(By.CSS_SELECTOR, ".swatch .btn-confirm")
        self.assertEqual(len([b for b in btns if b.is_displayed()]), 0)

    def test_clicking_unlocked_swatch_shows_ok_btn_inside_swatch(self):
        swatch = self.wait_for(
            lambda: self.browser.find_element(
                By.CSS_SELECTOR,
                ".palette-item:not(:has(.swatch.active)):not(:has(.swatch.locked)) .swatch",
            )
        )
        swatch.click()
        ok = self.wait_for(
            lambda: self.browser.find_element(
                By.CSS_SELECTOR, ".swatch.previewing .btn-confirm"
            )
        )
        self.assertTrue(ok.is_displayed())

    def test_clicking_locked_swatch_shows_disabled_times_btn(self):
        locked = self.wait_for(
            lambda: self.browser.find_element(By.CSS_SELECTOR, ".swatch.locked")
        )
        locked.click()
        disabled = self.wait_for(
            lambda: self.browser.find_element(
                By.CSS_SELECTOR, ".swatch.previewing .btn-disabled"
            )
        )
        self.assertIn("×", disabled.text)

    def test_clicking_ok_commits_palette_and_no_reload(self):
        self.browser.execute_script("window._no_reload = true")
        swatch = self.wait_for(
            lambda: self.browser.find_element(
                By.CSS_SELECTOR,
                ".palette-item:not(:has(.swatch.active)):not(:has(.swatch.locked)) .swatch",
            )
        )
        palette_name = swatch.get_attribute("data-palette")
        swatch.click()

        ok = self.wait_for(
            lambda: self.browser.find_element(
                By.CSS_SELECTOR, ".swatch.previewing .btn-confirm"
            )
        )
        ok.click()

        # Body ends up with only the new palette (preview cleared, committed)
        self.wait_for(
            lambda: self.assertNotIn(
                "palette-default",
                self.browser.find_element(By.TAG_NAME, "body").get_attribute("class"),
            )
        )
        self.wait_for(
            lambda: self.assertIn(
                palette_name,
                self.browser.find_element(By.TAG_NAME, "body").get_attribute("class"),
            )
        )
        self.assertTrue(self.browser.execute_script("return window._no_reload === true"))

    def test_btn_disappears_on_dismiss(self):
        swatch = self.wait_for(
            lambda: self.browser.find_element(
                By.CSS_SELECTOR,
                ".palette-item:not(:has(.swatch.active)):not(:has(.swatch.locked)) .swatch",
            )
        )
        swatch.click()
        self.wait_for(
            lambda: self.browser.find_element(
                By.CSS_SELECTOR, ".swatch.previewing .btn-confirm"
            )
        )
        self.browser.find_element(By.CSS_SELECTOR, "h1, h2").click()
        self.wait_for(
            lambda: self.assertEqual(
                len([b for b in self.browser.find_elements(
                    By.CSS_SELECTOR, ".swatch .btn-confirm"
                ) if b.is_displayed()]),
                0,
            )
        )



class PaletteTooltipTest(FunctionalTest):
    """Clicking a swatch shows a tooltip in #id_tooltip_portal."""

    def setUp(self):
        super().setUp()
        _setup_palette_applet()
        self.user, _ = User.objects.get_or_create(email="palettett@test.io")
        self.create_pre_authenticated_session("palettett@test.io")
        self.browser.get(self.live_server_url)

    def _click_swatch(self, selector=".swatch"):
        swatch = self.wait_for(
            lambda: self.browser.find_element(By.CSS_SELECTOR, selector)
        )
        swatch.click()
        return swatch

    def test_clicking_swatch_shows_tooltip_portal(self):
        self._click_swatch()
        portal = self.wait_for(
            lambda: self.browser.find_element(By.ID, "id_tooltip_portal")
        )
        self.assertTrue(portal.is_displayed())

    def test_tooltip_title_is_colored_ter_user(self):
        self._click_swatch()
        title = self.wait_for(
            lambda: self.browser.find_element(
                By.CSS_SELECTOR, "#id_tooltip_portal .tt-title"
            )
        )
        ter_user = self.browser.execute_script(
            "return getComputedStyle(document.body).getPropertyValue('--terUser').trim()"
        )
        r, g, b = [int(x.strip()) for x in ter_user.split(",")]
        color = self.browser.execute_script(
            "return getComputedStyle(arguments[0]).color", title
        )
        self.assertIn(f"rgb({r}, {g}, {b})", color)

    def test_tooltip_shows_description(self):
        self._click_swatch()
        self.wait_for(
            lambda: self.browser.find_element(
                By.CSS_SELECTOR, "#id_tooltip_portal .tt-description"
            )
        )

    def test_unlocked_swatch_shows_lock_open_and_unlocked(self):
        self._click_swatch(
            ".palette-item:not(:has(.swatch.locked)) .swatch"
        )
        lock_line = self.wait_for(
            lambda: self.browser.find_element(
                By.CSS_SELECTOR, "#id_tooltip_portal .tt-lock"
            )
        )
        self.assertIn("Unlocked", lock_line.text)
        self.assertTrue(lock_line.find_elements(By.CSS_SELECTOR, ".fa-lock-open"))

    def test_locked_swatch_shows_lock_and_locked(self):
        self._click_swatch(".swatch.locked")
        lock_line = self.wait_for(
            lambda: self.browser.find_element(
                By.CSS_SELECTOR, "#id_tooltip_portal .tt-lock"
            )
        )
        self.assertIn("Locked", lock_line.text)
        self.assertTrue(lock_line.find_elements(By.CSS_SELECTOR, ".fa-lock"))

    def test_unlocked_tooltip_shows_default_label(self):
        self._click_swatch(".palette-item:not(:has(.swatch.locked)) .swatch")
        lock_line = self.wait_for(
            lambda: self.browser.find_element(
                By.CSS_SELECTOR, "#id_tooltip_portal .tt-lock"
            )
        )
        self.assertIn("Unlocked", lock_line.text)

    def test_tooltip_dismisses_on_click_outside(self):
        self._click_swatch()
        self.wait_for(
            lambda: self.assertTrue(
                self.browser.find_element(By.ID, "id_tooltip_portal").is_displayed()
            )
        )
        self.browser.find_element(By.CSS_SELECTOR, "h1, h2").click()
        self.wait_for(
            lambda: self.assertFalse(
                self.browser.find_element(By.ID, "id_tooltip_portal").is_displayed()
            )
        )


class SiteThemeTest(FunctionalTest):
    def test_page_renders_with_earthman_palette(self):
        self.browser.get(self.live_server_url)
        body = self.browser.find_element(By.TAG_NAME, "body")
        self.assertIn("palette-default", body.get_attribute("class"))


class LightPaletteTest(FunctionalTest):
    def test_light_palette_tooltip_uses_white_background(self):
        user, _ = User.objects.get_or_create(email="light@example.com")
        user.palette = "palette-oblivion-light"
        user.save()
        self.create_pre_authenticated_session("light@example.com")

        self.browser.get(self.live_server_url + "/dashboard/wallet/")

        body = self.browser.find_element(By.TAG_NAME, "body")
        tooltip_bg = self.browser.execute_script(
            "return getComputedStyle(arguments[0]).getPropertyValue('--tooltip-bg').trim()",
            body,
        )
        self.assertEqual(tooltip_bg, "255, 255, 255")
