/**
 * Shell layout state: is the left sidebar collapsed, is the Piggy dock open.
 *
 * localStorage only, and deliberately so — do not "fix" this back into a
 * server mirror. Sidebar-collapsed and dock-open are per-DEVICE preferences by
 * nature: a 27-inch display and a laptop want different answers, and syncing
 * them would carry the wrong one across. Theme is not like that and stays
 * mirrored (see lib/theme.tsx).
 *
 * There WAS a mirror here. It PATCHed /api/me/preferences, whose zod schema is
 * non-strict and accepts only themeMode/accentColor/name/handle/title/timezone
 * — so every collapse fired an HTTP request and a real UPDATE that dropped the
 * keys and bumped `users.updatedAt`, and the matching adopt path was always a
 * no-op. A silent no-op, which is the failure mode this codebase keeps getting
 * bitten by.
 *
 * Reading localStorage during the initial `useState` is early enough: the panes
 * animate their width, so mounting collapsed shows no flash of the expanded
 * rail. What is deliberately NOT copied from the theme is the pre-paint inline
 * script — that script's SHA is pinned in the CSP in three places, and a layout
 * preference is not worth a production deploy that silently white-flashes if
 * one of them is missed.
 */
import {
  createContext,
  useCallback,
  useContext,
  useMemo,
  useState,
  type ReactNode,
} from 'react';

const STORAGE_SIDEBAR = 'pig.sidebarOpen';
const STORAGE_DOCK = 'pig.piggyDockOpen';

interface LayoutContextValue {
  /** Expanded (true) or collapsed to the icon rail (false). */
  sidebarOpen: boolean;
  setSidebarOpen: (open: boolean) => void;
  dockOpen: boolean;
  setDockOpen: (open: boolean) => void;
  toggleDock: () => void;
}

const LayoutContext = createContext<LayoutContextValue | null>(null);

function readStored(key: string, fallback: boolean): boolean {
  try {
    const raw = localStorage.getItem(key);
    return raw === null ? fallback : raw === 'true';
  } catch {
    // Private browsing can throw on access. A default is fine.
    return fallback;
  }
}

function store(key: string, value: boolean): void {
  try {
    localStorage.setItem(key, String(value));
  } catch {
    // Non-fatal: the preference simply does not survive the tab. Nothing else
    // holds a copy, so there is no inconsistency to repair.
  }
}

export function LayoutProvider({ children }: { children: ReactNode }) {
  const [sidebarOpen, setSidebarOpenState] = useState(() => readStored(STORAGE_SIDEBAR, true));
  // Closed by default. An agent panel that opens itself on a first visit,
  // before anyone has asked for one, takes a third of the window uninvited.
  const [dockOpen, setDockOpenState] = useState(() => readStored(STORAGE_DOCK, false));

  const setSidebarOpen = useCallback((next: boolean) => {
    setSidebarOpenState(next);
    store(STORAGE_SIDEBAR, next);
  }, []);

  const setDockOpen = useCallback((next: boolean) => {
    setDockOpenState(next);
    store(STORAGE_DOCK, next);
  }, []);

  const toggleDock = useCallback(() => setDockOpen(!dockOpen), [dockOpen, setDockOpen]);

  const value = useMemo(
    () => ({ sidebarOpen, setSidebarOpen, dockOpen, setDockOpen, toggleDock }),
    [sidebarOpen, setSidebarOpen, dockOpen, setDockOpen, toggleDock],
  );

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

export function useLayout(): LayoutContextValue {
  const context = useContext(LayoutContext);
  if (!context) throw new Error('useLayout must be used inside a LayoutProvider.');
  return context;
}
