import { useCallback, useEffect, useRef, useState } from 'react';

export type AutoSaveStatus = 'idle' | 'saving' | 'saved' | 'error';

type SaveFn<T> = (payload: T) => unknown;

export interface UseAutoSave<T> {
  status: AutoSaveStatus;
  schedule: (payload: T, delay?: number) => void;
  flush: () => void;
}

/**
 * Debounced auto-save with a status the UI can render.
 *
 * const { status, schedule, flush } = useAutoSave(payload => api.save(payload));
 * - schedule(payload[, delayMs]) — (re)arms the debounce with the latest payload
 * - flush() — saves any pending payload immediately (e.g. on blur)
 * - status — 'idle' | 'saving' | 'saved' | 'error' ('saved' fades to 'idle')
 *
 * Pending changes are flushed on unmount so navigating away never loses edits.
 */
export function useAutoSave<T>(saveFn: SaveFn<T>, defaultDelay = 400): UseAutoSave<T> {
  const [status, setStatus] = useState<AutoSaveStatus>('idle');
  const timer = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);
  const pending = useRef<T | null>(null);
  const saveRef = useRef<SaveFn<T>>(saveFn);
  saveRef.current = saveFn;

  const run = useCallback(async (payload: T) => {
    pending.current = null;
    setStatus('saving');
    try {
      await saveRef.current(payload);
      setStatus('saved');
    } catch {
      setStatus('error'); // saveFn is responsible for surfacing the error (toast)
    }
  }, []);

  const schedule = useCallback((payload: T, delay = defaultDelay) => {
    pending.current = payload;
    clearTimeout(timer.current);
    timer.current = setTimeout(() => run(payload), delay);
  }, [run, defaultDelay]);

  const flush = useCallback(() => {
    if (pending.current != null) {
      clearTimeout(timer.current);
      run(pending.current);
    }
  }, [run]);

  // Fade the "Saved" confirmation back to idle.
  useEffect(() => {
    if (status !== 'saved') return undefined;
    const t = setTimeout(() => setStatus('idle'), 2000);
    return () => clearTimeout(t);
  }, [status]);

  // Never lose a pending edit on unmount.
  useEffect(() => () => {
    clearTimeout(timer.current);
    if (pending.current != null) {
      Promise.resolve(saveRef.current(pending.current)).catch(() => {});
    }
  }, []);

  return { status, schedule, flush };
}
