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

const snapshot = (value, omit) =>
  JSON.stringify(value, (key, val) => (omit.includes(key) ? undefined : val));

/**
 * Tracks whether an editor's state differs from its last saved version.
 *
 * The baseline is captured after a short delay, because editors fill in
 * defaults and derived values in effects right after they mount. Call
 * `markSaved` after a successful save to take a fresh baseline.
 *
 * @param {object} value    The editor state to watch.
 * @param {string[]} omit   Keys to ignore anywhere in the state (derived data, timestamps).
 */
const useUnsavedChanges = (value, omit = [], settleMs = 300) => {
  const [baseline, setBaseline] = useState(null);
  const latest = useRef(value);
  latest.current = value;
  const omitRef = useRef(omit);

  const markSaved = useCallback(() => {
    const timer = setTimeout(
      () => setBaseline(snapshot(latest.current, omitRef.current)),
      settleMs,
    );
    return () => clearTimeout(timer);
  }, [settleMs]);

  useEffect(() => markSaved(), [markSaved]);

  const isDirty = baseline !== null && snapshot(value, omit) !== baseline;

  // Browser prompt when closing the tab or leaving through WP admin links.
  useEffect(() => {
    if (!isDirty) return;
    const handleBeforeUnload = (event) => {
      event.preventDefault();
      event.returnValue = "";
    };
    window.addEventListener("beforeunload", handleBeforeUnload);
    return () => window.removeEventListener("beforeunload", handleBeforeUnload);
  }, [isDirty]);

  return { isDirty, markSaved };
};

export default useUnsavedChanges;
