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

from .base import FunctionalTest
from apps.applets.models import Applet
from apps.lyric.models import Token, User


class CoinOnAStringTest(FunctionalTest):
    """COIN trinket gets the CARTE treatment — gets deposited through the
    kit-bag rails, becomes unequipped on deposit, fills 1 gate slot (vs.
    Carte's +6), shows In-Use: <room name> on Game Kit hover, and applies
    btn-disabled to its DON / DOFF apparatus while deposited."""

    def setUp(self):
        super().setUp()
        for slug, name, cols, rows in [
            ("new-game", "New Game", 6, 3),
            ("my-games", "My Games", 6, 3),
            ("game-kit", "Game Kit", 6, 3),
        ]:
            Applet.objects.get_or_create(
                slug=slug,
                defaults={
                    "name": name, "grid_cols": cols,
                    "grid_rows": rows, "context": "gameboard",
                },
            )
        # Non-staff user → post_save signal creates COIN + FREE; COIN auto-equipped
        # (cf. apps.lyric.models.create_wallet_and_tokens — is_staff branch grants
        # PASS instead and auto-equips that; we want COIN as the starter trinket).
        self.gamer = User.objects.create(email="coin@test.io")
        self.coin = self.gamer.tokens.get(token_type=Token.COIN)

    # ── Test 1 ───────────────────────────────────────────────────────────────

    def test_equipped_coin_shows_equipped_mini_tooltip_in_game_kit(self):
        """COIN auto-equipped for non-staff users; hover on its Game Kit
        token should surface the mini portal w. 'Equipped' just like the
        Carte / Pass equipped-state path does."""
        self.create_pre_authenticated_session("coin@test.io")
        self.browser.get(self.live_server_url + "/gameboard/")
        self.wait_for(lambda: self.browser.find_element(By.ID, "id_game_kit"))

        coin_el = self.browser.find_element(By.ID, "id_kit_coin_on_a_string")
        self.browser.execute_script(
            "arguments[0].scrollIntoView({block: 'center'})", coin_el
        )
        ActionChains(self.browser).move_to_element(coin_el).perform()
        self.wait_for(
            lambda: self.browser.find_element(By.ID, "id_tooltip_portal").is_displayed()
        )
        portal = self.browser.find_element(By.ID, "id_tooltip_portal")
        self.assertIn("Coin-on-a-String", portal.text)
        self.assertIn("Admit 1 Entry", portal.text)
        self.assertIn("and another after that", portal.text)
        self.assertIn("no expiry", portal.text)

        mini = self.browser.find_element(By.ID, "id_mini_tooltip_portal")
        self.wait_for(lambda: self.assertTrue(mini.is_displayed()))
        self.assertIn("Equipped", mini.text)

    # ── Test 2 ───────────────────────────────────────────────────────────────

    def test_coin_deposit_unequips_from_kit_bag_and_fills_one_slot(self):
        """Equipped COIN → create room → open kit bag → select COIN → click
        rails btn → slot 1 enters RESERVED w. OK btn → click OK → slot fills
        (COIN admits 1 Entry, cf. Carte's +6) → kit bag's Trinkets section
        is now empty (COIN unequipped on deposit same shape as Carte)."""
        self.create_pre_authenticated_session("coin@test.io")
        self.browser.get(self.live_server_url + "/gameboard/")
        self.wait_for(lambda: self.browser.find_element(By.ID, "id_game_kit"))

        # Create a new room → land on gatekeeper
        self.browser.find_element(By.ID, "id_new_game_name").send_keys("Coin Room")
        self.browser.find_element(By.ID, "id_create_game_btn").click()
        self.wait_for(
            lambda: self.assertIn("/gate/", self.browser.current_url)
        )
        self.wait_for(lambda: self.browser.find_element(By.CSS_SELECTOR, ".gate-overlay"))

        # Open kit bag, pick COIN, click rails btn — drop_token POSTs w. token_id
        # via the kit-bag JS that injects a hidden token_id input before submit.
        self.browser.find_element(By.ID, "id_kit_btn").click()
        self.wait_for(
            lambda: self.browser.find_element(
                By.CSS_SELECTOR,
                f'#id_kit_bag_dialog [data-token-type="{Token.COIN}"]',
            )
        ).click()
        self.browser.find_element(By.CSS_SELECTOR, "button.token-rails").click()

        # Slot 1 enters RESERVED — _table_positions.html renders an OK btn
        # (.btn-confirm) inside the reserved slot pointing at confirm_token.
        def get_slot(i):
            return self.browser.find_element(
                By.CSS_SELECTOR, f".gate-slot[data-slot='{i + 1}']"
            )

        self.wait_for(
            lambda: self.assertIn("reserved", get_slot(0).get_attribute("class"))
        )
        self.wait_for(
            lambda: get_slot(0).find_element(By.CSS_SELECTOR, ".btn-confirm")
        ).click()
        self.wait_for(
            lambda: self.assertIn("filled", get_slot(0).get_attribute("class"))
        )

        # COIN admits 1 Entry — slot 2 stays empty w. no confirm btn (cf.
        # Carte's carte_active path that would surface drop-token-btn there).
        self.assertEqual(
            len(get_slot(1).find_elements(By.CSS_SELECTOR, ".btn-confirm")), 0
        )

        # Kit bag's Trinket section is now empty (COIN unequipped on deposit).
        # Selector is scoped to .kit-bag-section--trinket because the Dice
        # section ALSO renders a .kit-bag-placeholder unconditionally (dice
        # feature isn't built yet) — a bare `.kit-bag-placeholder` selector
        # would silently pass even if the trinket section still rendered COIN.
        self.browser.find_element(By.ID, "id_kit_btn").click()
        self.wait_for(
            lambda: self.browser.find_element(
                By.CSS_SELECTOR,
                "#id_kit_bag_dialog .kit-bag-section--trinket .kit-bag-placeholder",
            )
        )

    # ── Test 3 ───────────────────────────────────────────────────────────────

    def test_coin_in_use_game_kit_shows_room_attribution_and_btn_disabled(self):
        """While COIN is deposited in a room, its Game Kit token carries the
        room name on data-current-room-name (parity w. Carte) so the mini
        portal can render 'In-Use: <room name>' on hover; the DON & DOFF btns
        are both btn-disabled while the COIN is committed elsewhere."""
        self.create_pre_authenticated_session("coin@test.io")
        self.browser.get(self.live_server_url + "/gameboard/")
        self.wait_for(lambda: self.browser.find_element(By.ID, "id_game_kit"))

        # Create room, deposit COIN — rails-click puts slot 1 in RESERVED;
        # OK btn fills it (debit_token sets coin.current_room = room).
        self.browser.find_element(By.ID, "id_new_game_name").send_keys("Commitment Room")
        self.browser.find_element(By.ID, "id_create_game_btn").click()
        self.wait_for(lambda: self.assertIn("/gate/", self.browser.current_url))
        self.browser.find_element(By.ID, "id_kit_btn").click()
        self.wait_for(
            lambda: self.browser.find_element(
                By.CSS_SELECTOR,
                f'#id_kit_bag_dialog [data-token-type="{Token.COIN}"]',
            )
        ).click()
        self.browser.find_element(By.CSS_SELECTOR, "button.token-rails").click()
        self.wait_for(
            lambda: self.browser.find_element(
                By.CSS_SELECTOR, ".gate-slot[data-slot='1'].reserved"
            )
        )
        self.wait_for(
            lambda: self.browser.find_element(
                By.CSS_SELECTOR, ".gate-slot[data-slot='1'] .btn-confirm"
            )
        ).click()
        self.wait_for(
            lambda: self.browser.find_element(
                By.CSS_SELECTOR, ".gate-slot[data-slot='1'].filled"
            )
        )

        # Game Kit panel only renders on /gameboard/, not the gatekeeper page
        self.browser.get(self.live_server_url + "/gameboard/")
        coin_el = self.wait_for(
            lambda: self.browser.find_element(By.CSS_SELECTOR, "#id_kit_coin_on_a_string")
        )
        self.assertEqual(
            "Commitment Room",
            coin_el.get_attribute("data-current-room-name"),
        )

        # Both DON and DOFF btns inside the COIN tooltip should be btn-disabled
        equip_btns = coin_el.find_elements(By.CSS_SELECTOR, ".tt .btn-equip, .tt .btn-unequip")
        self.assertEqual(len(equip_btns), 2, "expected one DON btn + one DOFF btn")
        for btn in equip_btns:
            self.assertIn(
                "btn-disabled", btn.get_attribute("class"),
                f"expected btn-disabled on {btn.get_attribute('class')}",
            )
