/* One persistence layer for the whole prototype.

   WHY THIS EXISTS. 21 files reached into `localStorage` directly, each with its
   own key string, its own JSON parse, and its own try/catch (or none). That is
   survivable while everything is mock. It stops being survivable at backend
   cutover, because five of those keys do not hold preferences — they hold
   CONTENT:

       pb-brands · pb-skins · pb-bonus-programs · pb-launch-urls
       hdp-deposit-actions

   An operator who created a bonus programme in the prototype keeps seeing it
   after the API is wired, because `pb-bonus-programs` is still in their browser
   and the screen merges it with the real response. Nothing in the codebase
   declared which of those keys were content and which were preferences, so
   there was no way to write the cutover step.

   This file declares it. Every key is registered with a `kind`, and
   `pbStore.clearContent()` is the one call the cutover needs.

   USE:
     pbStore.get("pb-accent", "red")            read, never throws
     pbStore.set("pb-accent", "navy")           write, never throws
     pbStore.remove("pb-accent")
     const [cols, setCols] = usePbStored("pb-tx-columns", DEFAULTS)

   ONE TRAP: `set` returns a boolean (false when the quota refuses the write).
   `localStorage.setItem` returned undefined, so an expression-bodied effect
   was safe before and is not now:

     useEffect(() => pbStore.set(k, v), [v])     // WRONG — React takes the
                                                 // boolean as a cleanup fn
     useEffect(() => { pbStore.set(k, v); }, [v])  // right

   The migration hit this on six effects; `tools/smoke.js` caught all six as
   "TypeError: destroy is not a function".

   The value is JSON. Strings are stored as JSON strings, so a key written
   before this file existed (raw, unquoted) still reads back correctly — see
   the fallback in `parse`. That matters: real browsers already carry the old
   raw values. */

const PB_KEYS = {
  /* ---- content: created by the operator, WILL collide with a real backend -- */
  "pb-brands":            { kind: "content", what: "the brand list Settings edits" },
  "pb-skins":             { kind: "content", what: "skin configuration" },
  "pb-bonus-programs":    { kind: "content", what: "bonus programmes created in the prototype" },
  "pb-launch-urls":       { kind: "content", what: "launch URL entries" },
  "hdp-deposit-actions":  { kind: "content", what: "approve/reject decisions on the deposits queue" },

  /* ---- preferences: safe to keep across the cutover --------------------- */
  "pb-accent":            { kind: "pref", what: "accent colour" },
  "pb-density":           { kind: "pref", what: "row density" },
  "pb-nav":               { kind: "pref", what: "navigation layout" },
  "pb-lang":              { kind: "pref", what: "interface language" },
  "pb-locale":            { kind: "pref", what: "i18n locale" },
  "pb-active":            { kind: "pref", what: "last active route" },
  "pb-brand-id":          { kind: "pref", what: "selected brand" },
  "pb-view-currency":     { kind: "pref", what: "report view currency" },
  "pb-display-currency":  { kind: "pref", what: "display currency" },
  "pb-side-collapsed":    { kind: "pref", what: "sidebar collapsed" },
  "pb-tx-columns":        { kind: "pref", what: "Transactions column visibility" },
  "pb-players-columns":   { kind: "pref", what: "Players column visibility" },
  "pb-methods-columns":   { kind: "pref", what: "Methods column visibility" },
  "hpx-player-cols":      { kind: "pref", what: "Host Players column visibility" },
  "iwk-hsc-hidden-cols":  { kind: "pref", what: "Sport coupons hidden columns" },
  "iwk-hnl-hidden-cols":  { kind: "pref", what: "Network liabilities hidden columns" },
  "summary_report_table_settings": { kind: "pref", what: "Summary report column settings" },
  "pb-myaccount":         { kind: "pref", what: "the signed-in operator's own profile (prototype state)" },
};

const pbStore = (() => {
  const available = (() => {
    try { const k = "__pb_probe__"; localStorage.setItem(k, "1"); localStorage.removeItem(k); return true; }
    catch (_e) { return false; }   // Safari private mode, disabled storage, sandboxed iframe
  })();

  // Anything not registered above still works, but says so once. A key that
  // nobody declared is a key the cutover cannot classify.
  const warned = new Set();
  const meta = (key) => {
    const m = PB_KEYS[key];
    if (!m && !warned.has(key)) {
      warned.add(key);
      console.warn(`[pbStore] "${key}" is not registered in PB_KEYS. Add it with a kind ("pref" or "content") so the backend cutover knows whether to clear it.`);
    }
    return m || { kind: "pref", what: "(unregistered)" };
  };

  const parse = (raw, fallback) => {
    if (raw === null || raw === undefined) return fallback;
    try { return JSON.parse(raw); }
    // Written before this file existed, as a bare string ("red", "compact").
    catch (_e) { return raw; }
  };

  return {
    keys: PB_KEYS,
    available,

    get(key, fallback) {
      meta(key);
      if (!available) return fallback;
      try { return parse(localStorage.getItem(key), fallback); }
      catch (_e) { return fallback; }
    },

    set(key, value) {
      meta(key);
      if (!available) return false;
      try { localStorage.setItem(key, JSON.stringify(value)); return true; }
      catch (_e) { return false; }   // quota exceeded — a preference is not worth throwing over
    },

    remove(key) {
      if (!available) return;
      try { localStorage.removeItem(key); } catch (_e) { /* nothing to do */ }
    },

    /* The cutover step. Call once, before the first API read, then never again.
       Returns what it removed so the caller can log or show it. */
    clearContent() {
      const removed = [];
      Object.entries(PB_KEYS).forEach(([key, m]) => {
        if (m.kind !== "content") return;
        if (!available) return;
        try { if (localStorage.getItem(key) !== null) { localStorage.removeItem(key); removed.push(key); } }
        catch (_e) { /* nothing to do */ }
      });
      return removed;
    },

    /* Which content keys currently hold something — for a cutover preflight. */
    contentKeysInUse() {
      if (!available) return [];
      return Object.keys(PB_KEYS).filter(k => PB_KEYS[k].kind === "content" && (() => {
        try { return localStorage.getItem(k) !== null; } catch (_e) { return false; }
      })());
    },
  };
})();

/* useState that persists. Same signature as useState, plus the key. */
const usePbStored = (key, fallback) => {
  const [value, setValue] = React.useState(() => pbStore.get(key, fallback));
  const write = React.useCallback((next) => {
    setValue((cur) => {
      const resolved = typeof next === "function" ? next(cur) : next;
      pbStore.set(key, resolved);
      return resolved;
    });
  }, [key]);
  return [value, write];
};

window.PB_KEYS = PB_KEYS;
window.pbStore = pbStore;
window.usePbStored = usePbStored;
