// Daily Ritual — the day's "calm set", a finite daily loop. Derived ENTIRELY
// from signals we already store (game state in localStorage + a brief-seen flag)
// — no backend, no new sync.
//
// "Today" is ALWAYS the server's puzzle/brief date passed in by the caller,
// never the browser's local date, so the daily reset matches the site's actual
// daily content (a reader in another timezone never sees "fresh set tomorrow"
// while today's puzzle is still up).
//
// Spirit (Claude + Codex): gentle and non-instrumental. "Enjoyed," not
// "completed"; "N of M · fresh set tomorrow," never "finish the rest." Brief
// counts only when the end-cap is reached (the finite read), not on page open.
//
// The set is CURATABLE: only daily-cadence "one-a-day, finish-and-done" things
// are eligible (never Free Play / ambient toys), and the reader chooses which of
// those fill THEIR set (settings → Calm set). Default = all eligible items.

const BRIEF_SEEN_KEY = (date) => `goodnews:briefSeen:${date}`;

function read(key) {
  try { return JSON.parse(localStorage.getItem(key) || 'null'); } catch { return null; }
}

// Mark the brief as enjoyed for `date` — call only when the reader actually
// reaches the end-cap, not merely on open.
export function markBriefSeen(date) {
  if (!date) return;
  try { localStorage.setItem(BRIEF_SEEN_KEY(date), '1'); } catch { /* ignore */ }
}

export function briefSeen(date) {
  if (!date) return false;
  try { return localStorage.getItem(BRIEF_SEEN_KEY(date)) === '1'; } catch { return false; }
}

// A word is enjoyed once you've seen it through (won or lost) on either length.
function wordEnjoyed(date) {
  for (const v of ['5', '6']) {
    const s = read(`goodnews:word:${v}:${date}`);
    if (s && (s.status === 'won' || s.status === 'lost')) return true;
  }
  return false;
}

// A word search is enjoyed once any size is cleared.
function wordsearchEnjoyed(date) {
  for (const sz of ['small', 'med', 'large']) {
    const s = read(`goodnews:wordsearch:${sz}:${date}`);
    if (s && s.status === 'done') return true;
  }
  return false;
}

// Bloom is enjoyed once the daily reaches the top tier (Flourishing) — the day's
// goal, the "saw it through" point (BloomGame persists `top`). Free Play doesn't count.
function bloomEnjoyed(date) {
  const s = read(`goodnews:bloom:${date}`);
  return !!(s && s.top);
}

// Memory Match is enjoyed once ANY daily board (any tier/format) is cleared — the
// reader's chosen mood-level counts; Free Play never does.
const MATCH_VARIANTS = ['gentle', 'standard', 'expert'].flatMap(
  (t) => ['icons', 'colors'].map((f) => `${t}-${f}`));
function matchEnjoyed(date) {
  for (const v of MATCH_VARIANTS) {
    const s = read(`goodnews:match:${v}:${date}`);
    if (s && s.done) return true;
  }
  return false;
}

// Eligible daily-cadence items, in display order. New daily games join here.
export const RITUAL_ITEMS = [
  { key: 'brief', label: 'Brief', href: '/', done: briefSeen },
  { key: 'word', label: 'Daily Word', href: '/play?game=word', done: wordEnjoyed },
  { key: 'wordsearch', label: 'Word Search', href: '/play?game=wordsearch', done: wordsearchEnjoyed },
  { key: 'bloom', label: 'Bloom', href: '/play?game=bloom&v=daily', done: bloomEnjoyed },
  { key: 'match', label: 'Memory Match', href: '/play?game=match&v=daily-icons-standard', done: matchEnjoyed },
];

const ALL_KEYS = RITUAL_ITEMS.map((i) => i.key);
// The default (out-of-box) set is a CURATED subset of the eligible items, kept
// separate from RITUAL_ITEMS on purpose: adding a new eligible daily game must
// NOT auto-bloat every uncustomized reader's set. A new game becomes selectable
// in the chooser immediately; whether it joins the default is a deliberate
// case-by-case decision (add its key here). `null` pref = "follow this default."
export const DEFAULT_KEYS = ['brief', 'word', 'wordsearch', 'bloom'];

// Normalize the reader's selection: null/undefined → the curated default;
// an array → those keys in canonical order; unknown keys ignored.
export function ritualKeys(enabled) {
  if (!Array.isArray(enabled)) return DEFAULT_KEYS.filter((k) => ALL_KEYS.includes(k));
  return ALL_KEYS.filter((k) => enabled.includes(k));
}

// The day's ritual snapshot for the reader's chosen set. Reads localStorage on
// each call (cheap) — callers recompute on mount / navigation / focus.
export function ritualState(date, enabled) {
  const keys = ritualKeys(enabled);
  const items = RITUAL_ITEMS
    .filter((i) => keys.includes(i.key))
    .map((i) => ({ key: i.key, label: i.label, href: i.href, done: i.done(date) }));
  return { items, count: items.filter((i) => i.done).length, total: items.length };
}
