/**
 * tapconfirm holds the small state machine behind the "tap to confirm" controls: the
 * first tap arms a confirmation window of durationMs (during which the view shows a
 * fading ✅), a second tap within it confirms, and otherwise the window reverts. It is
 * framework agnostic — a view observes onChange and renders accordingly — so the timing
 * logic is unit-testable without a DOM. The pending timer is the only side effect.
 */
export interface TapConfirmOptions {
  /** Length of the confirmation window in milliseconds. */
  durationMs: number;
  /** Invoked once when a confirmation lands inside the window. */
  onConfirm: () => void;
  /** Invoked whenever the confirming flag flips, so a view can react. */
  onChange?: (confirming: boolean) => void;
}

/** TapConfirmController drives a single "tap to confirm" control. */
export interface TapConfirmController {
  /** Whether the confirmation window is currently open. */
  readonly confirming: boolean;
  /** Arm the confirmation window; a no-op while it is already open. */
  arm(): void;
  /** Confirm within the window: fires onConfirm once and closes the window. A no-op
   *  while the window is closed. */
  confirm(): void;
  /** Close the window without confirming (e.g. the control was disabled). */
  cancel(): void;
  /** Clear any pending timer; the controller must not be reused afterwards. */
  dispose(): void;
}

/**
 * createTapConfirm builds a TapConfirmController whose confirmation window lasts
 * durationMs. onConfirm fires once per confirmed window; onChange (when given)
 * reports every flip of the confirming flag.
 */
export function createTapConfirm(opts: TapConfirmOptions): TapConfirmController {
  let confirming = false;
  let timer: ReturnType<typeof setTimeout> | null = null;

  function clear(): void {
    if (timer !== null) {
      clearTimeout(timer);
      timer = null;
    }
  }
  function set(next: boolean): void {
    if (confirming === next) return;
    confirming = next;
    opts.onChange?.(next);
  }
  return {
    get confirming() {
      return confirming;
    },
    arm() {
      if (confirming) return;
      set(true);
      timer = setTimeout(() => {
        timer = null;
        set(false);
      }, opts.durationMs);
    },
    confirm() {
      if (!confirming) return;
      clear();
      set(false);
      opts.onConfirm();
    },
    cancel() {
      if (!confirming) return;
      clear();
      set(false);
    },
    dispose() {
      clear();
    },
  };
}
