"""
Library — L0 (all shelves) and L1 (per-system).

L0: Continue pinned on top, then one shelf per system (a little stack of your
boxes + the Clerk's pick). A on a shelf falls into L1.
L1: the system's featured pick as a 21:9 banner + a 3:4 box grid. Shoulders
(L/R) switch system; D-pad navigates; A opens the game card.

Both reachable via the global nav bar (Up → nav).
"""

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, nav, hero as herow

BODY_TOP = t.Space.STATUS_BAR_HEIGHT
BODY_BOT = t.VIEWPORT_HEIGHT - t.Space.FOOTER_HINT_HEIGHT
VIEWPORT_H = BODY_BOT - BODY_TOP
SAFE = t.Space.SAFE_AREA

# compact labels for system names too long for the 104px shelf-card tag pill
SYS_TAG = {"Master System": "Master Sys", "Game Boy Color": "GB Color",
           "Game Boy Advance": "GBA"}


# ════════════════════════════ L0 — all shelves (v3) ═════════════════════
class LibraryAll:
    """Continue split-hero on top + an edge-to-edge rail of system cards
    (each system shown by its top game's box, with a tag pill + game count)."""
    name = "library"

    HERO = pygame.Rect(0, BODY_TOP, t.VIEWPORT_WIDTH, 200)   # 28..228
    HEAD_Y = 236
    CARD_X = 14
    CARD_W, CARD_GAP, CARD_AH = 104, 1, 139                  # 3:4 art
    CARD_Y = 258

    def __init__(self, app):
        self.app = app
        cat = app.catalog
        # "Your shelves" = what's actually on the card (the library overlay), not
        # the editorial catalog. Continue only counts if that game is installed.
        cont = (cat.continue_entries() or [None])[0]
        self.cont = cont if (cont and cat.is_installed(cont["game"]["id"])) else None
        # systems present on the card, each with its top pick first
        self.systems = []
        for s in cat.library_systems():
            games = sorted(cat.library_by_system(s),
                           key=lambda g: (g.get("rating") or 0), reverse=True)
            if games:
                self.systems.append((s, games))
        self.lib_count = sum(len(g) for _, g in self.systems)   # cached (no per-frame rebuild)
        self.feat = self.cont["game"] if self.cont else (
            self.systems[0][1][0] if self.systems else None)
        self.nav = nav.NavBar("library")
        self.sel = 0 if self.cont else 1   # 0 = continue hero, 1.. = system cards
        self.start = 0

    # ── input ────────────────────────────────────────────────────────
    def on_action(self, action):
        if self.nav.handle(self.app, action):
            return
        n = len(self.systems)
        if action == "up":
            if self.sel == 0:
                self.nav.focus()
            elif self.cont:
                self.sel = 0
            else:
                self.nav.focus()
        elif action == "down":
            if self.sel == 0 and n:
                self.sel = 1
        elif action == "left":
            if self.sel > 1:
                self.sel -= 1
        elif action == "right":
            if 1 <= self.sel < n:
                self.sel += 1
        elif action in ("azprev", "aznext"):       # right stick → fast scroll the shelf rail
            if n:
                step = 5 if action == "aznext" else -5
                self.sel = max(1, min(n, (self.sel if self.sel >= 1 else 1) + step))
        elif action == "a":
            if self.sel == 0 and self.cont:
                self.app.open_game(self.cont["game"]["id"])
            elif self.sel >= 1 and self.systems:
                self.app.open_system(self.systems[self.sel - 1][0])
            elif not self.systems:
                self.app.open_addgames()
        elif action == "y":
            self.app.go_tab("search")
        elif action == "b":
            self.app.go_tab("home")

    def update(self, dt_ms):
        pass

    # ── draw ─────────────────────────────────────────────────────────
    def draw(self, surf):
        self.nav.draw(surf, app=self.app)
        cat = self.app.catalog
        navf = self.nav.focused
        if not self.systems:                       # a truly empty card
            self._draw_empty(surf)
            chrome.footer_hints(surf, [("A", "Add games"), ("B", "Home")])
            return
        self._draw_hero(surf, self.sel == 0 and not navf)
        # rail header
        c.rail_header(surf, self.CARD_X, self.HEAD_Y, "Your shelves",
                      f"{self.lib_count} games · {len(self.systems)} shelves",
                      max_w=t.VIEWPORT_WIDTH - 2 * self.CARD_X)
        self._draw_shelves(surf, self.sel >= 1 and not navf)
        chrome.footer_hints(surf, [("A", "Open shelf"), ("B", "Home"), ("Y", "Search")])

    def _draw_empty(self, surf):
        cx = t.VIEWPORT_WIDTH // 2
        msg = fonts.render(t.Type.TITLE, "No games on the card yet", t.Color.TEXT_SECONDARY)
        surf.blit(msg, (cx - msg.get_width() // 2, 200))
        sub = fonts.render(t.Type.BODY, "Drop ROMs on the SD card, or add from a source.",
                           t.Color.TEXT_MUTED)
        surf.blit(sub, (cx - sub.get_width() // 2, 232))

    def _draw_hero(self, surf, focused):
        if not self.feat:
            return
        g = self.feat
        cat = self.app.catalog
        if self.cont:
            eyebrow = "Pick up where you left off"
            meta = [g["system"], self.cont["playtime"], self.cont["last_played"]]
            cta = "Continue"
        else:
            eyebrow = "Your library"
            meta = [g["system"], (g.get("genre") or [None])[0]]
            cta = "Play"
        herow.split_hero(surf, self.HERO, art_path=cat.asset_path(g, "hero"),
                         eyebrow=eyebrow, title=g["title"], title_style=t.Type.HERO_CARD,
                         meta=meta, rating=None, quote=None,
                         cta_label=cta, cta_focused=focused, art_focus=(0.5, 0.35))

    def _draw_shelves(self, surf, active):
        n = len(self.systems)
        view = (t.VIEWPORT_WIDTH - self.CARD_X) // (self.CARD_W + self.CARD_GAP)
        sel_i = self.sel - 1 if self.sel >= 1 else 0
        if active:
            self.start = max(0, min(sel_i - view + 2, max(0, n - view)))
        else:
            self.start = 0
        for k in range(self.start, min(self.start + view + 1, n)):
            x = self.CARD_X + (k - self.start) * (self.CARD_W + self.CARD_GAP)
            system, games = self.systems[k]
            self._sys_card(surf, x, self.CARD_Y, system, games,
                           focused=(active and k == sel_i),
                           dim=(active and k != sel_i))

    def _sys_card(self, surf, x, y, system, games, *, focused, dim):
        # the same universal tile as everywhere else (lift + bright + dim)
        c.poster_tile(surf, pygame.Rect(x, y, self.CARD_W, self.CARD_AH),
                      path=self.app.catalog.asset_path(games[0], "box"),
                      name=system, sub=f"{len(games)} games", focused=focused, dim=dim)


# ════════════════════════════ L1 — per system (v3) ══════════════════════
class LibrarySystem:
    name = "library_system"

    # one scrolling page (content coords, 0 = top of body): a 240px featured
    # hero, then the grid. The hero scrolls AWAY as you go down — it is not
    # pinned, so the grid gets the full height when browsing (like Home).
    HERO_H = 200
    GRID_CY = HERO_H + 8                                # content-coord grid top
    GRID_W = t.VIEWPORT_WIDTH - 22                      # clean A–Z gutter (no overlap)
    COLS = 5
    TILE_W = GRID_W // COLS                             # ~123
    TILE_ART_H = int(TILE_W * 4 / 3)                    # ~164
    ROW_H = TILE_ART_H + 4 + 18 + 8                     # art (genre overlays it) + name + air

    def __init__(self, app, system):
        self.app = app
        self.system = system
        self.nav = nav.NavBar("library")
        self._load()

    def _load(self):
        cat = self.app.catalog
        games = cat.library_by_system(self.system)        # the card's games for this system
        # hero = the Clerk's top pick; grid = A–Z so the index is truthful + jumpable
        self.featured = max(games, key=lambda g: (g.get("rating") or 0)) if games else None
        self.games = sorted(games, key=lambda g: g["title"].lower())
        self.row = 0          # 0 = feat, 1 = grid
        self.col = 0
        self.scroll = 0

    def _az_jump(self, direction):
        """Right-stick fast-jump: move the grid selection to the next/prev
        initial-letter group (the grid is A–Z sorted)."""
        if not self.games:
            return
        self.row = 1
        inits = [g["title"][:1].upper() for g in self.games]
        cur = inits[self.col]
        if direction > 0:
            for i in range(self.col + 1, len(self.games)):
                if inits[i] != cur:
                    self.col = i
                    return
            self.col = len(self.games) - 1
        else:
            s = self.col
            while s > 0 and inits[s - 1] == cur:        # to start of current group
                s -= 1
            if s == 0:
                self.col = 0
                return
            prev = inits[s - 1]                          # to start of previous group
            j = s - 1
            while j > 0 and inits[j - 1] == prev:
                j -= 1
            self.col = j

    def _switch(self, delta):
        syss = self.app.catalog.library_systems()          # only systems on the card
        if self.system in syss and len(syss) > 1:
            self.system = syss[(syss.index(self.system) + delta) % len(syss)]
            self._load()

    # ── input ────────────────────────────────────────────────────────
    def on_action(self, action):
        if self.nav.handle(self.app, action):
            return
        if action in ("azprev", "aznext"):
            self._az_jump(1 if action == "aznext" else -1)
        elif action in ("l", "r"):
            self._switch(-1 if action == "l" else 1)
        elif action == "up":
            if self.row == 0:
                self.nav.focus()
            elif self.col // self.COLS == 0:
                self.row = 0
            else:
                self.col -= self.COLS
        elif action == "down":
            if self.row == 0 and self.games:
                self.row = 1
            elif self.row == 1 and self.col + self.COLS < len(self.games):
                self.col += self.COLS
        elif action == "left":
            if self.row == 1 and self.col % self.COLS:
                self.col -= 1
        elif action == "right":
            if self.row == 1 and self.col % self.COLS < self.COLS - 1 \
                    and self.col < len(self.games) - 1:
                self.col += 1
        elif action == "a":
            g = self._focused()
            if g:
                self.app.open_game(g["id"])
        elif action == "y":
            self.app.go_tab("search")
        elif action == "b":
            self.app.pop()

    def _focused(self):
        if not self.games:
            return None
        return self.featured if self.row == 0 else self.games[self.col]

    def update(self, dt_ms):
        pass

    # ── scroll (page: hero + grid scroll together) ───────────────────
    def _update_scroll(self):
        rows = (len(self.games) + self.COLS - 1) // self.COLS
        content_h = self.GRID_CY + rows * self.ROW_H
        body_h = BODY_BOT - BODY_TOP
        maxs = max(0, content_h - body_h)
        if self.row == 0:
            self.scroll = 0
            return
        r = self.col // self.COLS
        top = self.GRID_CY + r * self.ROW_H
        bot = top + self.TILE_ART_H + 18 + 8
        if top - 8 < self.scroll:
            self.scroll = top - 8
        if bot + 8 > self.scroll + body_h:
            self.scroll = bot + 8 - body_h
        self.scroll = max(0, min(self.scroll, maxs))

    # ── draw ─────────────────────────────────────────────────────────
    def draw(self, surf):
        self.nav.draw(surf, app=self.app)
        cat = self.app.catalog
        if not self.games:
            msg = fonts.render(t.Type.TITLE, f"No {self.system} games yet",
                               t.Color.TEXT_SECONDARY)
            surf.blit(msg, ((t.VIEWPORT_WIDTH - msg.get_width()) // 2, 220))
            chrome.footer_hints(surf, [("L1/R1", "System"), ("B", "Back")])
            return
        self._update_scroll()
        clip = pygame.Rect(0, BODY_TOP, t.VIEWPORT_WIDTH, BODY_BOT - BODY_TOP)
        surf.set_clip(clip)
        # featured hero — scrolls up with the page (not pinned)
        hy = BODY_TOP - self.scroll
        if hy + self.HERO_H > BODY_TOP:
            g = self.featured
            herow.split_hero(surf, pygame.Rect(0, hy, t.VIEWPORT_WIDTH, self.HERO_H),
                             art_path=cat.asset_path(g, "hero"),
                             eyebrow=f"Essential for the {self.system} shelf",
                             title=g["title"], title_style=t.Type.HERO_LIB,
                             meta=[g.get("year"), (g.get("genre") or [None])[0]],
                             rating=g.get("rating"),
                             quote=c.first_sentence(g.get("description", ""), cap=150),
                             quote_lines=3, cta_label="Play",
                             cta_focused=(self.row == 0 and not self.nav.focused),
                             art_focus=(0.5, 0.30))
        self._draw_grid(surf)
        surf.set_clip(None)
        c.scroll_fade(surf, bottom=BODY_BOT)
        foc = self._focused()
        az_top = max(BODY_TOP, BODY_TOP + self.GRID_CY - self.scroll)
        c.az_index(surf, top=az_top, bottom=BODY_BOT,
                   current=(foc["title"][:1] if foc else None))
        chrome.footer_hints(surf, [("A", "Play"), ("B", "Back"),
                                   ("L1/R1", "System"), ("L2/R2", "A-Z")])

    def _draw_grid(self, surf):
        cat = self.app.catalog
        active = (self.row == 1 and not self.nav.focused)
        focused_xy = None
        for i, g in enumerate(self.games):
            r, ccol = divmod(i, self.COLS)
            x = ccol * (self.TILE_W + 1)
            y = BODY_TOP + self.GRID_CY + r * self.ROW_H - self.scroll
            if y + self.TILE_ART_H < BODY_TOP or y > BODY_BOT:
                continue
            if active and i == self.col:
                focused_xy = (g, x, y)
                continue
            c.poster_tile(surf, pygame.Rect(x, y, self.TILE_W, self.TILE_ART_H),
                          path=cat.asset_path(g, "box"), name=g["title"],
                          badge=c.rating_badge(g.get("rating")), badge_r=c.sys_year(g),
                          chips=[((g.get("genre") or [None])[0], t.Color.TEXT_SECONDARY)],
                          sub=None, dim=active)
        if focused_xy:
            g, x, y = focused_xy
            c.poster_tile(surf, pygame.Rect(x, y, self.TILE_W, self.TILE_ART_H),
                          path=cat.asset_path(g, "box"), name=g["title"],
                          badge=c.rating_badge(g.get("rating")), badge_r=c.sys_year(g),
                          chips=[((g.get("genre") or [None])[0], t.Color.TEXT_SECONDARY)],
                          sub=None, focused=True)
