// pantry-sync.jsx — sync hook for authenticated users
//
// Usage in PantryAppContent:
//   usePantrySync({ user, inventory, setInventory, grocery, setGrocery, ... });
//
// Behavior:
//   • On mount with authed user → pull household data from /api/sync.
//     If the server has data, it REPLACES local state (server wins on first load).
//   • Whenever any synced slice changes → debounced push to /api/sync (1.5s).
//   • Polls every 30s for changes from other family members.
//   • If offline / API down → silent fallback (localStorage already covers durability).

const { useEffect: useEffectSync, useRef: useRefSync, useState: useStateSync } = React;

const SYNC_KEYS = ['inventory', 'grocery', 'mealPlan', 'leftovers', 'favorites', 'customRecipes', 'privacyPrefs'];
const PUSH_DEBOUNCE_MS = 1500;
const POLL_INTERVAL_MS = 30_000;

function shallowSerialize(slice) {
  if (slice instanceof Set) return [...slice];
  return slice;
}

function usePantrySync(opts) {
  const {
    enabled, user,
    inventory, setInventory,
    grocery, setGrocery,
    mealPlan, setMealPlan,
    leftovers, setLeftovers,
    favorites, setFavorites,
    customRecipes, setCustomRecipes,
    privacyPrefs, setPrivacyPrefs,
  } = opts;

  const [syncState, setSyncState] = useStateSync({ status: 'idle', lastSyncedAt: 0, error: null });
  const initialPullDone = useRefSync(false);
  const lastPushHash = useRefSync('');
  const pushTimer = useRefSync(null);
  const applyingRemote = useRefSync(false); // suppress push while applying remote update

  // ── apply remote data → local state ─────────────────────────
  const applyRemote = (data) => {
    if (!data) return;
    applyingRemote.current = true;
    if (Array.isArray(data.inventory))      setInventory(window.normalizeInventory ? window.normalizeInventory(data.inventory) : data.inventory);
    if (Array.isArray(data.grocery))        setGrocery(data.grocery);
    if (Array.isArray(data.mealPlan))       setMealPlan(window.migrateMealPlan ? window.migrateMealPlan(data.mealPlan) : data.mealPlan);
    if (Array.isArray(data.leftovers))      setLeftovers(data.leftovers);
    if (Array.isArray(data.favorites))      setFavorites(new Set(data.favorites));
    if (Array.isArray(data.customRecipes))  setCustomRecipes(data.customRecipes);
    if (data.privacyPrefs && typeof data.privacyPrefs === 'object') setPrivacyPrefs(data.privacyPrefs);
    // release the flag after React applies the updates
    setTimeout(() => { applyingRemote.current = false; }, 50);
  };

  // ── reset local state to truly empty (used when a fresh authed user
  //    discovers the server is empty — we don't want to push demo seed
  //    data into their brand-new household).
  const resetToEmpty = () => {
    applyingRemote.current = true;
    setInventory([]);
    setGrocery([]);
    setMealPlan([]);
    setLeftovers([]);
    setFavorites(new Set());
    setCustomRecipes([]);
    setPrivacyPrefs({ shareBreakfast: false, shareLunch: false, shareDinner: true });
    setTimeout(() => { applyingRemote.current = false; }, 50);
  };

  // ── initial pull on mount / when user logs in ───────────────
  useEffectSync(() => {
    if (!enabled || !user) return;
    initialPullDone.current = false;
    setSyncState((s) => ({ ...s, status: 'pulling', error: null }));
    window.PWAuth.api.syncPull().then(({ data, updatedAt }) => {
      // Server has data → apply (existing user signing in on a new device).
      // Server is empty → BLANK SLATE: clear local demo seed data so the new
      // household starts from scratch (no fake inventory/family/etc).
      const hasServerData = data && Object.keys(data).some((k) => data[k] != null);
      if (hasServerData) {
        applyRemote(data);
      } else {
        resetToEmpty();
      }
      initialPullDone.current = true;
      setSyncState({ status: 'ok', lastSyncedAt: updatedAt || Date.now(), error: null });
      // After resetting, push the (empty) state so the server has a row per key
      if (!hasServerData) schedulePush(true);
    }).catch((e) => {
      initialPullDone.current = true; // allow pushes — server might be initialised after retry
      setSyncState({ status: 'offline', lastSyncedAt: 0, error: e.message });
    });
  }, [enabled, user?.id]);

  // ── debounced push when any slice changes ────────────────────
  const schedulePush = (immediate = false) => {
    if (!enabled || !user || !initialPullDone.current || applyingRemote.current) return;
    if (pushTimer.current) clearTimeout(pushTimer.current);
    pushTimer.current = setTimeout(doPush, immediate ? 0 : PUSH_DEBOUNCE_MS);
  };

  // Returns a promise resolving to { ok, updatedAt } (or { ok:false, error }).
  const doPush = () => {
    const payload = {
      inventory:     shallowSerialize(inventory),
      grocery:       shallowSerialize(grocery),
      mealPlan:      shallowSerialize(mealPlan),
      leftovers:     shallowSerialize(leftovers),
      favorites:     shallowSerialize(favorites),
      customRecipes: shallowSerialize(customRecipes),
      privacyPrefs:  shallowSerialize(privacyPrefs),
    };
    const hash = JSON.stringify(payload);
    if (hash === lastPushHash.current) return Promise.resolve({ ok: true, updatedAt: 0 });
    lastPushHash.current = hash;
    setSyncState((s) => ({ ...s, status: 'pushing' }));
    return window.PWAuth.api.syncPush(payload).then(({ updatedAt }) => {
      setSyncState({ status: 'ok', lastSyncedAt: updatedAt || Date.now(), error: null });
      return { ok: true, updatedAt };
    }).catch((e) => {
      // Don't drop the hash — let a later change retry this payload.
      lastPushHash.current = '';
      setSyncState({ status: 'offline', lastSyncedAt: 0, error: e.message });
      return { ok: false, error: e.message };
    });
  };

  useEffectSync(() => { schedulePush(); }, [
    enabled, user?.id,
    inventory, grocery, mealPlan, leftovers, favorites, customRecipes, privacyPrefs,
  ]);

  // ── flush: force any pending/debounced push to the server NOW and wait ──
  // Exposed globally so sign-out (and page-hide) can guarantee the last change
  // is saved before local data is cleared. Resolves to { ok } so the caller
  // knows whether it's safe to wipe localStorage.
  useEffectSync(() => {
    window.PWAuth = window.PWAuth || {};
    window.PWAuth.flushSync = () => {
      if (!enabled || !user || !initialPullDone.current) return Promise.resolve({ ok: true });
      if (pushTimer.current) { clearTimeout(pushTimer.current); pushTimer.current = null; }
      return doPush();
    };
    return () => { if (window.PWAuth) delete window.PWAuth.flushSync; };
  });

  // ── periodic pull for family updates ────────────────────────
  useEffectSync(() => {
    if (!enabled || !user) return;
    const id = setInterval(() => {
      if (document.hidden) return;
      window.PWAuth.api.syncPull().then(({ data, updatedAt }) => {
        // Only apply if remote is newer than what we last pushed
        if (!data) return;
        // naive: just apply — last-write-wins
        applyRemote(data);
        setSyncState({ status: 'ok', lastSyncedAt: updatedAt || Date.now(), error: null });
      }).catch(() => {});
    }, POLL_INTERVAL_MS);
    return () => clearInterval(id);
  }, [enabled, user?.id]);

  return syncState;
}

window.usePantrySync = usePantrySync;
