'use strict';

const fs = require('node:fs/promises');
const os = require('node:os');
const path = require('node:path');
const crypto = require('node:crypto');
const { normalizeAgentMode, normalizeAgentRunResult } = require('./contract');
const {
  sanitizeEnv,
  runCommand,
  getChangedFiles,
  getDiff,
  parseJsonLines,
} = require('./utils');

const CODEX_CLI_MODEL_RE = /^(gpt-5\.5|gpt-5\.4|gpt-5\.4-mini|gpt-5\.3-codex)$/i;

function buildCodexArgs(prompt, opts = {}) {
  const args = [
    'exec',
    '--skip-git-repo-check',
    '--ephemeral',
  ];
  if (opts.json !== false) args.push('--json');
  if (opts.outputLastMessage) args.push('--output-last-message', opts.outputLastMessage);
  if (opts.fullAuto) args.push('--full-auto');
  if (opts.sandbox) args.push('--sandbox', opts.sandbox);
  if (opts.model && CODEX_CLI_MODEL_RE.test(opts.model)) args.push('-m', opts.model);
  args.push(prompt);
  return args;
}

function extractCodexFinalMessage(events, fallbackText) {
  for (let i = events.length - 1; i >= 0; i--) {
    const event = events[i];
    const item = event.item || event.params?.item || event.msg?.item;
    const text = item?.text || item?.message || event.final_message || event.message;
    if (item?.type === 'agent_message' && text) return text;
    if (event.type === 'item.completed' && item?.type === 'agent_message' && text) return text;
  }
  return fallbackText || '';
}

async function runTask(options = {}) {
  const prompt = options.prompt || '';
  const cwd = options.cwd || process.cwd();
  const mode = normalizeAgentMode(options.mode);
  const sessionId = options.sessionId || crypto.randomUUID();
  const dir = await fs.mkdtemp(path.join(os.tmpdir(), 'walle-codex-runner-'));
  const outputLastMessage = path.join(dir, 'final-message.txt');

  try {
    const args = buildCodexArgs(prompt, { ...options, outputLastMessage });
    const env = options.useApiKeyAuth
      ? { ...process.env }
      : sanitizeEnv(process.env, { deleteKeys: ['OPENAI_API_KEY'] });

    const result = await runCommand('codex', args, {
      cwd,
      env,
      timeoutMs: options.timeoutMs || 300000,
      maxOutputBytes: options.maxOutputBytes,
    });

    let finalMessage = '';
    try {
      finalMessage = (await fs.readFile(outputLastMessage, 'utf8')).trim();
    } catch {
      // Older or mocked CLIs may not write the final-message file.
    }

    const events = parseJsonLines(result.output);
    if (!finalMessage) finalMessage = extractCodexFinalMessage(events, result.output.trim());
    const changedFiles = getChangedFiles(cwd);

    return normalizeAgentRunResult({
      runnerId: 'codex',
      providerType: 'openai',
      mode,
      success: result.success,
      output: finalMessage || result.output,
      stderr: result.timedOut
        ? `${result.stderr}${result.stderr ? '\n' : ''}Timed out after ${options.timeoutMs || 300000}ms`
        : result.stderr,
      sessionId,
      resumeHandle: sessionId,
      exitCode: result.exitCode,
      timedOut: result.timedOut,
      signal: result.signal,
      latencyMs: result.latencyMs,
      events,
      changedFiles,
      diff: getDiff(cwd, changedFiles),
      artifacts: {
        command: 'codex',
        outputFormat: 'jsonl',
      },
    });
  } finally {
    await fs.rm(dir, { recursive: true, force: true }).catch(() => {});
  }
}

module.exports = {
  id: 'codex',
  providerType: 'openai',
  kind: 'cli-agent',
  capabilities: {
    modes: ['plan', 'build', 'review', 'analysis'],
    jsonOutput: true,
    streamJson: true,
    resume: true,
    worktree: false,
  },
  buildCodexArgs,
  extractCodexFinalMessage,
  runTask,
};
