"use client";

import { useCallback, useSyncExternalStore } from "react";

type Listener = () => void;

const listeners = new Map<string, Set<Listener>>();
const cache = new Map<string, unknown>();

function readFromStorage<T>(key: string, initial: T): T {
  try {
    const raw = window.localStorage.getItem(key);
    return raw != null ? (JSON.parse(raw) as T) : initial;
  } catch {
    return initial;
  }
}

function notify(key: string) {
  listeners.get(key)?.forEach((l) => l());
}

/**
 * State, der in localStorage persistiert wird.
 * SSR-sicher über useSyncExternalStore: liefert serverseitig `initial`,
 * hydriert clientseitig aus localStorage.
 */
export function useLocalStorage<T>(key: string, initial: T) {
  const subscribe = useCallback(
    (callback: Listener) => {
      let set = listeners.get(key);
      if (!set) {
        set = new Set();
        listeners.set(key, set);
      }
      set.add(callback);
      return () => set!.delete(callback);
    },
    [key],
  );

  const getSnapshot = useCallback(() => {
    if (!cache.has(key)) cache.set(key, readFromStorage(key, initial));
    return cache.get(key) as T;
    // eslint-disable-next-line react-hooks/exhaustive-deps -- `initial` is only used as a one-time fallback
  }, [key]);

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

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

  const setValue = useCallback(
    (next: T | ((prev: T) => T)) => {
      const prev = cache.has(key) ? (cache.get(key) as T) : readFromStorage(key, initial);
      const resolved = typeof next === "function" ? (next as (p: T) => T)(prev) : next;
      cache.set(key, resolved);
      try {
        window.localStorage.setItem(key, JSON.stringify(resolved));
      } catch {
        // Speicher voll o.ä. – egal
      }
      notify(key);
    },
    [key, initial],
  );

  return [value, setValue] as const;
}
