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

export type PageDraftSnapshot = {
  title: string;
  summary: string;
  content: string;
};

const MAX_HISTORY = 50;
const HISTORY_DEBOUNCE_MS = 600;

export function usePageDraftHistory(initial: PageDraftSnapshot) {
  const [snapshot, setSnapshot] = useState(initial);
  const stableRef = useRef(initial);
  const pastRef = useRef<PageDraftSnapshot[]>([]);
  const futureRef = useRef<PageDraftSnapshot[]>([]);
  const debounceRef = useRef<number | null>(null);
  const [historyRevision, setHistoryRevision] = useState(0);
  const [canUndo, setCanUndo] = useState(false);
  const [canRedo, setCanRedo] = useState(false);

  const bump = () => {
    setCanUndo(pastRef.current.length > 0);
    setCanRedo(futureRef.current.length > 0);
    setHistoryRevision((value) => value + 1);
  };

  const flushDebounced = useCallback(() => {
    if (debounceRef.current != null) {
      window.clearTimeout(debounceRef.current);
      debounceRef.current = null;
    }
  }, []);

  const recordStable = useCallback((next: PageDraftSnapshot) => {
    if (JSON.stringify(stableRef.current) === JSON.stringify(next)) return;
    pastRef.current = [...pastRef.current.slice(-(MAX_HISTORY - 1)), stableRef.current];
    futureRef.current = [];
    stableRef.current = next;
    bump();
  }, []);

  const update = useCallback(
    (next: PageDraftSnapshot) => {
      setSnapshot(next);
      bump();
      flushDebounced();
      debounceRef.current = window.setTimeout(() => {
        debounceRef.current = null;
        recordStable(next);
      }, HISTORY_DEBOUNCE_MS);
    },
    [flushDebounced, recordStable],
  );

  const replace = useCallback(
    (next: PageDraftSnapshot, options?: { recordHistory?: boolean }) => {
      flushDebounced();
      if (options?.recordHistory) {
        pastRef.current = [...pastRef.current.slice(-(MAX_HISTORY - 1)), snapshot];
        futureRef.current = [];
      }
      stableRef.current = next;
      setSnapshot(next);
      bump();
    },
    [flushDebounced, snapshot],
  );

  const undo = useCallback((): PageDraftSnapshot | null => {
    flushDebounced();
    const previous = pastRef.current[pastRef.current.length - 1];
    if (!previous) return null;
    pastRef.current = pastRef.current.slice(0, -1);
    futureRef.current = [snapshot, ...futureRef.current];
    stableRef.current = previous;
    setSnapshot(previous);
    bump();
    return previous;
  }, [flushDebounced, snapshot]);

  const redo = useCallback((): PageDraftSnapshot | null => {
    flushDebounced();
    const next = futureRef.current[0];
    if (!next) return null;
    futureRef.current = futureRef.current.slice(1);
    pastRef.current = [...pastRef.current, snapshot];
    stableRef.current = next;
    setSnapshot(next);
    bump();
    return next;
  }, [flushDebounced, snapshot]);

  const reset = useCallback(
    (next: PageDraftSnapshot) => {
      flushDebounced();
      pastRef.current = [];
      futureRef.current = [];
      stableRef.current = next;
      setSnapshot(next);
      bump();
    },
    [flushDebounced],
  );

  return {
    snapshot,
    update,
    replace,
    undo,
    redo,
    reset,
    canUndo,
    canRedo,
    historyRevision,
  };
}
