// Native-backed mirror: the shared Cambium dir (device.json, pins.json,
// workspaces.json, per-key client-state files) is the cross-process source of
// truth once the desktop app has a native bridge — a future CLI reads/writes
// the same files. But the webview needs SYNCHRONOUS reads for some of this
// state (serverPins.getPin is called synchronously from pinnedApiFetch), so
// those bits keep living in localStorage for fast sync reads and are kept in
// sync with native at two boundaries:
//
//   - hydrate(): at boot, read native and seed localStorage with it. If
//     native has never been written (the loader resolves to null) but
//     localStorage already holds a value — an existing install predating this
//     migration — push that value up to native once, so the shared file
//     picks up where the old localStorage-only install left off.
//   - writeThrough(): every write updates localStorage immediately (for sync
//     readers) and fires the native save in the background (fire-and-forget;
//     localStorage is already the durable copy for this process).
//
// On the web build there is no native bridge: callers pass loaders that
// resolve to `undefined` (see platform.js's bridge?.foo?.() ?? ... fallback
// convention) and savers that no-op, so both functions degrade to touching
// localStorage only — exactly the pre-migration behavior.

/**
 * Seed localStorage[key] from native, migrating existing localStorage data up
 * to native the first time (native empty, localStorage present).
 *
 * @param {string} key localStorage key.
 * @param {() => Promise<string|null|undefined>} nativeLoader Resolves to the
 *   stored string, `null` if native has never been saved (an empty/default
 *   store), or `undefined` if the read failed or there is no native bridge.
 * @param {(value: string) => Promise<void>} [nativePusher] Called with the
 *   existing localStorage string when native is empty, to migrate it up.
 */
export async function hydrate(key, nativeLoader, nativePusher) {
  let native;
  try {
    native = await nativeLoader();
  } catch {
    native = undefined;
  }
  if (typeof native === 'string') {
    try {
      localStorage.setItem(key, native);
    } catch {
      /* quota/private mode: native remains the source of truth this run */
    }
    return;
  }
  if (native === null && nativePusher) {
    let existing;
    try {
      existing = localStorage.getItem(key);
    } catch {
      existing = null;
    }
    if (existing != null) {
      try {
        await nativePusher(existing);
      } catch {
        /* migration is best-effort; localStorage still has the data */
      }
    }
    return;
  }
  // undefined: native read failed, or there is no native bridge (web) — leave
  // localStorage exactly as it is.
}

/**
 * Write `value` to localStorage[key] and fire the native save in the
 * background (errors are swallowed — localStorage already has the durable
 * copy for this process).
 *
 * @param {string} key localStorage key.
 * @param {string} value New value (already JSON-stringified by the caller).
 * @param {(value: string) => Promise<unknown>} [nativeSaver]
 */
export function writeThrough(key, value, nativeSaver) {
  try {
    localStorage.setItem(key, value);
  } catch {
    /* quota/private mode: value just won't persist locally */
  }
  if (nativeSaver) {
    Promise.resolve()
      .then(() => nativeSaver(value))
      .catch(() => {});
  }
}
