import { useEffect, useRef } from 'react';

/**
 * Lightweight registry for "this view has unsaved changes" checkers.
 *
 * Components that hold editable drafts (Settings, etc.) call `useUnsavedGuard`
 * with their current dirty flag. While ANY registered view reports dirty,
 * `hasUnsavedChanges()` returns true and the browser's beforeunload prompt
 * is enabled, so callers performing in-app navigation can wrap their action
 * with `confirmDiscardUnsaved()` to give the user a chance to back out.
 */
type Checker = () => boolean;

const checkers = new Set<Checker>();

export function hasUnsavedChanges(): boolean {
  for (const checker of checkers) {
    if (checker()) return true;
  }
  return false;
}

/**
 * Returns true if it's safe to proceed (either nothing dirty, or the user
 * accepted the confirm prompt). Returns false if the user cancelled.
 */
export function confirmDiscardUnsaved(
  message = '未保存の変更があります。保存せずに移動しますか？',
): boolean {
  return !hasUnsavedChanges() || window.confirm(message);
}

/**
 * Register an unsaved-changes checker for the lifetime of the calling
 * component. While `isDirty` is true, the browser's beforeunload prompt is
 * also armed, so closing the tab / refreshing / hitting back will warn.
 */
export function useUnsavedGuard(isDirty: boolean): void {
  const dirtyRef = useRef(isDirty);
  dirtyRef.current = isDirty;

  useEffect(() => {
    const checker: Checker = () => dirtyRef.current;
    checkers.add(checker);
    return () => {
      checkers.delete(checker);
    };
  }, []);

  useEffect(() => {
    if (!isDirty) return;
    const handler = (e: BeforeUnloadEvent) => {
      e.preventDefault();
      // Modern browsers ignore the message but require returnValue to be set.
      e.returnValue = '';
    };
    window.addEventListener('beforeunload', handler);
    return () => {
      window.removeEventListener('beforeunload', handler);
    };
  }, [isDirty]);
}
