// Garde-fou navigation : popin si modifications non enregistrées.
const { useState, useEffect, useCallback, useRef, useContext, createContext } = React;

const UnsavedChangesContext = createContext(null);

function UnsavedLeaveDialog({ open, busy, onCancel, onSave, onDiscard }) {
  if (!open) return null;
  return (
    <div className="ob-quit-overlay" onClick={busy ? undefined : onCancel} role="presentation">
      <div
        className="ob-quit-dialog"
        role="dialog"
        aria-modal="true"
        aria-labelledby="sg-unsaved-title"
        onClick={(e) => e.stopPropagation()}
      >
        <h3 id="sg-unsaved-title">Modifications non enregistrées</h3>
        <p>Voulez-vous enregistrer vos modifications avant de quitter cette page ?</p>
        <div className="ob-quit-actions">
          <button type="button" className="ob-btn-primary" onClick={onSave} disabled={busy}>
            {busy ? (<><span className="sg-spinner sm" /> Enregistrement…</>) : "Enregistrer"}
          </button>
          <button type="button" className="ob-btn-ghost" onClick={onDiscard} disabled={busy}>
            Partir sans enregistrer
          </button>
          <button type="button" className="ob-btn-ghost ob-quit-cancel" onClick={onCancel} disabled={busy}>
            Annuler
          </button>
        </div>
      </div>
    </div>
  );
}

function UnsavedChangesProvider({ children, onNavigate }) {
  const guardRef = useRef({ isDirty: false, save: null, discard: null });
  const [pending, setPending] = useState(null);
  const [busy, setBusy] = useState(false);
  const [dirty, setDirty] = useState(false);

  const registerGuard = useCallback((guard) => {
    guardRef.current = guard || { isDirty: false, save: null, discard: null };
    setDirty(!!guard?.isDirty);
  }, []);

  const requestNavigate = useCallback(
    (next) => {
      if (guardRef.current.isDirty) {
        setPending(next);
        return;
      }
      onNavigate(next);
    },
    [onNavigate],
  );

  const cancelLeave = useCallback(() => setPending(null), []);

  const discardLeave = useCallback(() => {
    const next = pending;
    setPending(null);
    guardRef.current.discard?.();
    if (next != null) onNavigate(next);
  }, [pending, onNavigate]);

  const saveAndLeave = useCallback(async () => {
    const next = pending;
    if (!guardRef.current.save) {
      discardLeave();
      return;
    }
    setBusy(true);
    try {
      await guardRef.current.save();
      setPending(null);
      if (next != null) onNavigate(next);
    } catch (e) {
      alert(e?.message || "Enregistrement impossible.");
    } finally {
      setBusy(false);
    }
  }, [pending, onNavigate, discardLeave]);

  useEffect(() => {
    if (!dirty) return undefined;
    function onBeforeUnload(e) {
      e.preventDefault();
      e.returnValue = "";
    }
    window.addEventListener("beforeunload", onBeforeUnload);
    return () => window.removeEventListener("beforeunload", onBeforeUnload);
  }, [dirty]);

  const value = { registerGuard, requestNavigate };

  return (
    <UnsavedChangesContext.Provider value={value}>
      {children}
      <UnsavedLeaveDialog
        open={pending != null}
        busy={busy}
        onCancel={cancelLeave}
        onSave={() => void saveAndLeave()}
        onDiscard={discardLeave}
      />
    </UnsavedChangesContext.Provider>
  );
}

function useUnsavedChangesGuard({ isDirty, onSave, onDiscard }) {
  const ctx = useContext(UnsavedChangesContext);
  useEffect(() => {
    if (!ctx) return undefined;
    ctx.registerGuard(
      isDirty
        ? { isDirty: true, save: onSave || null, discard: onDiscard || null }
        : null,
    );
    return () => ctx.registerGuard(null);
  }, [ctx, isDirty, onSave, onDiscard]);
}

function useGuardedNavigate() {
  const ctx = useContext(UnsavedChangesContext);
  return ctx?.requestNavigate;
}

Object.assign(window, {
  UnsavedChangesProvider,
  useUnsavedChangesGuard,
  useGuardedNavigate,
});
