import { spawn } from 'node:child_process';
import { appendAudit } from '../../runtime.js';
import { createApprovalRequest, consumeApprovalRequest } from '../../approvals.js';
import { classifyCommand, redactToolOutput, resolveSafePath } from '../safety.js';

function cap(text) {
  return redactToolOutput(String(text || '').slice(0, Number(process.env.MADARX_TOOL_OUTPUT_MAX_BYTES || 50000)));
}

function sameArgs(left, right) {
  return JSON.stringify(left || []) === JSON.stringify(right || []);
}

export function terminalRunPreflight(input = {}) {
  const command = String(input.command || '');
  const args = Array.isArray(input.args) ? input.args.map(String) : [];
  const { path: cwd } = resolveSafePath(input.cwd || '.');
  const classified = classifyCommand(command, args);
  if (classified.blocked) throw new Error(classified.reason);
  return { command, args, cwd, classified };
}

export async function terminalRun(input = {}, options = {}) {
  const { command, args, cwd, classified } = terminalRunPreflight(input);
  if (classified.approval_required && !options.owner_trusted && !consumeApprovalRequest(input.approval_id, {
    action_type: 'terminal_run',
    matches: payload => payload.command === command && sameArgs(payload.args, args) && payload.cwd === cwd,
  })) {
    const req = createApprovalRequest({
      action_type: 'terminal_run',
      risk: classified.risk,
      summary: `Run ${command} ${args.join(' ')}`.trim(),
      payload: { command, args, cwd, reason: classified.reason },
    });
    return { ok: false, status: 'approval_required', approval_request_id: req.id, reason: classified.reason };
  }
  const timeoutMs = Math.max(1, Math.min(600000, Number(input.timeout_ms || process.env.MADARX_TERMINAL_TIMEOUT_MS || 180000)));
  const started = Date.now();
  return await new Promise(resolve => {
    let stdout = '';
    let stderr = '';
    const child = spawn(command, args, { cwd, shell: false, stdio: ['ignore', 'pipe', 'pipe'] });
    const timer = setTimeout(() => child.kill('SIGTERM'), timeoutMs);
    child.stdout.on('data', chunk => { stdout += chunk; });
    child.stderr.on('data', chunk => { stderr += chunk; });
    child.on('error', error => {
      clearTimeout(timer);
      appendAudit({ type: 'local.terminal_run', summary: `terminal_run failed: ${command}`, status: 'failed', meta: { command, args, cwd, error: error.message } });
      resolve({ ok: false, tool: 'terminal_run', status: 'failed', command, args, cwd, error: error.message, stdout: cap(stdout), stderr: cap(stderr) });
    });
    child.on('close', (code, signal) => {
      clearTimeout(timer);
      const status = signal ? 'timeout' : code === 0 ? 'complete' : 'failed';
      appendAudit({ type: 'local.terminal_run', summary: `terminal_run ${status}: ${command}`, status, meta: { command, args, cwd, exit_code: code, signal, duration_ms: Date.now() - started, stdout_preview: cap(stdout), stderr_preview: cap(stderr) } });
      resolve({ ok: code === 0 && !signal, tool: 'terminal_run', status, command, args, cwd, exit_code: code, signal, stdout: cap(stdout), stderr: cap(stderr), duration_ms: Date.now() - started });
    });
  });
}
