"""
§7 — Series / franchises.

SeriesIndex: a grid of series cards (name + count + a representative box).
SeriesDetail: the parts of a series in order, with your progress on each.
Reached from Discover ("Series & franchises") and the game card's series row.
"""

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

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


class SeriesIndex:
    """v3 — a poster grid of franchises (representative box + name + count),
    edge-to-edge with the rose→cyan focus glow + a right-edge A–Z fast-jump."""
    name = "series_index"
    COLS = 5
    HEAD_H = 26
    GRID_TOP = BODY_TOP + HEAD_H
    GRID_W = t.VIEWPORT_WIDTH - 22                  # leave a clean A–Z gutter
    TILE_W = GRID_W // COLS                         # ~123
    TILE_ART_H = int(TILE_W * 4 / 3)               # ~164
    ROW_H = TILE_ART_H + 3 + 17 + 13 + 11           # art + name + count + air (no overlap)

    def __init__(self, app):
        self.app = app
        self.nav = nav.NavBar("discover")
        # A–Z sorted so the right-edge index is truthful + right-stick jumpable
        self.groups = sorted(app.catalog.series_groups().items(),
                             key=lambda kv: kv[0].lower())          # [(name, games)]
        self.sel = 0
        self.scroll = 0

    def _az_jump(self, direction):
        if not self.groups:
            return
        inits = [n[:1].upper() for n, _ in self.groups]
        cur = inits[self.sel]
        if direction > 0:
            for i in range(self.sel + 1, len(self.groups)):
                if inits[i] != cur:
                    self.sel = i
                    return
            self.sel = len(self.groups) - 1
        else:
            s = self.sel
            while s > 0 and inits[s - 1] == cur:
                s -= 1
            if s == 0:
                self.sel = 0
                return
            prev = inits[s - 1]
            j = s - 1
            while j > 0 and inits[j - 1] == prev:
                j -= 1
            self.sel = j

    def on_action(self, action):
        if self.nav.handle(self.app, action):
            return
        n = len(self.groups)
        if action in ("azprev", "aznext"):
            self._az_jump(1 if action == "aznext" else -1)
            return
        if action == "up":
            if self.sel < self.COLS:
                self.nav.focus()
            else:
                self.sel -= self.COLS
        elif action == "down":
            if self.sel + self.COLS <= n - 1:
                self.sel += self.COLS
        elif action == "left":
            if self.sel % self.COLS:
                self.sel -= 1
        elif action == "right":
            if self.sel % self.COLS < self.COLS - 1 and self.sel < n - 1:
                self.sel += 1
        elif action == "a" and self.groups:
            self.app.push(SeriesDetail(self.app, self.groups[self.sel][0],
                                       self.groups[self.sel][1]))
        elif action == "b":
            self.app.pop()

    def update(self, dt_ms):
        pass

    def draw(self, surf):
        self.nav.draw(surf, app=self.app)
        navf = self.nav.focused
        # header
        surf.blit(fonts.render(t.Type.ROW_TITLE, "Series & franchises", t.Color.TEXT_PRIMARY),
                  (16, BODY_TOP + 4))
        cnt = fonts.render(t.Type.TILE_SUB, f"{len(self.groups)} franchises", t.Color.TEXT_MUTED)
        surf.blit(cnt, (t.VIEWPORT_WIDTH - 24 - cnt.get_width(), BODY_TOP + 8))
        # scroll
        rows = (len(self.groups) + self.COLS - 1) // self.COLS
        avail = BODY_BOT - self.GRID_TOP
        maxs = max(0, rows * self.ROW_H - avail)
        srow = self.sel // self.COLS
        self.scroll = max(0, min(maxs, max(0, (srow + 1) * self.ROW_H - avail)))
        clip = pygame.Rect(0, self.GRID_TOP, t.VIEWPORT_WIDTH, avail)
        surf.set_clip(clip)
        focused_xy = None
        for i, (name, games) in enumerate(self.groups):
            row, col = divmod(i, self.COLS)
            x = col * (self.TILE_W + 1)
            y = self.GRID_TOP + row * self.ROW_H - self.scroll
            if y + self.TILE_ART_H < self.GRID_TOP or y > BODY_BOT:
                continue
            if (i == self.sel and not navf):
                focused_xy = (x, y, name, games)
                continue
            self._card(surf, x, y, name, games, focused=False, dim=(not navf))
        if focused_xy:
            self._card(surf, *focused_xy, focused=True)
        surf.set_clip(None)
        c.scroll_fade(surf, bottom=BODY_BOT)
        foc = self.groups[self.sel][0] if self.groups else None
        c.az_index(surf, top=self.GRID_TOP, bottom=BODY_BOT,
                   current=(foc[:1] if foc else None))
        chrome.footer_hints(surf, [("A", "Open series"), ("B", "Back"), ("L2/R2", "A-Z")])

    def _card(self, surf, x, y, name, games, *, focused, dim=False):
        ar = pygame.Rect(x, y, self.TILE_W, self.TILE_ART_H)
        c.poster_tile(surf, ar, path=self.app.catalog.asset_path(games[0], "box"),
                      name=f"{name} series", sub=f"{len(games)} games",
                      focused=focused, dim=dim)


class SeriesDetail:
    name = "series_detail"
    ROW_H = 64
    TOP = 44

    def __init__(self, app, title, games=None):
        self.app = app
        self.nav = nav.NavBar("discover")
        self.series = title
        self.games = (games if games is not None
                      else app.catalog.series_groups().get(title, []))
        self.sel = 0
        self.scroll = 0

    def on_action(self, action):
        if self.nav.handle(self.app, action):
            return
        if action == "up":
            if self.sel == 0:
                self.nav.focus()
            else:
                self.sel -= 1
        elif action == "down":
            self.sel = min(len(self.games) - 1, self.sel + 1)
        elif action in ("azprev", "aznext"):       # right stick → fast scroll the list
            if self.games:
                step = 5 if action == "aznext" else -5
                self.sel = max(0, min(len(self.games) - 1, self.sel + step))
        elif action == "a" and self.games:
            self.app.open_game(self.games[self.sel]["id"])
        elif action == "b":
            self.app.pop()

    def update(self, dt_ms):
        pass

    def draw(self, surf):
        self.nav.draw(surf, app=self.app)
        navf = self.nav.focused
        # header
        ttl = fonts.render(t.Type.TITLE, self.series, t.Color.TEXT_PRIMARY)
        surf.blit(ttl, (16, BODY_TOP + 8))
        sub = fonts.render(t.Type.CAPTION, f"{len(self.games)} games · release order",
                           t.Color.TEXT_MUTED)
        surf.blit(sub, (16, BODY_TOP + 8 + ttl.get_height() + 2))
        list_top = BODY_TOP + self.TOP
        avail = BODY_BOT - list_top
        content_h = len(self.games) * (self.ROW_H + 6)
        maxs = max(0, content_h - avail)
        self.scroll = max(0, min(maxs, max(0, (self.sel + 1) * (self.ROW_H + 6) - avail)))
        clip = pygame.Rect(0, list_top, t.VIEWPORT_WIDTH, avail)
        surf.set_clip(clip)
        for i, g in enumerate(self.games):
            y = list_top + i * (self.ROW_H + 6) - self.scroll
            if y + self.ROW_H < list_top or y > BODY_BOT:
                continue
            self._row(surf, y, i, g, focused=(i == self.sel and not navf))
        surf.set_clip(None)
        chrome.footer_hints(surf, [("A", "Open"), ("B", "Back")])

    def _row(self, surf, y, i, g, *, focused):
        r = pygame.Rect(16, y, 608, self.ROW_H)
        body = render.rounded_gradient((r.w, r.h), t.Color.SURFACE_BASE, t.Color.BG_MID,
                                       t.Radius.BUTTON, 160)
        if not focused:
            pygame.draw.rect(body, t.Color.BORDER_SOFT, (0, 0, r.w, r.h), width=1,
                             border_radius=t.Radius.BUTTON)
        # no part number — chronological order tells the sequence; box leads the row
        box = images.cover(self.app.catalog.asset_path(g, "box"), 36, 48, radius=4,
                           mode="contain")
        body.blit(box, (16, (r.h - 48) // 2))
        tx = 64
        # meta sits right-aligned; ellipsize the title so it never collides with it
        meta = " · ".join(str(x) for x in [g["system"], g.get("year")] if x)
        ms = fonts.render(t.Type.TILE_SUB, meta, t.Color.TEXT_MUTED)
        title_max = r.w - tx - ms.get_width() - 16
        nm = fonts.render(t.Type.ROW_TITLE, c._ellipsize(t.Type.ROW_TITLE, g["title"], title_max),
                          t.Color.TEXT_PRIMARY)
        body.blit(nm, (tx, 12))
        ent = self.app.catalog.continue_entry(g["id"])
        status = (f"Continue · {ent['playtime']}" if ent else
                  "In your library")
        scol = t.Color.ACCENT_CYAN if ent else t.Color.TEXT_MUTED
        body.blit(fonts.render(t.Type.CAPTION, status, scol), (tx, 36))
        body.blit(ms, (r.w - 16 - ms.get_width(), (r.h - ms.get_height()) // 2))
        if focused:
            glow, gpos = render.focus_glow(r, t.Radius.BUTTON); surf.blit(glow, gpos)
        surf.blit(body, r.topleft)
        if focused:
            surf.blit(render.gradient_border((r.w, r.h), t.Radius.BUTTON), r.topleft)
