import { useEffect, useRef, useState } from "react";
import { LazyStore } from "@tauri-apps/plugin-store";

// ponytail: one store file for small UI state. Generated content (NPCs,
// encounters, etc.) goes through the generations backend instead; this is
// only for things that live in component state today (initiative, dice
// history, calendar events). Debounced save so a fast dice session doesn't
// write on every roll.
const store = new LazyStore("dm-pal-state.json");

function debounce<T extends (...args: never[]) => void>(fn: T, ms: number): T {
  let t: ReturnType<typeof setTimeout> | undefined;
  return ((...args: never[]) => {
    if (t) clearTimeout(t);
    t = setTimeout(() => fn(...args), ms);
  }) as T;
}

const save = debounce((key: string, value: unknown) => {
  void store.set(key, value);
  void store.save();
}, 400);

/**
 * useState that persists to tauri-plugin-store. Loads on mount (returns the
 * fallback until the async load completes), saves on every change (debounced).
 * Reload = no more data loss for initiative / dice / calendar.
 */
export function usePersistentState<T>(
  key: string,
  fallback: T,
): [T, React.Dispatch<React.SetStateAction<T>>, boolean] {
  const [value, setValue] = useState<T>(fallback);
  const [loaded, setLoaded] = useState(false);
  const skipSave = useRef(true);

  // Load once on mount.
  useEffect(() => {
    let alive = true;
    (async () => {
      try {
        const stored = await store.get<T>(key);
        if (alive && stored !== null && stored !== undefined) {
          setValue(stored);
        }
      } catch {
        // store unavailable — keep fallback.
      }
      if (alive) setLoaded(true);
    })();
    return () => {
      alive = false;
    };
  }, [key]);

  // Save on change (skip the initial load-induced render).
  useEffect(() => {
    if (skipSave.current) {
      skipSave.current = false;
      return;
    }
    save(key, value);
  }, [key, value]);

  return [value, setValue, loaded];
}