'use client';

import {useCallback, useSyncExternalStore} from 'react';

/**
 * A boolean persisted in localStorage, read without a hydration mismatch.
 *
 * useSyncExternalStore already solves the hydration problem on its own:
 * React uses `getServerSnapshot` for the SSR render AND for the first client
 * render, so the two markups match, then immediately re-reads `getSnapshot`
 * and re-renders with the stored value. No `isHydrated` flag is needed — and
 * adding one means a setState inside an effect, which React's lint rule
 * rejects and which costs an extra render pass to do what the hook already
 * does natively.
 *
 * Writes notify every subscriber, so two components reading the same key never
 * disagree, and the `storage` event keeps other tabs in step.
 */
const listeners = new Set<() => void>();

function emit() {
  listeners.forEach((l) => l());
}

function subscribe(onChange: () => void) {
  listeners.add(onChange);
  window.addEventListener('storage', onChange);
  return () => {
    listeners.delete(onChange);
    window.removeEventListener('storage', onChange);
  };
}

/**
 * The same store, but "never set" stays distinguishable from "set to false".
 *
 * The sidebar needs that distinction: its default is per-device (a tablet
 * starts collapsed, a desktop starts expanded), so a stored `false` has to
 * mean "this merchant expanded it" and not "no preference yet". Collapsing
 * the two would either strand tablets expanded or force desktops to re-expand
 * on every first visit.
 */
export function usePersistentTriState(
  key: string,
): [boolean | null, (next: boolean) => void] {
  const getSnapshot = useCallback(() => {
    try {
      const raw = window.localStorage.getItem(key);
      return raw === null ? null : raw === 'true';
    } catch {
      // Private mode / storage disabled — degrade to "no preference" rather
      // than taking the whole shell down over a preference.
      return null;
    }
  }, [key]);

  const getServerSnapshot = useCallback(() => null, []);

  const value = useSyncExternalStore(
    subscribe,
    getSnapshot,
    getServerSnapshot,
  );

  const set = useCallback(
    (next: boolean) => {
      try {
        window.localStorage.setItem(key, String(next));
      } catch {
        /* ignore — the notify below still updates this session */
      }
      emit();
    },
    [key],
  );

  return [value, set];
}

/**
 * The plain boolean form, for a preference with one default everywhere.
 *
 * Kept rather than deleted as unused: it is the shape most callers want, the
 * tri-state variant exists only because the sidebar's default is per-device,
 * and collapsing the file to the harder API would push that `?? fallback` into
 * every future call site.
 */
export function usePersistentFlag(
  key: string,
  fallback = false,
): [boolean, (next: boolean) => void] {
  const [stored, set] = usePersistentTriState(key);
  return [stored ?? fallback, set];
}
