/* global React */
// store.jsx — tiny global store. Components read via useStore(selector),
// write via STORE.set(partial) or STORE.set(updater). Listeners run on every
// mutation. The whole state is mirrored to localStorage on each write so a
// reload keeps the user's draft lineup, palette, settings, and clubhouse
// drafts intact.

// v2 bump: cleared seed mock data (fake names + sample lineup) — bumping the
// key blows away any older cached state that still has those defaults.
const STORE_KEY = 'cambam-store-v2';

const DEFAULT_STATE = {
  // Auth
  authed: false,
  me: { handle: '', email: '', displayName: '', tone: 6 },

  // Lineup draft (per-tournament). Empty by default — populated from Supabase
  // by api-client.jsx after sign-in, or via the SwapDrawer as the user picks.
  lineupDraft: {
    pga: {
      slots: [null, null, null, null, null, null],
      savedAt: null,
    },
  },

  // Settings
  settings: {
    palette: 'dusk',          // dusk | dawn | fireplace | moonlit
    reduceMotion: false,
    tabularZeros: true,
    notifications: {
      lockReminders: true,
      scoreSwings: true,
      leaderboard: true,
      wdMc: true,
      weeklyRecap: false,
      clubMentions: true,
    },
  },

  // Clubhouse messages — hydrated from Supabase by ClubhouseScreen.
  clubhouse: {
    pga: [],
  },

  // Matchup
  matchup: {
    opponentHandle: '',   // resolved against standings by PGAMatchupScreen
    mode: 'live',         // 'live' | 'projected'
    dimCommon: true,
  },

  // Sheet payload — when a sheet opens, what context should it render with?
  // e.g., { sheet: 'golfer-detail', payload: { name: 'Scottie Scheffler', ... } }
  sheetPayload: null,

  // Last action — used for the lightweight toast/feedback after Save lineup, etc.
  toast: null,
};

const STORE = {
  state: load(),
  listeners: new Set(),

  set(updater) {
    const prev = this.state;
    const next = typeof updater === 'function'
      ? updater(prev)
      : { ...prev, ...updater };
    if (next === prev) return;
    this.state = next;
    save(next);
    this.listeners.forEach((l) => { try { l(next, prev); } catch (e) { console.error(e); } });
  },

  // Helper for shallow-merging a nested slice like 'settings' or 'matchup'.
  patch(slice, partial) {
    this.set({ [slice]: { ...this.state[slice], ...partial } });
  },

  subscribe(listener) {
    this.listeners.add(listener);
    return () => this.listeners.delete(listener);
  },

  reset() {
    localStorage.removeItem(STORE_KEY);
    this.state = JSON.parse(JSON.stringify(DEFAULT_STATE));
    this.listeners.forEach((l) => l(this.state, null));
  },

  toast(text, ms = 2000) {
    this.set({ toast: { text, at: Date.now() } });
    if (ms > 0) setTimeout(() => {
      // Only clear if it's still our toast.
      const t = this.state.toast;
      if (t && t.text === text) this.set({ toast: null });
    }, ms);
  },
};

function load() {
  try {
    const raw = localStorage.getItem(STORE_KEY);
    if (!raw) return JSON.parse(JSON.stringify(DEFAULT_STATE));
    const parsed = JSON.parse(raw);
    // Shallow-merge so new defaults arrive when we add fields between releases.
    return {
      ...JSON.parse(JSON.stringify(DEFAULT_STATE)),
      ...parsed,
      settings:    { ...DEFAULT_STATE.settings, ...(parsed.settings || {}),
                     notifications: { ...DEFAULT_STATE.settings.notifications, ...((parsed.settings && parsed.settings.notifications) || {}) } },
      lineupDraft: { ...DEFAULT_STATE.lineupDraft, ...(parsed.lineupDraft || {}) },
      clubhouse:   { ...DEFAULT_STATE.clubhouse,   ...(parsed.clubhouse   || {}) },
      matchup:     { ...DEFAULT_STATE.matchup,     ...(parsed.matchup     || {}) },
      me:          { ...DEFAULT_STATE.me,          ...(parsed.me          || {}) },
    };
  } catch (e) {
    console.warn('store: failed to load, using defaults', e);
    return JSON.parse(JSON.stringify(DEFAULT_STATE));
  }
}

function save(state) {
  try {
    // Don't persist transient slices.
    const { sheetPayload, toast, ...persisted } = state;
    localStorage.setItem(STORE_KEY, JSON.stringify(persisted));
  } catch (e) {
    console.warn('store: failed to save', e);
  }
}

// React hook: subscribe to a derived slice. Re-renders on changes.
function useStore(selector = (s) => s, equalityFn = Object.is) {
  const [slice, setSlice] = React.useState(() => selector(STORE.state));
  const ref = React.useRef({ selector, equalityFn, slice });
  ref.current = { selector, equalityFn, slice };
  React.useEffect(() => {
    return STORE.subscribe((next) => {
      const nextSlice = ref.current.selector(next);
      if (!ref.current.equalityFn(ref.current.slice, nextSlice)) {
        setSlice(nextSlice);
      }
    });
  }, []);
  return slice;
}

window.STORE = STORE;
window.useStore = useStore;
