"""
Assist — "How do you want to play?" (the human-readable, no-judgment cheats).

Opened from the game card / Quick Menu assist row (incl. the "+2 more" pill).
A curated "Take it easy" master row that toggles all helpers in one tap,
followed by the per-game cheats curated by gpt-5 from the libretro database
on the card (see `tools/curate_cheats.py`). Peach, quiet — assist, not
"CHEATS!!!". Toggle state persists per-game in PlayState (
`enabled_cheats(gid)` / `toggle_cheat(gid, label)`) so the player's choices
survive across sessions — the on-device launch hook (slice CT.C) reads
this same state to materialize a .cht file at emulator start.

When the catalog has no curated cheats for this game (system not supported,
or no .cht file on the card), we render the empty state — same peach panel,
same eyebrow, but the list reads "no tweaks curated for this game yet" so
the player isn't left wondering why the menu is empty.
"""

from __future__ import annotations

import pygame

from ..design import tokens as t
from ..shared import fonts, render
from ..widgets import chrome, components as c, images

PANEL = pygame.Rect(40, 44, t.VIEWPORT_WIDTH - 80, 392)   # 44 .. 436
PEACH = t.Color.ACCENT_PEACH
ROW_H = 44
LIST_TOP = PANEL.y + 122          # below the header + clerk line (no overlap)
# Reserve the bottom strip for the "How to use" hint — shown when the
# focused row has `how_to_use` (Level Select needs an in-game menu visit,
# Debug needs a button combo, etc.). Empty for passive cheats; the strip
# stays blank then so list math doesn't shift.
HINT_AREA_H = 60
LIST_BOT = PANEL.bottom - HINT_AREA_H - 8
HINT_TOP = LIST_BOT + 4
HINT_BOT = PANEL.bottom - 8


class AssistScreen:
    name = "assist"

    def __init__(self, app, game):
        self.app = app
        self.game = game
        self.gid = game["id"]
        # Build the toggle list from the catalog's curated cheats:
        #   row 0 = "Take it easy" master (preset row — toggles all at once)
        #   row 1..N = one per curated cheat (label, why, persisted on-state)
        # Empty curated list → just the empty-state hint (no rows beyond the
        # preset, which becomes harmless — A toggles nothing meaningful but
        # the screen still navigates cleanly).
        ps = self.app.library.play_state()
        enabled = set(ps.enabled_cheats(self.gid))
        curated = self.app.catalog.cheats_for(self.gid)
        self.has_curated = bool(curated)
        rows: list[dict] = []
        rows.append({"name": "Take it easy", "desc": "turn on every helper at once",
                     "on": bool(curated) and all(c.get("label") in enabled for c in curated),
                     "preset": True})
        for ch in curated:
            label = ch.get("label") or "?"
            rows.append({"name": label,
                         "desc": ch.get("why") or "",
                         "on": label in enabled,
                         "label": label,                     # persistence key
                         # how_to_use: in-game activation steps for cheats
                         # that need a menu / button combo after enabling
                         # (Level Select, Debug, pause-tricks). Empty for
                         # passive cheats. Rendered below the toggle row
                         # when this cheat is focused.
                         "how_to_use": ch.get("how_to_use") or ""})
        self.toggles = rows
        self.sel = 0
        self.scroll = 0

    def on_action(self, action):
        if action == "up":
            self.sel = max(0, self.sel - 1)
        elif action == "down":
            self.sel = min(len(self.toggles) - 1, self.sel + 1)
        elif action == "a":
            self._toggle_current()
        elif action == "b":
            self.app.pop()

    def _toggle_current(self):
        tg = self.toggles[self.sel]
        ps = self.app.library.play_state()
        if tg.get("preset"):
            # Master row: invert the bundle — if any are off, turn all on;
            # if all are on, turn all off. Then sync individual rows.
            target_on = not tg["on"]
            for row in self.toggles:
                label = row.get("label")
                if not label:
                    continue
                if (label in ps.enabled_cheats(self.gid)) != target_on:
                    ps.toggle_cheat(self.gid, label)
                row["on"] = target_on
            tg["on"] = target_on
            self.app.toast(f"All helpers {'on' if target_on else 'off'}")
            return
        # Individual row
        label = tg.get("label")
        if not label:
            return
        now_on = ps.toggle_cheat(self.gid, label)
        tg["on"] = now_on
        # keep the master row in sync with the current bundle state
        enabled = set(ps.enabled_cheats(self.gid))
        labels = [r["label"] for r in self.toggles if r.get("label")]
        self.toggles[0]["on"] = bool(labels) and all(l in enabled for l in labels)
        self.app.toast(f"{tg['name']} {'on' if now_on else 'off'}")

    def update(self, dt_ms):
        pass

    def _update_scroll(self):
        avail = LIST_BOT - LIST_TOP
        maxs = max(0, len(self.toggles) * ROW_H - avail)
        self.scroll = max(0, min(maxs, max(0, (self.sel + 1) * ROW_H - avail)))

    def draw(self, surf):
        cat = self.app.catalog
        surf.blit(images.backdrop(cat.asset_path(self.game, "hero")
                                  or cat.asset_path(self.game, "box"),
                                  t.VIEWPORT_WIDTH, t.VIEWPORT_HEIGHT, darken=0.66), (0, 0))
        veil = pygame.Surface((t.VIEWPORT_WIDTH, t.VIEWPORT_HEIGHT), pygame.SRCALPHA)
        veil.fill(t.with_alpha(t.Color.BG_DEEP, 0.5))
        surf.blit(veil, (0, 0))

        panel = pygame.Surface((PANEL.w, PANEL.h), pygame.SRCALPHA)
        pygame.draw.rect(panel, t.with_alpha(t.Color.SURFACE_ELEVATED, 0.92),
                         (0, 0, PANEL.w, PANEL.h), border_radius=14)
        pygame.draw.rect(panel, t.with_alpha(PEACH, 0.22), (0, 0, PANEL.w, PANEL.h),
                         width=1, border_radius=14)
        surf.blit(panel, PANEL.topleft)

        x = PANEL.x + 20
        c.clerk_eyebrow(surf, x, PANEL.y + 16, "Clerk's tweaks · optional", color=PEACH)
        surf.blit(fonts.render(t.Type.HERO_CARD, "How do you want to play?",
                               t.Color.TEXT_PRIMARY), (x, PANEL.y + 36))
        # The paragraph adapts to whether we actually HAVE curated tweaks. The
        # "you're not doing it wrong" tone stays — empty state just acknowledges
        # the game has nothing curated yet (and never feels like a failure).
        if self.has_curated:
            paragraph = ("You loved these games. Play the way that brings you joy — "
                         "you're not doing it wrong.")
        else:
            paragraph = ("No tweaks curated for this game yet — most consoles have "
                         "useful ones, but a few are best played the original way.")
        c.draw_paragraph(surf, t.Type.CLERK, paragraph,
                         x, PANEL.y + 78, PANEL.w - 40,
                         t.Color.TEXT_SECONDARY, line_h=17, max_lines=2)

        self._update_scroll()
        clip = pygame.Rect(PANEL.x + 8, LIST_TOP, PANEL.w - 16, LIST_BOT - LIST_TOP)
        surf.set_clip(clip)
        if self.has_curated:
            for i, tg in enumerate(self.toggles):
                y = LIST_TOP + i * ROW_H - self.scroll
                if y + ROW_H - 6 < LIST_TOP or y > LIST_BOT:
                    continue
                self._row(surf, x, y, PANEL.right - 20 - x, tg, focused=(i == self.sel))
        else:
            # Empty state — quiet, no toggle rows drawn (preset row would be
            # misleading if there's nothing to bundle).
            hint = fonts.render(t.Type.CAPTION,
                                "Check back after the next catalog refresh.",
                                t.Color.TEXT_MUTED)
            surf.blit(hint, (x, LIST_TOP + 8))
        surf.set_clip(None)
        # rows that run under the fold fade into the PANEL (not a full-width
        # BG_DEEP band across the whole screen, which scroll_fade would draw)
        if len(self.toggles) * ROW_H > LIST_BOT - LIST_TOP:
            fade = pygame.Surface((PANEL.w - 16, 14), pygame.SRCALPHA)
            for fy in range(14):
                fade.fill(t.with_alpha(t.Color.SURFACE_ELEVATED, fy / 13.0),
                          (0, fy, PANEL.w - 16, 1))
            surf.blit(fade, (PANEL.x + 8, LIST_BOT - 14))

        # ── "How to use" hint strip for the focused row ──
        # Only Level Select / Debug Menu / etc. cheats need this; passive
        # ones (Invincibility, Infinite Lives) leave how_to_use empty and
        # the strip stays blank. The peach divider + eyebrow signals it
        # as the same "Clerk's tweaks" register, just zoomed into the
        # specific cheat the player is examining.
        focused_row = self.toggles[self.sel] if 0 <= self.sel < len(self.toggles) else None
        hint_text = (focused_row or {}).get("how_to_use", "")
        if self.has_curated and hint_text:
            pygame.draw.line(surf, t.with_alpha(PEACH, 0.22),
                             (PANEL.x + 20, HINT_TOP),
                             (PANEL.right - 20, HINT_TOP), 1)
            c.clerk_eyebrow(surf, PANEL.x + 20, HINT_TOP + 6,
                            "How to use", color=PEACH)
            c.draw_paragraph(surf, t.Type.CAPTION, hint_text,
                             PANEL.x + 20, HINT_TOP + 22, PANEL.w - 40,
                             t.Color.TEXT_PRIMARY, line_h=14, max_lines=3)

        chrome.footer_hints(surf, [("A", "Toggle"), ("B", "Back")])

    def _row(self, surf, x, y, w, tg, *, focused):
        H = 40
        r = pygame.Rect(x, y, w, H)
        body = pygame.Surface((w, H), pygame.SRCALPHA)
        tint = 0.06 if tg.get("preset") else 0.025
        pygame.draw.rect(body, t.with_alpha(PEACH if tg.get("preset") else t.Color.TEXT_PRIMARY,
                                            tint), (0, 0, w, H), border_radius=9)
        if not focused:
            pygame.draw.rect(body, t.with_alpha(t.Color.TEXT_PRIMARY, 0.06), (0, 0, w, H),
                             width=1, border_radius=9)
        # label + desc on the left (desc kept clear of the bottom border)
        body.blit(fonts.render(t.Type.LABEL, tg["name"], t.Color.TEXT_PRIMARY), (14, 4))
        body.blit(fonts.render(t.Type.TILE_SUB, tg["desc"], t.Color.TEXT_MUTED), (14, 21))
        # the single toggle switch, right-aligned (state IS the switch — no ON/OFF text)
        on = tg["on"]
        tw, th = 38, 18
        tgx, ty = w - 14 - tw, (H - th) // 2
        pygame.draw.rect(body, t.with_alpha(PEACH, 0.55 if on else 0.12), (tgx, ty, tw, th),
                         border_radius=999)
        if not on:
            pygame.draw.rect(body, t.with_alpha(t.Color.TEXT_PRIMARY, 0.18), (tgx, ty, tw, th),
                             width=1, border_radius=999)
        knob_x = tgx + tw - 9 if on else tgx + 9
        pygame.draw.circle(body, PEACH if on else t.Color.TEXT_MUTED, (knob_x, ty + th // 2), 7)
        if focused:
            glow, gpos = render.focus_glow(r, 9); surf.blit(glow, gpos)
        surf.blit(body, r.topleft)
        if focused:
            surf.blit(render.gradient_border((w, H), 9), r.topleft)
