import fs from 'node:fs';
import path from 'node:path';
import { spawn } from 'node:child_process';
import { validateClaudeCommand } from '@/lib/claude-runner.js';
import { getRun, updateRun, appendAudit, recordArtifact } from '@/lib/runtime.js';
import { ARTIFACTS_DIR, ensureDirs, relToRoot } from '@/lib/paths.js';
import { requireSession } from '@/lib/auth.js';

export const runtime = 'nodejs';
export const dynamic = 'force-dynamic';

function sse(encoder, ctrl, event, data) {
  ctrl.enqueue(encoder.encode(`event: ${event}\ndata: ${JSON.stringify(data)}\n\n`));
}

function whichClaude() {
  return new Promise(resolve => {
    const child = spawn('which', ['claude'], { shell: false });
    let out = '';
    child.stdout.on('data', chunk => { out += chunk.toString(); });
    child.on('error', () => resolve(null));
    child.on('close', code => resolve(code === 0 ? out.trim() : null));
  });
}

function saveOutputArtifact(run, output) {
  ensureDirs();
  const stamp = new Date().toISOString().replace(/[:.]/g, '-');
  const file = path.join(ARTIFACTS_DIR, `claude-run-${run.id}-${stamp}.txt`);
  fs.writeFileSync(file, output || '', 'utf8');
  const file_path = relToRoot(file);
  recordArtifact({
    run_id: run.id,
    agent_id: run.agent_id,
    file_path,
    kind: 'text',
    title: `Claude CLI output: ${run.title || run.id}`,
    content: output || '',
  });
  return file_path;
}

export async function POST(req) {
  try {
    await requireSession(req);
  } catch (err) {
    if (err && typeof err === 'object' && err.status) {
      return Response.json({ error: err.error || 'unauthorized' }, { status: err.status });
    }
    throw err;
  }

  let body;
  try {
    body = await req.json();
  } catch {
    return Response.json({ ok: false, error: 'Invalid JSON' }, { status: 400 });
  }

  const runId = String(body.run_id || body.runId || '').trim();
  if (!runId) return Response.json({ ok: false, error: 'run_id required' }, { status: 400 });

  const run = getRun(runId);
  if (!run) return Response.json({ ok: false, error: 'run not found' }, { status: 404 });
  if (run.kind !== 'claude-code-dry-run') {
    return Response.json({ ok: false, error: 'run is not a Claude Code dry run' }, { status: 400 });
  }
  if (!run.command) return Response.json({ ok: false, error: 'run has no command to approve' }, { status: 400 });
  if (run.status && !['approval-needed', 'failed'].includes(run.status)) {
    return Response.json({ ok: false, error: `run status is ${run.status}; cannot approve` }, { status: 409 });
  }

  let parsed;
  try {
    parsed = validateClaudeCommand(run.command);
  } catch (error) {
    updateRun(run.id, { status: 'rejected', preview: error.message });
    appendAudit({ type: 'claude.approve.rejected', summary: error.message, status: 'rejected', meta: { run_id: run.id } });
    return Response.json({ ok: false, error: error.message }, { status: 400 });
  }

  const cliPath = await whichClaude();
  if (!cliPath) {
    return Response.json({
      ok: false,
      error: 'claude CLI not found',
      setup: 'Install Claude Code and make sure the `claude` binary is available on PATH.',
    }, { status: 404 });
  }

  const encoder = new TextEncoder();
  const stream = new ReadableStream({
    start(ctrl) {
      let output = '';
      let stderr = '';
      let closed = false;
      updateRun(run.id, { status: 'running', preview: `Executing approved command:\n${run.command}` });
      appendAudit({ type: 'claude.approve.start', summary: 'Approved Claude CLI run started', status: 'running', meta: { run_id: run.id } });
      sse(encoder, ctrl, 'start', { ok: true, run_id: run.id, command: run.command });

      const child = spawn('claude', parsed.args, {
        shell: false,
        cwd: process.cwd(),
        env: { ...process.env },
      });

      child.stdout.on('data', chunk => {
        const text = chunk.toString();
        output += text;
        for (const line of text.split(/\r?\n/).filter(Boolean)) {
          sse(encoder, ctrl, 'stdout', { type: 'stdout', line });
        }
      });

      child.stderr.on('data', chunk => {
        const text = chunk.toString();
        stderr += text;
        for (const line of text.split(/\r?\n/).filter(Boolean)) {
          sse(encoder, ctrl, 'stderr', { type: 'stderr', line });
        }
      });

      child.on('error', error => {
        if (closed) return;
        closed = true;
        const message = error.message || 'failed to start claude CLI';
        updateRun(run.id, { status: 'failed', preview: message });
        appendAudit({ type: 'claude.approve.error', summary: message, status: 'failed', meta: { run_id: run.id } });
        sse(encoder, ctrl, 'error', { type: 'error', message });
        ctrl.close();
      });

      child.on('close', code => {
        if (closed) return;
        closed = true;
        const combined = output + (stderr ? `\n\n[stderr]\n${stderr}` : '');
        const artifact = saveOutputArtifact(run, combined);
        const status = code === 0 ? 'complete' : 'failed';
        updateRun(run.id, {
          status,
          artifact,
          preview: combined.slice(0, 1000) || `Claude CLI exited with code ${code}`,
        });
        appendAudit({
          type: 'claude.approve.complete',
          summary: `Claude CLI run ${status}`,
          status,
          meta: { run_id: run.id, exit_code: code, artifact },
        });
        sse(encoder, ctrl, 'done', { type: 'done', ok: code === 0, run_id: run.id, status, exit_code: code, artifact });
        ctrl.close();
      });
    },
  });

  return new Response(stream, {
    headers: {
      'Content-Type': 'text/event-stream',
      'Cache-Control': 'no-cache, no-transform',
      Connection: 'keep-alive',
      'X-Accel-Buffering': 'no',
    },
  });
}
