/**
 * Local shell exec on the BooCoder host (replaces deprecated ssh.ts for worktrees).
 */
import { spawn } from 'node:child_process';

export interface HostExecResult {
  exitCode: number;
  stdout: string;
  stderr: string;
}

export async function hostExec(
  command: string,
  opts?: { signal?: AbortSignal; timeoutMs?: number },
): Promise<HostExecResult> {
  return new Promise<HostExecResult>((resolve, reject) => {
    const child = spawn('bash', ['-lc', command], {
      stdio: ['pipe', 'pipe', 'pipe'],
    });

    let stdout = '';
    let stderr = '';
    let killed = false;

    child.stdout!.on('data', (chunk: Buffer) => { stdout += chunk.toString(); });
    child.stderr!.on('data', (chunk: Buffer) => { stderr += chunk.toString(); });

    const cleanup = () => {
      if (!killed) {
        killed = true;
        child.kill('SIGTERM');
      }
    };

    if (opts?.signal) {
      if (opts.signal.aborted) {
        cleanup();
        reject(new Error('host exec aborted before start'));
        return;
      }
      opts.signal.addEventListener('abort', cleanup, { once: true });
    }

    let timer: ReturnType<typeof setTimeout> | undefined;
    if (opts?.timeoutMs) {
      timer = setTimeout(() => {
        cleanup();
        reject(new Error(`host exec timed out after ${opts.timeoutMs}ms`));
      }, opts.timeoutMs);
    }

    child.on('close', (code) => {
      if (timer) clearTimeout(timer);
      if (opts?.signal) opts.signal.removeEventListener('abort', cleanup);
      resolve({ exitCode: code ?? 1, stdout, stderr });
    });

    child.on('error', (err) => {
      if (timer) clearTimeout(timer);
      if (opts?.signal) opts.signal.removeEventListener('abort', cleanup);
      reject(err);
    });

    child.stdin!.end();
  });
}
