import { execFile, type ExecFileOptions } from 'child_process'

/**
 * Normalized result of a one-shot child-process run. `ok` is true on a clean
 * exit-0; `timedOut` distinguishes a kill-by-timeout (Node sets `err.killed` when
 * it terminates the process on the `timeout` option) from an ordinary non-zero
 * exit, so callers can show "Timed out …" versus the captured stderr.
 */
export interface ExecResult {
  ok: boolean
  stdout: string
  stderr: string
  timedOut: boolean
  /** The raw error on a non-zero exit / spawn failure (undefined when ok). */
  error?: Error
}

/**
 * `execFile` as a promise that never rejects (audit SIMP2/CC4/CC5). Replaces the
 * six hand-rolled `new Promise((resolve) => execFile(…, cb))` wrappers across
 * probe / ytdlp (×2) / ffmpeg / indexer / download.probeMeta — each caller now maps
 * this normalized result to its own shape instead of repeating the `err.killed`
 * timeout check and the resolve/reject plumbing. (schedule.ts keeps its own wrapper:
 * it needs the numeric exit code, which this helper deliberately doesn't surface.)
 *
 * `windowsHide` defaults on (every caller wants the console window suppressed);
 * pass `timeout` / `maxBuffer` per call exactly as before.
 */
export function execFileAsync(
  file: string,
  args: readonly string[],
  options: ExecFileOptions = {}
): Promise<ExecResult> {
  return new Promise((resolve) => {
    execFile(file, [...args], { windowsHide: true, ...options }, (err, stdout, stderr) => {
      // With the default (utf8) encoding stdout/stderr are strings, but ExecFileOptions
      // permits a Buffer encoding, so normalize defensively.
      const out = typeof stdout === 'string' ? stdout : stdout.toString()
      const errOut = typeof stderr === 'string' ? stderr : stderr.toString()
      if (err) {
        resolve({
          ok: false,
          stdout: out,
          stderr: errOut,
          timedOut: (err as { killed?: boolean }).killed === true,
          error: err
        })
      } else {
        resolve({ ok: true, stdout: out, stderr: errOut, timedOut: false })
      }
    })
  })
}
