"""
Pm1 — PortMaster Health.

Recreates the Claude Design handoff (Pm1-portmaster.html) in pygame. A
consumer-first health view for installed ports: each row tells the owner,
in plain words, whether a game is Ready, needs a data file, needs its
runtime, or didn't launch last time — never "harbourmaster" or "runtime
sha". Status filters across the top narrow the list.

  status bar : back chevron + "Ports" · battery · clock
  hero       : play glyph + "N ports installed" + needs-attention summary
  filters    : pill row (All / Ready / Needs setup / Recently played) with
               counts, plus a "checked N min ago" meta
  list       : slim per-port rows — color-coded cover initials, title, a
               state-specific colored subline, a status pill, and a chevron
  footer     : A fix · X show last error · Y repair runtimes · B back

Class ⇄ handoff:
  Pm1Ports ⇄ Pm1-portmaster.html

The filter row is driven by the L / R shoulder buttons so the D-pad up/down
stays dedicated to the list. Screen shape mirrors the other modules: name,
__init__(self, app), on_action(self, action), update(self, dt_ms),
draw(self, surf). This module only defines the screen + local helpers; it
never edits main.py or widgets.py.
"""

from __future__ import annotations

import pygame

from ..design import tokens as t
from ..shared import widgets as w
from ..shared import fonts, icons, render

SAFE = t.Space.SAFE_AREA
STAGE_W = t.VIEWPORT_WIDTH
STAGE_H = t.VIEWPORT_HEIGHT
BODY_W = STAGE_W - SAFE * 2  # 608

BODY_TOP = t.Space.STATUS_BAR_HEIGHT               # 48
BODY_BOTTOM = STAGE_H - t.Space.FOOTER_HINT_HEIGHT  # 448

# Status → (pill color, subline color). Consumer states only.
_STATUS_COL = {
    "ok":   t.Color.SUCCESS,
    "warn": t.Color.ACCENT_PEACH,
    "err":  t.Color.ERROR,
}

# Cover-initials tint keyed by a short code (matches .cover .cy/.rs/.pe/.mt).
_COVER_TINT = {
    "cy": t.Color.ACCENT_CYAN,
    "rs": t.Color.ACCENT_ROSE,
    "pe": t.Color.ACCENT_PEACH,
    "mt": t.Color.SUCCESS,
}

PORT_ROW_H = 48
PORT_ROW_GAP = 5


# ─── module-level helpers ─────────────────────────────────────────────


def _cover(initials: str, tint_key: str) -> pygame.Surface:
    """
    36px rounded cover with bold initials, tinted by status. Mirrors
    .port-row .cover — a tinted translucent fill + matching border + glyph.
    """
    box = 36
    tint = _COVER_TINT.get(tint_key)
    s = pygame.Surface((box, box), pygame.SRCALPHA)
    if tint is None:
        pygame.draw.rect(s, t.Color.BG_MID, (0, 0, box, box), border_radius=7)
        pygame.draw.rect(s, t.Color.BORDER_SOFT, (0, 0, box, box),
                         width=1, border_radius=7)
        col = t.Color.TEXT_SECONDARY
    else:
        pygame.draw.rect(s, t.with_alpha(tint, 0.10), (0, 0, box, box),
                         border_radius=7)
        pygame.draw.rect(s, t.with_alpha(tint, 0.30), (0, 0, box, box),
                         width=1, border_radius=7)
        col = tint
    lab = fonts.render(t.TextStyle(t.SPACE_GROTESK_BOLD, 13, "Bold", 0.0),
                       initials, col)
    s.blit(lab, ((box - lab.get_width()) // 2, (box - lab.get_height()) // 2))
    return s


def _status_pill(text: str, level: str) -> pygame.Surface:
    """Mono status pill (ready / missing data / runtime / controls)."""
    col = _STATUS_COL.get(level, t.Color.TEXT_SECONDARY)
    lab = fonts.render(t.Type.META_SM, text, col)
    pw, ph = lab.get_width() + 18, 18
    pill = pygame.Surface((pw, ph), pygame.SRCALPHA)
    pygame.draw.rect(pill, t.with_alpha(col, 0.14), (0, 0, pw, ph),
                     border_radius=999)
    pygame.draw.rect(pill, t.with_alpha(col, 0.38), (0, 0, pw, ph),
                     width=1, border_radius=999)
    pill.blit(lab, ((pw - lab.get_width()) // 2, (ph - lab.get_height()) // 2))
    return pill


def _port_row(surf, rect, port, *, focused=False):
    """
    Slim port row: cover initials + title/subline stack + status pill +
    chevron. Mirrors .port-row. Focused gets the elevated gradient + glow.
    """
    radius = t.Radius.BUTTON
    if focused:
        body = render.rounded_gradient((rect.w, rect.h), t.Color.SURFACE_ELEVATED,
                                       t.Color.SURFACE_BASE, radius, 160)
    else:
        body = render.rounded_gradient((rect.w, rect.h), t.Color.SURFACE_BASE,
                                       t.Color.BG_MID, radius, 160)
        pygame.draw.rect(body, t.Color.BORDER_SOFT, (0, 0, rect.w, rect.h),
                         width=1, border_radius=radius)

    pad = 12
    cy = rect.h // 2
    # cover
    cov = _cover(port["initials"], port.get("tint", ""))
    body.blit(cov, (pad, cy - cov.get_height() // 2))

    # right cluster (measured right→left): chevron then status pill
    rx = rect.w - pad
    chev = icons.icon("ph-caret-right", 12, t.Color.TEXT_MUTED)
    rx -= chev.get_width()
    body.blit(chev, (rx, cy - chev.get_height() // 2))
    rx -= 10
    pill = _status_pill(port["status"], port["level"])
    rx -= pill.get_width()
    body.blit(pill, (rx, cy - pill.get_height() // 2))
    rx -= 10

    # title + subline stack, clipped to the space before the pill cluster
    tx = pad + cov.get_width() + 10
    avail = max(10, rx - tx)
    ttl = fonts.render(t.TextStyle(t.INTER_MEDIUM, 13, "Medium", 0.0),
                       port["title"], t.Color.TEXT_PRIMARY)
    if ttl.get_width() > avail:
        ttl = ttl.subsurface((0, 0, avail, ttl.get_height()))
    sub_col = _STATUS_COL.get(port["level"], t.Color.TEXT_MUTED)
    sub = fonts.render(t.Type.META_SM, port["sub"], sub_col)
    if sub.get_width() > avail:
        sub = sub.subsurface((0, 0, avail, sub.get_height()))
    stack_h = ttl.get_height() + 2 + sub.get_height()
    sy = (rect.h - stack_h) // 2
    body.blit(ttl, (tx, sy))
    body.blit(sub, (tx, sy + ttl.get_height() + 2))

    if focused:
        w.draw_focused_card(surf, rect, radius, scale=1.0, base_surface=body)
    else:
        surf.blit(body, rect.topleft)


def _filters(surf, y, filters, active_idx) -> int:
    """
    Filter pill row with counts + a right-aligned "checked …" meta. The
    active pill gets the rose→cyan tint. Mirrors .pm-filters. Returns the
    baseline y just below the row.
    """
    h = 22
    x = SAFE
    gap = 6
    for i, (label, count) in enumerate(filters):
        on = (i == active_idx)
        lab = fonts.render(t.TextStyle(t.INTER_REGULAR, 11, "Regular", 0.0),
                           label,
                           t.Color.TEXT_PRIMARY if on else t.Color.TEXT_SECONDARY)
        cnt = fonts.render(t.Type.META_SM, str(count),
                           t.Color.ACCENT_ROSE if on else t.Color.TEXT_MUTED)
        pw = 11 + lab.get_width() + 5 + cnt.get_width() + 11
        if on:
            pill = render.rounded_gradient(
                (pw, h), t.with_alpha(t.Color.ACCENT_ROSE, 0.18)[:3],
                t.with_alpha(t.Color.ACCENT_CYAN, 0.18)[:3], 999, 135)
            pygame.draw.rect(pill, t.with_alpha(t.Color.ACCENT_ROSE, 0.40),
                             (0, 0, pw, h), width=1, border_radius=999)
        else:
            pill = pygame.Surface((pw, h), pygame.SRCALPHA)
            pygame.draw.rect(pill, t.with_alpha(t.Color.BG_DEEP, 0.40),
                             (0, 0, pw, h), border_radius=999)
            pygame.draw.rect(pill, t.Color.BORDER_SOFT, (0, 0, pw, h),
                             width=1, border_radius=999)
        cxp = 11
        pill.blit(lab, (cxp, (h - lab.get_height()) // 2))
        cxp += lab.get_width() + 5
        pill.blit(cnt, (cxp, (h - cnt.get_height()) // 2 + 1))
        surf.blit(pill, (x, y))
        x += pw + gap

    meta = fonts.render(t.Type.META_SM, "checked 2 min ago", t.Color.TEXT_MUTED)
    surf.blit(meta, (STAGE_W - SAFE - meta.get_width(),
                     y + (h - meta.get_height()) // 2))
    return y + h


def _hero(surf, y, *, title, sub_segments, big, big_label):
    """
    Pm1 hero band: peach-tinted play glyph + title + colored subline, and a
    right-aligned count. Returns the divider baseline y. Mirrors .pm-hero.
    """
    box = 44
    chip = pygame.Surface((box, box), pygame.SRCALPHA)
    pygame.draw.rect(chip, t.with_alpha(t.Color.ACCENT_PEACH, 0.18),
                     (0, 0, box, box), border_radius=11)
    pygame.draw.rect(chip, t.with_alpha(t.Color.ACCENT_PEACH, 0.40),
                     (0, 0, box, box), width=1, border_radius=11)
    gic = icons.icon("ph-play-circle", 24, t.Color.ACCENT_PEACH)
    chip.blit(gic, ((box - 24) // 2, (box - 24) // 2))
    surf.blit(chip, (SAFE, y))

    # right: big count + small caption
    rx = STAGE_W - SAFE
    big_s = fonts.render(t.TextStyle(t.SPACE_GROTESK_BOLD, 22, "Bold", -0.4),
                         str(big), t.Color.TEXT_PRIMARY)
    surf.blit(big_s, (rx - big_s.get_width(), y + 2))
    cap = fonts.render(t.Type.META_SM, big_label, t.Color.TEXT_MUTED)
    surf.blit(cap, (rx - cap.get_width(), y + 2 + big_s.get_height() + 3))
    tally_right = rx - max(big_s.get_width(), cap.get_width())

    tx = SAFE + box + 14
    title_s = fonts.render(t.TextStyle(t.SPACE_GROTESK_BOLD, 20, "Bold", -0.5),
                           title, t.Color.TEXT_PRIMARY)
    tmax = tally_right - 14 - tx
    if title_s.get_width() > tmax:
        title_s = title_s.subsurface((0, 0, max(tmax, 1), title_s.get_height()))
    surf.blit(title_s, (tx, y + 1))

    sy = y + 1 + title_s.get_height() + 5
    sx = tx
    for text, color, strong in sub_segments:
        style = (t.TextStyle(t.INTER_SEMIBOLD, 12, "SemiBold", 0.0)
                 if strong else t.Type.BODY_SM)
        seg = fonts.render(style, text, color)
        if sx + seg.get_width() > tally_right - 14:
            seg = seg.subsurface(
                (0, 0, max(1, tally_right - 14 - sx), seg.get_height()))
            surf.blit(seg, (sx, sy))
            break
        surf.blit(seg, (sx, sy))
        sx += seg.get_width()

    div_y = y + box + 8
    pygame.draw.line(surf, t.Color.BORDER_SOFT, (SAFE, div_y),
                     (STAGE_W - SAFE, div_y), 1)
    return div_y


# ═══════════════════════════════════════════════════════════════════════
# Pm1 — PortMaster Health
# ═══════════════════════════════════════════════════════════════════════


class Pm1Ports:
    """Per-port health list with status filters; consumer-first verdicts."""
    name = "ports"

    # filter id ⇄ which port levels/flags it shows
    _FILTERS = ["all", "ready", "needs", "recent"]

    def __init__(self, app):
        self.app = app
        self.focus = 0
        self.filter_idx = 0
        self._scroll = 0

        self.total_installed = 18
        self.hero_title = "18 ports installed"
        self.hero_sub = [
            ("3 need attention — ", t.Color.TEXT_SECONDARY, False),
            ("1 missing data, 2 last launch failed", t.Color.ACCENT_PEACH, True),
        ]

        # Each port: cover initials + tint, title, consumer subline (colored
        # by level), terse status pill + level, plus flags used for filtering.
        # level ∈ {ok, warn, err}; "recent" marks recently-played entries.
        self.ports = [
            dict(initials="SV", tint="pe", title="Stardew Valley",
                 sub="needs game files in /roms/ports/stardew",
                 status="missing data", level="warn",
                 group="needs", recent=False),
            dict(initials="UT", tint="rs", title="Undertale",
                 sub="last launch crashed · runtime missing",
                 status="runtime", level="err",
                 group="needs", recent=False),
            dict(initials="CV", tint="cy", title="Cave Story",
                 sub="ready · last played 2 h ago",
                 status="ready", level="ok",
                 group="ready", recent=True),
            dict(initials="CK", tint="mt", title="Commander Keen",
                 sub="ready · last played 4 d ago",
                 status="ready", level="ok",
                 group="ready", recent=True),
            dict(initials="VC", tint="rs", title="Vice City",
                 sub="last launch crashed · controls config issue",
                 status="controls", level="err",
                 group="needs", recent=False),
            dict(initials="DI", tint="pe", title="Diablo I",
                 sub="ready · MPQ file detected",
                 status="ready", level="ok",
                 group="ready", recent=False),
        ]

    # ── derived ─────────────────────────────────────────────────────────
    def _filter_defs(self):
        ready = sum(1 for p in self.ports if p["group"] == "ready")
        needs = sum(1 for p in self.ports if p["group"] == "needs")
        recent = sum(1 for p in self.ports if p["recent"])
        return [("All", self.total_installed), ("Ready", ready),
                ("Needs setup", needs), ("Recently played", recent)]

    def _visible_ports(self):
        f = self._FILTERS[self.filter_idx]
        if f == "all":
            return self.ports
        if f == "ready":
            return [p for p in self.ports if p["group"] == "ready"]
        if f == "needs":
            return [p for p in self.ports if p["group"] == "needs"]
        if f == "recent":
            return [p for p in self.ports if p["recent"]]
        return self.ports

    # ── input ─────────────────────────────────────────────────────────
    def on_action(self, action):
        vis = self._visible_ports()
        n = len(vis)
        if action == "down":
            self.focus = min(max(0, n - 1), self.focus + 1)
        elif action == "up":
            self.focus = max(0, self.focus - 1)
        elif action in ("l", "left"):
            self.filter_idx = (self.filter_idx - 1) % len(self._FILTERS)
            self.focus = 0
            self._scroll = 0
        elif action in ("r", "right"):
            self.filter_idx = (self.filter_idx + 1) % len(self._FILTERS)
            self.focus = 0
            self._scroll = 0
        elif action == "a":
            if vis:
                self.app.toast(f"{vis[self.focus]['title']} — opening fix")
        elif action == "x":
            if vis:
                p = vis[self.focus]
                if p["level"] == "ok":
                    self.app.toast(f"{p['title']} — no recent errors")
                else:
                    self.app.toast(f"{p['title']} — {p['sub']}")
        elif action == "y":
            self.app.toast("Repairing runtimes…")
        elif action == "b":
            self.app.pop()

    def update(self, dt_ms):
        pass

    # ── draw ──────────────────────────────────────────────────────────
    def draw(self, surf):
        w.status_bar(surf, title="Ports", back=True)

        div_y = _hero(surf, 56, title=self.hero_title, sub_segments=self.hero_sub,
                      big=self.total_installed, big_label="ports")

        fy = _filters(surf, div_y + 8, self._filter_defs(), self.filter_idx)

        vis = self._visible_ports()
        if self.focus > max(0, len(vis) - 1):
            self.focus = max(0, len(vis) - 1)

        list_top = fy + 8
        list_bottom = BODY_BOTTOM - 6
        view_h = list_bottom - list_top

        stride = PORT_ROW_H + PORT_ROW_GAP
        content_h = len(vis) * stride - PORT_ROW_GAP if vis else 0

        # keep focused row visible
        f_top = self.focus * stride
        f_bot = f_top + PORT_ROW_H
        if f_top - self._scroll < 0:
            self._scroll = f_top
        elif f_bot - self._scroll > view_h:
            self._scroll = f_bot - view_h
        max_scroll = max(0, content_h - view_h)
        self._scroll = max(0, min(self._scroll, max_scroll))

        prev_clip = surf.get_clip()
        surf.set_clip(pygame.Rect(SAFE, list_top, BODY_W, view_h))
        if not vis:
            empty = fonts.render(t.Type.BODY_SM, "No ports in this filter",
                                 t.Color.TEXT_MUTED)
            surf.blit(empty, (SAFE + (BODY_W - empty.get_width()) // 2,
                              list_top + 12))
        for i, port in enumerate(vis):
            ry = list_top + i * stride - self._scroll
            if ry + PORT_ROW_H < list_top or ry > list_bottom:
                continue
            rect = pygame.Rect(SAFE, ry, BODY_W, PORT_ROW_H)
            _port_row(surf, rect, port, focused=(self.focus == i))
        surf.set_clip(prev_clip)

        if self._scroll < max_scroll:
            car = icons.icon("ph-caret-down", 12, t.Color.TEXT_MUTED)
            surf.blit(car, (STAGE_W // 2 - car.get_width() // 2,
                            list_bottom - car.get_height()))

        w.footer_hints(surf, [("A", "fix"), ("X", "last error"),
                              ("Y", "repair"), ("B", "back")])
