"""
Notifications feed — built from REAL state, not mock.

The drawer used to show hardcoded items ("Aria of Sorrow · 4.2 MB",
"AAhead 1.4.0 installed"). This builds the feed from what we actually
know:

  1. Library growth — games installed since the player last opened the
     drawer ("55 games joined your shelf"). First run shows a one-shot
     welcome instead of screaming "977 new".
  2. In progress — the continue list ("3 games in progress · pick up X").
  3. The Clerk's pick — a deterministic high-rated game the player hasn't
     started, deep-linked to its card.

Layering: this lives in `library/` (pure logic over Catalog + PlayState),
so both the NotificationsScreen and the NavBar bell can import it without
a screen↔widget cycle.

`unread_count()` drives the bell's dot — it counts only the "actionable
new" items (library growth / first-run welcome). The in-progress and
Clerk-pick items are evergreen ambient content; they don't light the dot.
"""

from __future__ import annotations


def _installed_ids(catalog) -> set:
    """The games actually on the card (library overlay) if set, else the
    whole catalog (dev box before set_library runs)."""
    if getattr(catalog, "library_ready", False) and catalog.library_order:
        return set(catalog.library_order)
    return set(catalog.by_id)


def _first_sentence(text: str, cap: int = 90) -> str:
    """First sentence of a description, capped — no widget dependency."""
    t = (text or "").strip()
    if not t:
        return ""
    for end in (". ", "! ", "? "):
        i = t.find(end)
        if 0 < i <= cap:
            return t[: i + 1]
    return (t[:cap] + "…") if len(t) > cap else t


def _clerk_pick(catalog, exclude: set):
    """A deterministic 'pick for you': the highest-rated installed game the
    player hasn't started (not in `exclude` = continue ∪ favorites). Ties
    broken by id so it's stable across runs (no random — repeatable feed)."""
    installed = _installed_ids(catalog)
    cands = [catalog.by_id[g] for g in installed
             if g in catalog.by_id and g not in exclude]
    if not cands:
        return None
    return sorted(cands, key=lambda g: (-(g.get("rating") or 0), g["id"]))[0]


def new_game_ids(catalog, play_state) -> list:
    """Installed game ids the player hasn't acknowledged yet (sorted for a
    stable feed)."""
    seen = play_state.seen_game_ids()
    return sorted(g for g in _installed_ids(catalog) if g not in seen)


def unread_count(catalog, play_state) -> int:
    """Bell dot driver. 1 on first run (welcome), else the count of newly
    installed games since last seen. Evergreen items don't count."""
    if not play_state.notifications_baselined():
        return 1
    return len(new_game_ids(catalog, play_state))


def build_feed(catalog, play_state) -> list:
    """The notification list, newest/most-actionable first. Each item:
       {var, title, det, act, unread}
    where `var` ∈ clerk|dl|save (icon family), `act` is None or
    (kind, arg) — kind ∈ discover|library|game."""
    items: list[dict] = []
    installed = _installed_ids(catalog)

    # 1) library growth / first-run welcome
    if not play_state.notifications_baselined():
        n = len(installed)
        nsys = len({catalog.by_id[g].get("system")
                    for g in installed if g in catalog.by_id})
        items.append({
            "var": "clerk",
            "title": "Your library is ready",
            "det": (f"{n} games across {nsys} systems — the Clerk has written "
                    f"up every one. Browse by mood in Discover."),
            "act": ("discover", None),
            "unread": True,
        })
    else:
        new = new_game_ids(catalog, play_state)
        if new:
            titles = [catalog.by_id[g]["title"] for g in new[:3]
                      if g in catalog.by_id]
            sample = ", ".join(titles)
            more = " and more" if len(new) > len(titles) else ""
            items.append({
                "var": "dl",
                "title": f"{len(new)} game{'s' if len(new) != 1 else ''} joined your shelf",
                "det": f"{sample}{more}. Open Library to see them.",
                "act": ("library", None),
                "unread": True,
            })

    # 2) in progress
    ce = catalog.continue_entries()
    in_progress = {e["game"]["id"] for e in ce}
    if ce:
        g = ce[0]["game"]
        items.append({
            "var": "save",
            "title": f"{len(ce)} game{'s' if len(ce) != 1 else ''} in progress",
            "det": f"Pick up {g['title']} right where you left off.",
            "act": ("game", g["id"]),
            "unread": False,
        })

    # 3) the Clerk's pick (exclude what they're already playing / love)
    exclude = in_progress | set(play_state.favorites())
    pick = _clerk_pick(catalog, exclude)
    if pick:
        det = f"{pick['title']}"
        fs = _first_sentence(pick.get("description", ""))
        if fs:
            det = f"{pick['title']} — {fs}"
        items.append({
            "var": "clerk",
            "title": "The Clerk's pick for you",
            "det": det,
            "act": ("game", pick["id"]),
            "unread": False,
        })

    return items
