// Global state for the MC Command Center.
// Persisted to Supabase via window._sbClient (initialised in index.html).
// Components consume via useStore(). Nothing here knows about the DB schema —
// it just reads/writes a single JSON blob per user.

// Seed — Michelle Cheri's actual founder context (plausible mock shown on
// first launch before real data is entered).
const SEED = () => {
  // dates relative to today
  const today = new Date();
  const inDays = (n) => {
    const d = new Date(today);
    d.setDate(d.getDate() + n);
    return d.toISOString().slice(0, 10);
  };
  const agoDays = (n) => inDays(-n);

  return {
    user: { name: 'Michelle Cheri', initials: 'MC' },
    week: weekNumber(today),
    weekStart: 'Monday',
    energy: 'strong', // spent | steady | strong | wired
    priorities: [
      {
        id: 'p1',
        title: 'Pitch deck v3 — investor meeting Friday',
        tag: 'bdm',
        urgency: 'crit',
        transferred: false,
        rollovers: 0,
        extended: false,
        done: false,
        tasks: [
          { id: 't11', label: 'Rewrite traction slide (numbers from Stripe)', done: true },
          { id: 't12', label: 'Get Marcus feedback on positioning', done: true },
          { id: 't13', label: 'Re-do market sizing — bottoms-up', done: false },
          { id: 't14', label: 'Practice run with Sara at 4pm', done: false },
        ],
      },
      {
        id: 'p2',
        title: 'Close senior designer hire',
        tag: 'ops',
        urgency: 'soon',
        transferred: false,
        rollovers: 0,
        extended: false,
        done: false,
        tasks: [
          { id: 't21', label: 'Final call with Liesel — Wed 14:00', done: false },
          { id: 't22', label: 'Reference checks (3)', done: true },
          { id: 't23', label: 'Send offer letter', done: false },
        ],
      },
      {
        id: 'p3',
        title: 'Q3 board update — written + numbers',
        tag: 'admin',
        urgency: 'warn',
        transferred: true,
        rollovers: 1,
        extended: false,
        done: false,
        tasks: [
          { id: 't31', label: 'Pull Q3 metrics from data team', done: false },
          { id: 't32', label: 'Outline narrative (one page)', done: false },
          { id: 't33', label: 'Draft + share with Anders for review', done: false },
        ],
      },
      {
        id: 'p4',
        title: 'Sign Nordlys MSA — kickoff Monday',
        tag: 'bdm',
        urgency: 'soon',
        transferred: false,
        rollovers: 0,
        extended: false,
        done: false,
        tasks: [
          { id: 't41', label: 'Redline contract w/ lawyer', done: true },
          { id: 't42', label: 'Send signed PDF', done: false },
        ],
      },
    ],
    braindump: [
      { id: 'b1', text: 'Founder roundtable idea — quarterly, 8 people, no agenda', kind: 'idea', flagged: false, done: false, at: agoDays(0) },
      { id: 'b2', text: 'Ask Petra about her ops playbook',                          kind: 'dump', flagged: true,  done: false, at: agoDays(0) },
      { id: 'b3', text: 'Newsletter: "the transfer rule" essay',                     kind: 'idea', flagged: false, done: false, at: agoDays(1) },
      { id: 'b4', text: 'Cancel Loom subscription, we use Tella now',                kind: 'dump', flagged: false, done: true,  at: agoDays(1) },
      { id: 'b5', text: 'New pricing: maybe seat-based for enterprise?',             kind: 'idea', flagged: true,  done: false, at: agoDays(2) },
      { id: 'b6', text: 'Book Lisbon flights for Web Summit',                        kind: 'dump', flagged: false, done: false, at: agoDays(2) },
    ],
    deadlines: [
      { id: 'd1', name: 'Investor meeting',     ctx: 'with Marcus + partners',  due: inDays(3),  bucket: 'crit' },
      { id: 'd2', name: 'Nordlys signed MSA',   ctx: 'kickoff depends on this', due: inDays(5),  bucket: 'soon' },
      { id: 'd3', name: 'Q3 board update',      ctx: 'board reads sent Sunday', due: inDays(8),  bucket: 'soon' },
      { id: 'd4', name: 'VAT Q2 filing',        ctx: 'auto-prep by accountant', due: inDays(12), bucket: 'warn' },
      { id: 'd5', name: 'Series A target close',ctx: 'soft deadline',           due: inDays(38), bucket: 'soon' },
    ],
    admin: [
      { id: 'a1', kind: 'invoice-overdue', count: 1, when: '18 days overdue',  do: 'Send reminder', who: 'Acme · €4,200', sev: 'crit' },
      { id: 'a2', kind: 'invoice-send',    count: 2, when: 'ready to send',    do: 'Send now',      who: 'Nordlys + Saber', sev: 'warn' },
      { id: 'a3', kind: 'vat',             count: 1, when: 'due in 12 days',   do: 'Review & file', who: 'Q2 / NL',         sev: 'warn' },
      { id: 'a4', kind: 'expenses',        count: 14, when: 'this month',      do: 'Triage receipts',who: 'auto-imported',  sev: 'calm' },
      { id: 'a5', kind: 'contract',        count: 1, when: 'awaiting signature', do: 'Resend',      who: 'Nordlys MSA',     sev: 'warn' },
      { id: 'a6', kind: 'subscriptions',   count: 3, when: 'unused 60+ days',  do: 'Review & cut',  who: '€127/mo',         sev: 'calm' },
      { id: 'a7', kind: 'inbox',           count: 47, when: '3 flagged important', do: 'Triage 10 min', who: '',            sev: 'ok' },
      { id: 'a8', kind: 'tax-q3',          count: 1, when: 'estimate due Sep 15', do: 'Review',     who: 'income tax',      sev: 'calm' },
    ],
    money: {
      inMonth:    34200,
      outMonth:   18400,
      outstanding: 6050,
      currency:   '€',
      inSpark:  [12, 19, 14, 22, 16, 25, 30],
      outSpark: [16, 12, 18, 14, 19, 15, 13],
    },
    wins: [
      { id: 'w1', text: 'Sent investor shortlist to Marcus', when: 'Yesterday' },
      { id: 'w2', text: 'Closed dev contract with Saber',     when: 'Mon' },
      { id: 'w3', text: 'Q2 books reconciled (clean)',        when: '4d ago' },
      { id: 'w4', text: 'Hired fractional CFO',               when: '2w ago' },
      { id: 'w5', text: 'Series Seed extension closed',       when: '3w ago' },
    ],
    pipeline: {
      Opportunity: [{ n: 'Ovaro',   v: '€60k', hot: false }, { n: 'Tessera', v: '€140k', hot: true }, { n: 'Brink',   v: '€45k', hot: false }],
      Discovery:   [{ n: 'Halden',  v: '€80k', hot: true  }, { n: 'Marlow',  v: '€55k', hot: false }],
      Proposal:    [{ n: 'Nordlys', v: '€220k', hot: true }],
      Negotiation: [{ n: 'Acme',    v: '€180k', hot: true }],
      Won:         [{ n: 'Saber',   v: '€95k', hot: false }, { n: 'Lumen',   v: '€60k', hot: false }],
    },
    burst: {
      active: false,
      taskId: null,
      priorityId: null,
      length: 30 * 60, // seconds
      remaining: 30 * 60,
      phase: 'focus',  // 'focus' | 'break'
      running: false,
      completedToday: 3,
      streakDays: 4,
    },
    nextWeek: [],
    lastBurstPriorityId: null,
    cleanseStreak: 2,
    wrapStreak: 5,
    wrap: {
      active: false,
      priorityId: null,
      taskId: null,
      taskLabel: '',
      loops: [],
      win: '',
    },
  };
};

function weekNumber(d) {
  const date = new Date(d.getTime());
  date.setHours(0, 0, 0, 0);
  date.setDate(date.getDate() + 4 - (date.getDay() || 7));
  const yearStart = new Date(date.getFullYear(), 0, 1);
  return Math.ceil(((date - yearStart) / 86400000 + 1) / 7);
}

// ------ context + hook ------

const StoreCtx = React.createContext(null);

function StoreProvider({ children, seedOverride }) {
  const [state, _setState] = React.useState(() =>
    seedOverride ? { ...SEED(), ...seedOverride } : SEED()
  );
  // 'loading' → waiting for DB | 'signed-out' → no session | 'saving' → write in flight | 'synced' → all good
  const [syncState, setSyncState] = React.useState('loading');
  const saveTimer = React.useRef(null);

  React.useEffect(() => {
    const sb = window._sbClient;
    if (!sb) {
      // No Supabase client available — fall back to localStorage so the app
      // still works (dev without credentials, etc.)
      try {
        const raw = localStorage.getItem('mc-command-center-v1');
        if (raw) _setState(prev => ({ ...SEED(), ...JSON.parse(raw) }));
      } catch (e) {}
      setSyncState('synced');
      return;
    }

    let mounted = true;

    // Load the user's data from Supabase, migrating from localStorage if needed.
    const loadFromDB = async (userId) => {
      try {
        const { data: existing } = await sb
          .from('command_center_state')
          .select('data')
          .eq('user_id', userId)
          .maybeSingle();

        if (!mounted) return;

        if (existing?.data) {
          _setState(seedOverride
            ? { ...SEED(), ...existing.data, ...seedOverride }
            : { ...SEED(), ...existing.data }
          );
        } else {
          // One-time migration: carry over any localStorage data.
          const legacy = localStorage.getItem('mc-command-center-v1');
          if (legacy) {
            try {
              const parsed = JSON.parse(legacy);
              await sb.from('command_center_state')
                .insert({ user_id: userId, data: parsed });
              if (mounted) _setState(seedOverride
                ? { ...SEED(), ...parsed, ...seedOverride }
                : { ...SEED(), ...parsed }
              );
            } catch (e) {
              if (mounted) _setState(seedOverride ? { ...SEED(), ...seedOverride } : SEED());
            }
          } else {
            _setState(seedOverride ? { ...SEED(), ...seedOverride } : SEED());
          }
        }
      } catch (e) {
        console.warn('Failed to load from Supabase:', e);
        if (mounted) _setState(seedOverride ? { ...SEED(), ...seedOverride } : SEED());
      }
      if (mounted) setSyncState('synced');
    };

    // Use onAuthStateChange as single source of truth for session state.
    // INITIAL_SESSION fires immediately on mount (with session or null).
    // SIGNED_IN fires after a magic link redirect.
    const { data: { subscription } } = sb.auth.onAuthStateChange((event, session) => {
      if (!mounted) return;

      if (event === 'INITIAL_SESSION' || event === 'SIGNED_IN') {
        if (session?.user) {
          setSyncState('loading');
          loadFromDB(session.user.id);
        } else {
          // INITIAL_SESSION with no session = not signed in
          setSyncState('signed-out');
        }
      } else if (event === 'SIGNED_OUT') {
        _setState(SEED());
        setSyncState('signed-out');
      }
      // TOKEN_REFRESHED: session is still valid, no data reload needed.
    });

    return () => {
      mounted = false;
      subscription.unsubscribe();
    };
  }, []);

  const setState = React.useCallback((updater, opts) => {
    _setState((prev) => {
      const next = typeof updater === 'function' ? updater(prev) : updater;
      // Quiet updates skip the persist write AND the "saving…" indicator.
      // Used for ephemeral state like the pomodoro tick (1Hz would otherwise
      // flicker the sync indicator constantly).
      if (opts?.quiet) return next;

      setSyncState('saving');
      if (saveTimer.current) clearTimeout(saveTimer.current);

      saveTimer.current = setTimeout(async () => {
        const sb = window._sbClient;
        if (!sb) {
          // Fallback: localStorage
          try { localStorage.setItem('mc-command-center-v1', JSON.stringify(next)); } catch (e) {}
          setSyncState('synced');
          return;
        }
        try {
          const { data: { user } } = await sb.auth.getUser();
          if (!user) { setSyncState('synced'); return; }
          await sb.from('command_center_state')
            .upsert({ user_id: user.id, data: next, updated_at: new Date().toISOString() });
        } catch (e) {
          console.warn('Save failed:', e);
        }
        setSyncState('synced');
      }, 400);

      return next;
    });
  }, []);

  // ---- mutators (closures so children can call) ----
  const api = React.useMemo(() => ({
    state, syncState,
    setEnergy: (e) => setState((s) => ({ ...s, energy: e })),

    toggleTask: (priorityId, taskId) => setState((s) => ({
      ...s,
      priorities: s.priorities.map((p) => p.id !== priorityId ? p : {
        ...p,
        tasks: p.tasks.map((t) => t.id !== taskId ? t : { ...t, done: !t.done }),
        done:  p.tasks.every((t) => t.id === taskId ? !t.done : t.done) && p.tasks.length > 0 ? true : p.done,
      }),
    })),
    addTask: (priorityId, label) => setState((s) => ({
      ...s,
      priorities: s.priorities.map((p) => p.id !== priorityId ? p : {
        ...p, tasks: [...p.tasks, { id: 't' + Date.now(), label, done: false }],
      }),
    })),
    deletePriority: (id) => setState((s) => ({ ...s, priorities: s.priorities.filter((p) => p.id !== id) })),
    addPriority: (title) => setState((s) => ({
      ...s,
      priorities: [...s.priorities, {
        id: 'p' + Date.now(), title, tag: 'ops', urgency: 'soon',
        transferred: false, rollovers: 0, extended: false, done: false, tasks: [],
      }],
    })),
    renamePriority: (id, title) => setState((s) => ({
      ...s, priorities: s.priorities.map((p) => p.id === id ? { ...p, title } : p),
    })),
    setPriorityCategory: (id, tag) => setState((s) => ({
      ...s, priorities: s.priorities.map((p) => p.id === id ? { ...p, tag } : p),
    })),
    transferPriority: (id) => setState((s) => ({
      ...s, priorities: s.priorities.map((p) => p.id === id
        ? { ...p, transferred: true, rollovers: Math.min(((p.rollovers || 0) + 1), 2) }
        : p),
    })),
    grantExtension: (id) => setState((s) => ({
      ...s, priorities: s.priorities.map((p) => p.id === id ? { ...p, extended: true } : p),
    })),
    completePriority: (id) => setState((s) => {
      const p = s.priorities.find((x) => x.id === id);
      if (!p) return s;
      return {
        ...s,
        priorities: s.priorities.filter((x) => x.id !== id),
        wins: [{ id: 'w' + Date.now(), text: `✓ ${p.title}`, when: 'just now' }, ...s.wins],
      };
    }),
    bumpCleanseStreak: () => setState((s) => ({ ...s, cleanseStreak: (s.cleanseStreak || 0) + 1 })),

    // ---- wrap-up ritual ----
    startWrap: (priorityId, taskId) => setState((s) => {
      const p = s.priorities.find((x) => x.id === priorityId);
      const t = p?.tasks.find((x) => x.id === taskId);
      return {
        ...s,
        wrap: {
          active: true,
          priorityId: priorityId || null,
          taskId:     taskId     || null,
          taskLabel:  t?.label || p?.title || 'this burst',
          loops: [],
          win: '',
        },
      };
    }),
    addLoop: (text) => setState((s) => ({
      ...s,
      wrap: { ...s.wrap, loops: [...s.wrap.loops, { id: 'l' + Date.now() + Math.random(), text, routed: null }] },
    })),
    routeLoop: (loopId, destination) => setState((s) => {
      const loop = s.wrap.loops.find((l) => l.id === loopId);
      if (!loop) return s;
      let extra = {};
      if (destination === 'zip') {
        extra.braindump = [{ id: 'b' + Date.now() + Math.random(), text: loop.text, kind: 'dump',
          flagged: false, done: false, at: new Date().toISOString().slice(0, 10) }, ...s.braindump];
      } else if (destination === 'idea') {
        extra.braindump = [{ id: 'b' + Date.now() + Math.random(), text: loop.text, kind: 'idea',
          flagged: false, done: false, at: new Date().toISOString().slice(0, 10) }, ...s.braindump];
      } else if (destination?.startsWith('priority:')) {
        const pid = destination.slice('priority:'.length);
        extra.priorities = s.priorities.map((p) => p.id !== pid ? p : {
          ...p, tasks: [...p.tasks, { id: 't' + Date.now() + Math.random(), label: loop.text, done: false }],
        });
      }
      return {
        ...s,
        ...extra,
        wrap: { ...s.wrap, loops: s.wrap.loops.map((l) => l.id === loopId ? { ...l, routed: destination } : l) },
      };
    }),
    setWrapWin: (text) => setState((s) => ({ ...s, wrap: { ...s.wrap, win: text } })),
    completeWrap: ({ markTaskDone = false } = {}) => setState((s) => {
      let wins = s.wins;
      const w = s.wrap.win?.trim();
      if (w) wins = [{ id: 'w' + Date.now(), text: w, when: 'just now' }, ...wins];

      let priorities = s.priorities;
      if (s.wrap.priorityId && s.wrap.taskId) {
        priorities = priorities.map((p) => p.id !== s.wrap.priorityId ? p : {
          ...p, tasks: p.tasks.map((t) => t.id !== s.wrap.taskId ? t : { ...t, done: markTaskDone }),
        });
      }

      return {
        ...s, wins, priorities,
        wrapStreak: (s.wrapStreak || 0) + 1,
        wrap: { ...s.wrap, active: false },
      };
    }),
    dismissWrap: () => setState((s) => ({ ...s, wrap: { ...s.wrap, active: false } })),

    addBrainItem: (text, kind) => setState((s) => ({
      ...s,
      braindump: [{ id: 'b' + Date.now(), text, kind, flagged: false, done: false, at: new Date().toISOString().slice(0, 10) }, ...s.braindump],
    })),
    deleteBrainItem: (id) => setState((s) => ({ ...s, braindump: s.braindump.filter((b) => b.id !== id) })),
    toggleBrainDone: (id) => setState((s) => ({
      ...s, braindump: s.braindump.map((b) => b.id === id ? { ...b, done: !b.done } : b),
    })),
    toggleBrainFlag: (id) => setState((s) => ({
      ...s, braindump: s.braindump.map((b) => b.id === id ? { ...b, flagged: !b.flagged } : b),
    })),
    promoteBrainItem: (id) => setState((s) => {
      const item = s.braindump.find((b) => b.id === id);
      if (!item) return s;
      return {
        ...s,
        braindump: s.braindump.filter((b) => b.id !== id),
        priorities: [...s.priorities, {
          id: 'p' + Date.now(), title: item.text,
          tag: item.kind === 'idea' ? 'marketing' : 'ops',
          urgency: 'soon', transferred: false, rollovers: 0, extended: false, done: false, tasks: [],
        }],
      };
    }),

    startBurst: (priorityId, taskId, length) => setState((s) => ({
      ...s,
      burst: { ...s.burst, active: true, priorityId, taskId, length, remaining: length, phase: 'focus', running: true },
      lastBurstPriorityId: priorityId || s.lastBurstPriorityId,
    })),
    pauseBurst: () => setState((s) => ({ ...s, burst: { ...s.burst, running: !s.burst.running } })),
    closeBurst: () => setState((s) => ({ ...s, burst: { ...s.burst, active: false, running: false } })),
    tickBurst: () => setState((s) => {
      if (!s.burst.active || !s.burst.running) return s;
      const r = s.burst.remaining - 1;
      if (r <= 0) {
        const p = s.priorities.find((x) => x.id === s.burst.priorityId);
        const t = p?.tasks.find((x) => x.id === s.burst.taskId);
        return {
          ...s,
          burst: { ...s.burst, remaining: 0, running: false, completedToday: s.burst.completedToday + 1 },
          wrap: {
            active: true,
            priorityId: s.burst.priorityId,
            taskId:     s.burst.taskId,
            taskLabel:  t?.label || p?.title || 'this burst',
            loops: [],
            win: '',
          },
        };
      }
      return { ...s, burst: { ...s.burst, remaining: r } };
    }, { quiet: true }),
    setBurstLength: (sec) => setState((s) => ({ ...s, burst: { ...s.burst, length: sec, remaining: sec } })),

    // ---- next-week forward queue ----
    addNextWeekItem: (text, block = 'other') => setState((s) => ({
      ...s,
      nextWeek: [{ id: 'nw' + Date.now(), text, block, at: new Date().toISOString().slice(0, 10) }, ...(s.nextWeek || [])],
    })),
    deleteNextWeekItem: (id) => setState((s) => ({ ...s, nextWeek: (s.nextWeek || []).filter((i) => i.id !== id) })),
    promoteNextWeekItem: (id) => setState((s) => {
      const item = (s.nextWeek || []).find((i) => i.id === id);
      if (!item) return s;
      return {
        ...s,
        nextWeek: (s.nextWeek || []).filter((i) => i.id !== id),
        priorities: [...s.priorities, {
          id: 'p' + Date.now(), title: item.text,
          tag: 'ops', urgency: 'soon',
          transferred: false, rollovers: 0, extended: false, done: false, tasks: [],
        }],
      };
    }),
    updateNextWeekBlock: (id, block) => setState((s) => ({
      ...s,
      nextWeek: (s.nextWeek || []).map((i) => i.id === id ? { ...i, block } : i),
    })),

    completeAdmin: (id) => setState((s) => ({ ...s, admin: s.admin.filter((a) => a.id !== id) })),
    addWin: (text) => setState((s) => ({ ...s, wins: [{ id: 'w' + Date.now(), text, when: 'just now' }, ...s.wins] })),

    // Reset clears the DB row and reverts to seed data.
    reset: () => {
      _setState(SEED());
      const sb = window._sbClient;
      if (sb) {
        (async () => {
          try {
            const { data: { user } } = await sb.auth.getUser();
            if (user) await sb.from('command_center_state').delete().eq('user_id', user.id);
          } catch (e) {}
        })();
      } else {
        try { localStorage.removeItem('mc-command-center-v1'); } catch (e) {}
      }
    },

    // Sign out of Supabase (shows the sign-in screen).
    signOut: () => { window._sbClient?.auth.signOut(); },

  }), [state, syncState, setState]);

  return <StoreCtx.Provider value={api}>{children}</StoreCtx.Provider>;
}

const useStore = () => React.useContext(StoreCtx);

Object.assign(window, { StoreProvider, useStore });
