import { spawn } from 'node:child_process';
import type { ChildProcess } from 'node:child_process';

import type { AdapterOutcome, AdapterRequest, ProcessAdapter } from './types.js';

/**
 * Default thin process adapter (spawn-based). Runs a command to completion with
 * a hard timeout and reports exit code + captured output — it owns NO verdict
 * logic. Interpreting the outcome is always the check implementation's job.
 */
export function createSpawnProcessAdapter(): ProcessAdapter {
  return {
    run(request: AdapterRequest): Promise<AdapterOutcome> {
      return new Promise((resolve) => {
        let child: ChildProcess;
        try {
          child = spawn(request.file, request.args, {
            cwd: request.cwd,
            stdio: ['ignore', 'pipe', 'pipe'],
          });
        } catch (error) {
          resolve({
            ok: false,
            kind: 'spawn-error',
            message: error instanceof Error ? error.message : String(error),
          });
          return;
        }

        let stdout = '';
        let stderr = '';
        let settled = false;

        const timer = setTimeout(() => {
          if (settled) return;
          settled = true;
          child.kill('SIGKILL');
          resolve({
            ok: false,
            kind: 'timeout',
            message: `process timed out after ${request.timeoutMs}ms: ${request.file}`,
          });
        }, request.timeoutMs);

        const settle = (outcome: AdapterOutcome): void => {
          if (settled) return;
          settled = true;
          clearTimeout(timer);
          resolve(outcome);
        };

        child.stdout?.on('data', (chunk: Buffer) => {
          stdout += chunk.toString('utf8');
        });
        child.stderr?.on('data', (chunk: Buffer) => {
          stderr += chunk.toString('utf8');
        });
        child.on('error', (error: Error) => {
          settle({ ok: false, kind: 'spawn-error', message: error.message });
        });
        child.on('close', (code: number | null) => {
          settle({ ok: true, exitCode: code, stdout, stderr });
        });
      });
    },
  };
}
