/**
 * public/voice-confirm.ts — Approve confirm window controller (VC / V1b, decision B).
 *
 * PLAN_VOICE_COMMANDS.md §4: a spoken "approve" is irreversible (grants shell
 * command execution via the held permission gate), so instead of committing
 * immediately it arms a short cancellable window. `arm()` fires `onHeard`
 * synchronously (so the caller can show a "听到:批准" style affordance) and
 * starts a `windowMs` timer; if `cancel()` is not called first, `onCommit`
 * fires when the timer elapses.
 *
 * Pure timing logic — NO DOM here. The caller (tabs.ts, lane V2) owns the
 * overlay UI and calls `session.approve()` inside `onCommit`. Kept dependency-
 * free (setTimeout/clearTimeout only) so tests can use vi.useFakeTimers
 * without any DOM/WS coupling.
 */

/** Default confirm-window duration in milliseconds (PLAN_VOICE_COMMANDS §4). */
const DEFAULT_WINDOW_MS = 1500

/** A cancellable approve confirm window. Obtain via createApproveConfirm(). */
export interface ApproveConfirm {
  /**
   * Arm the confirm window: calls `onHeard` immediately, then starts a
   * `windowMs` timer that calls `onCommit` unless `cancel()` runs first.
   * Re-arming (calling `arm` again while already armed) clears the prior
   * timer, so only the latest arm's `onCommit` can ever fire.
   */
  arm(onHeard: () => void, onCommit: () => void): void
  /** Cancel the pending window, if any. No-op (does not throw) if not armed. */
  cancel(): void
  /** True while a confirm window is pending (armed and not yet committed/cancelled). */
  isArmed(): boolean
}

/**
 * Create an approve confirm window controller.
 *
 * @param windowMs  Duration of the cancellable window in ms. Default: 1500 (B).
 */
export function createApproveConfirm(windowMs: number = DEFAULT_WINDOW_MS): ApproveConfirm {
  let timer: ReturnType<typeof setTimeout> | undefined

  return {
    arm(onHeard: () => void, onCommit: () => void): void {
      if (timer !== undefined) {
        clearTimeout(timer)
      }
      onHeard()
      timer = setTimeout(() => {
        timer = undefined
        onCommit()
      }, windowMs)
    },

    cancel(): void {
      if (timer === undefined) return
      clearTimeout(timer)
      timer = undefined
    },

    isArmed(): boolean {
      return timer !== undefined
    },
  }
}
