// Client store — thin wrapper over the Node/SQLite backend.
// The server is the source of truth; every mutation POSTs and the response
// returns the new { user, huddle, items, comments } bundle which we set as-is.

const API = {
  async get(path) {
    const r = await fetch(path, { credentials: 'same-origin' });
    if (!r.ok) {
      const j = await r.json().catch(() => ({}));
      throw new Error(j.error || `${r.status} ${r.statusText}`);
    }
    return r.json();
  },
  async post(path, body) {
    const isForm = typeof FormData !== 'undefined' && body instanceof FormData;
    const r = await fetch(path, {
      method: 'POST',
      credentials: 'same-origin',
      ...(isForm ? {} : { headers: { 'Content-Type': 'application/json' } }),
      body: isForm ? body : JSON.stringify(body || {}),
    });
    const j = await r.json().catch(() => ({}));
    if (!r.ok) throw new Error(j.error || `${r.status} ${r.statusText}`);
    return j;
  },
};

function initialState() {
  return {
    booting: true,          // first /api/me + /api/state load
    error: null,
    // Server-authoritative:
    user: null,
    huddle: null,           // null when user is not in a huddle
    items: [],
    comments: {},
    proposals: {},          // { itemId: [{id, startDate, endDate, createdBy, votes[]}] }
    reviews: {},            // { itemId: [{user, rating, text, time}] }
    // UI-only:
    tab: 'feed',
    activeItemId: null,
    youPhase: 'member',     // 'member' | 'join' | 'create' — sub-routes inside You
    authPhase: 'signin',    // 'signin' | 'signup' | 'sent'
    pendingEmail: '',
    devMagicLink: null,
    busy: false,            // any write in flight
  };
}

function reducer(s, a) {
  switch (a.type) {
    case 'set':
      // Merge server-authoritative fields in.
      return {
        ...s,
        user:      'user'      in a ? a.user      : s.user,
        huddle:    'huddle'    in a ? a.huddle    : s.huddle,
        items:     'items'     in a ? a.items     : s.items,
        comments:  'comments'  in a ? a.comments  : s.comments,
        proposals: 'proposals' in a ? a.proposals : s.proposals,
        reviews:   'reviews'   in a ? a.reviews   : s.reviews,
      };
    case 'booted':   return { ...s, booting: false };
    case 'error':   return { ...s, error: a.msg, busy: false };
    case 'tab':     return { ...s, tab: a.tab, activeItemId: null };
    case 'open':    return { ...s, activeItemId: a.id };
    case 'close':   return { ...s, activeItemId: null };
    case 'youPhase':  return { ...s, youPhase: a.phase };
    case 'authPhase': return { ...s, authPhase: a.phase };
    case 'pendingEmail': return { ...s, pendingEmail: a.email };
    case 'devMagicLink': return { ...s, devMagicLink: a.link };
    case 'busy':     return { ...s, busy: a.v };
    case 'signout':  return { ...initialState(), booting: false, authPhase: 'signin' };
    default: return s;
  }
}

const StoreContext = React.createContext(null);

function StoreProvider({ children }) {
  const [state, dispatch] = React.useReducer(reducer, null, initialState);

  // Boot: check session; if authed, pull state; otherwise show auth.
  React.useEffect(() => {
    (async () => {
      try {
        const r = await fetch('/api/me', { credentials: 'same-origin' });
        if (!r.ok) { dispatch({ type: 'booted' }); return; }
        const st = await API.get('/api/state');
        dispatch({ type: 'set',
          user: st.user, huddle: st.huddle,
          items: st.items || [], comments: st.comments || {},
          proposals: st.proposals || {}, reviews: st.reviews || {},
        });
        dispatch({ type: 'youPhase', phase: st.huddle ? 'member' : 'member' });
      } catch (e) {
        dispatch({ type: 'error', msg: String(e.message || e) });
      } finally {
        dispatch({ type: 'booted' });
      }
    })();
  }, []);

  const actions = React.useMemo(() => {
    const withBusy = async (fn) => {
      dispatch({ type: 'busy', v: true });
      try { return await fn(); }
      catch (e) { dispatch({ type: 'error', msg: String(e.message || e) }); throw e; }
      finally { dispatch({ type: 'busy', v: false }); }
    };
    const applyState = (st) => {
      dispatch({
        type: 'set',
        user: st.user, huddle: st.huddle,
        items: st.items || [], comments: st.comments || {},
        proposals: st.proposals || {}, reviews: st.reviews || {},
      });
    };
    return {
      async requestMagicLink(email, remember) {
        return withBusy(async () => {
          const r = await API.post('/api/auth/magic-link', { email, remember });
          dispatch({ type: 'pendingEmail', email });
          dispatch({ type: 'authPhase', phase: 'sent' });
          dispatch({ type: 'devMagicLink', link: r.devLink || null });
        });
      },
      async signOut() {
        await API.post('/api/auth/logout');
        dispatch({ type: 'signout' });
      },
      async refresh() {
        const st = await API.get('/api/state');
        applyState(st);
      },
      async createHuddle(name, emoji) {
        return withBusy(async () => applyState(await API.post('/api/huddles', { name, emoji })));
      },
      async joinHuddle(code) {
        return withBusy(async () => applyState(await API.post('/api/huddles/join', { code })));
      },
      async leaveHuddle() {
        return withBusy(async () => applyState(await API.post('/api/huddles/leave', {})));
      },
      async addItem(item) {
        return withBusy(async () => applyState(await API.post('/api/items', item)));
      },
      async toggleDown(id) {
        applyState(await API.post(`/api/items/${id}/down`, {}));
      },
      async addComment(id, text) {
        applyState(await API.post(`/api/items/${id}/comments`, { text }));
      },
      async extractLink(url) {
        const r = await API.post('/api/extract/link', { url });
        return r.detected;
      },
      async extractImage(file) {
        const fd = new FormData();
        fd.append('image', file);
        const r = await API.post('/api/extract/image', fd);
        return { detected: r.detected, uploadUrl: r.uploadUrl };
      },
      async updateProfile({ name, avatarUrl, avatar }) {
        const r = await fetch('/api/me', { method: 'PATCH', credentials: 'same-origin',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ name, avatarUrl, avatar }) });
        if (!r.ok) throw new Error((await r.json()).error || r.statusText);
        applyState(await r.json());
      },
      async uploadAvatar(file) {
        const fd = new FormData();
        fd.append('avatar', file);
        const r = await API.post('/api/me/avatar', fd);
        applyState(r.state);
        return r.avatarUrl;
      },
      async setNotifications(pref) {
        const r = await fetch('/api/me/prefs', { method: 'PATCH', credentials: 'same-origin',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ notifications: pref }) });
        if (!r.ok) throw new Error((await r.json()).error || r.statusText);
        applyState(await r.json());
      },
      async renameHuddle({ name, emoji }) {
        const r = await fetch('/api/huddles/current', { method: 'PATCH', credentials: 'same-origin',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ name, emoji }) });
        if (!r.ok) throw new Error((await r.json()).error || r.statusText);
        applyState(await r.json());
      },
      async transferOwnership(toUserId, leaveAfter) {
        applyState(await API.post('/api/huddles/transfer', { toUserId, leaveAfter: !!leaveAfter }));
      },
      async deleteHuddle() {
        const r = await fetch('/api/huddles/current', { method: 'DELETE', credentials: 'same-origin' });
        if (!r.ok) throw new Error((await r.json()).error || r.statusText);
        applyState(await r.json());
      },
      async tryLeaveHuddle() {
        // Returns { ok:true } on success, or { needsTransfer:true, members } / { needsDelete:true }
        const r = await fetch('/api/huddles/leave', { method: 'POST', credentials: 'same-origin',
          headers: { 'Content-Type': 'application/json' }, body: '{}' });
        if (r.ok) { applyState(await r.json()); return { ok: true }; }
        const j = await r.json().catch(() => ({}));
        if (j.error === 'must_transfer') return { needsTransfer: true, members: j.members || [] };
        if (j.error === 'must_delete') return { needsDelete: true };
        throw new Error(j.error || r.statusText);
      },
      async editItem(id, patch) {
        const r = await fetch(`/api/items/${id}`, { method: 'PATCH', credentials: 'same-origin',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify(patch) });
        if (!r.ok) throw new Error((await r.json()).error || r.statusText);
        applyState(await r.json());
      },
      async deleteItem(id) {
        const r = await fetch(`/api/items/${id}`, { method: 'DELETE', credentials: 'same-origin' });
        if (!r.ok) throw new Error((await r.json()).error || r.statusText);
        applyState(await r.json());
      },
      async proposeDates(itemId, startDate, endDate) {
        applyState(await API.post(`/api/items/${itemId}/proposals`, { startDate, endDate }));
      },
      async voteProposal(itemId, proposalId) {
        applyState(await API.post(`/api/items/${itemId}/proposals/${proposalId}/vote`, {}));
      },
      async withdrawProposal(itemId, proposalId) {
        const r = await fetch(`/api/items/${itemId}/proposals/${proposalId}`, { method: 'DELETE', credentials: 'same-origin' });
        if (!r.ok) throw new Error((await r.json()).error || r.statusText);
        applyState(await r.json());
      },
      async lockProposal(itemId, proposalId) {
        applyState(await API.post(`/api/items/${itemId}/lock`, { proposalId }));
      },
      async submitReview(itemId, rating, text) {
        applyState(await API.post(`/api/items/${itemId}/review`, { rating, text }));
      },
      async removeReview(itemId) {
        const r = await fetch(`/api/items/${itemId}/review`, { method: 'DELETE', credentials: 'same-origin' });
        if (!r.ok) throw new Error((await r.json()).error || r.statusText);
        applyState(await r.json());
      },
    };
  }, []);

  const value = React.useMemo(() => ({ state, dispatch, actions }), [state, actions]);
  return <StoreContext.Provider value={value}>{children}</StoreContext.Provider>;
}

function useStore() {
  const ctx = React.useContext(StoreContext);
  if (!ctx) throw new Error('useStore must be used inside StoreProvider');
  return ctx;
}

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