#!/usr/bin/env node
// Spike S3 — dialogues TUI : détection (registre + écran), extraction des options, frappes de réponse.
// Types couverts : trust dialog (nouveau dossier), permission Bash, permission Write (édition),
// AskUserQuestion, refus par Esc. (Approbation de plan : suivi manuel, voir VERDICT.)
import { execSync } from 'node:child_process';
import { mkdirSync, writeFileSync, rmSync, readFileSync, existsSync } from 'node:fs';
import { join } from 'node:path';
import { homedir } from 'node:os';
import { ClaudeSession, sleep, setLogFile, log } from '../s1-resume/harness.mjs';

const CAPTURES = new URL('./captures/', import.meta.url).pathname;
mkdirSync(CAPTURES, { recursive: true });
setLogFile(join(CAPTURES, 'steps.jsonl'));

const results = {};
const save = () => writeFileSync(join(CAPTURES, 'results.json'), JSON.stringify(results, null, 1));

function freshRepo(name) {
  const d = `/tmp/spike-s3-${name}`;
  rmSync(d, { recursive: true, force: true });
  mkdirSync(d, { recursive: true });
  execSync('git init -q -b main && echo hello > witness.txt && git add -A && git -c user.email=s@s -c user.name=spike commit -qm init', { cwd: d, shell: '/bin/bash' });
  return d;
}

// Extraction des options d'un dialogue depuis l'écran (regex du design moteur).
// NB : le strip ANSI mange les espaces peints par déplacements de curseur → \s* après le point,
// et les labels peuvent être concaténés (« Yes,Itrustthisfolder »).
function extractOptions(screenTail) {
  const opts = [];
  for (const m of screenTail.matchAll(/^\s*(❯\s*)?(\d+)\.\s*(.+)$/gm)) {
    opts.push({ selected: !!m[1], n: m[2], label: m[3].trim().slice(0, 90) });
  }
  return opts;
}

async function waitWaiting(S, timeoutMs = 120000) {
  return S.until('registre → waiting', () => {
    const r = S.registry();
    return r && r.status === 'waiting' ? r : false;
  }, timeoutMs, 400);
}

// Répond au dialogue : essaie la touche chiffre seule, puis Entrée, puis flèches+Entrée.
async function respond(S, digit) {
  const tries = [];
  S.write(digit);
  await sleep(2500);
  let r = S.registry();
  tries.push({ method: `digit ${digit}`, statusAfter: r?.status });
  if (r?.status !== 'waiting') return { ok: true, tries };
  S.write('\r');
  await sleep(2500);
  r = S.registry();
  tries.push({ method: 'enter', statusAfter: r?.status });
  if (r?.status !== 'waiting') return { ok: true, tries };
  const downs = Math.max(0, parseInt(digit, 10) - 1);
  for (let i = 0; i < downs; i++) { S.write('\x1b[B'); await sleep(250); }
  S.write('\r');
  await sleep(2500);
  r = S.registry();
  tries.push({ method: `arrows x${downs} + enter`, statusAfter: r?.status });
  return { ok: r?.status !== 'waiting', tries };
}

try {
  // ---------- D1 : trust dialog sur dossier neuf + état ~/.claude.json ----------
  log('=== D1 trust dialog ===');
  const d1 = freshRepo('trust');
  const claudeJsonBefore = existsSync(join(homedir(), '.claude.json'))
    ? JSON.parse(readFileSync(join(homedir(), '.claude.json'), 'utf8')) : null;
  const trustBefore = claudeJsonBefore?.projects?.[d1] ?? null;
  const T = new ClaudeSession([], d1, 'trust', CAPTURES);
  // waitReady gère le trust par Entrée et logge l'écran du dialogue
  const regT = await T.waitReady(60000);
  await sleep(1500);
  const claudeJsonAfter = JSON.parse(readFileSync(join(homedir(), '.claude.json'), 'utf8'));
  results.d1_trust = {
    sessionStarted: !!regT,
    projectEntryBefore: trustBefore,
    projectEntryAfter: claudeJsonAfter?.projects?.[d1] ?? null,
    note: 'écran du dialogue dans trust.raw.log + steps.jsonl',
  };
  T.kill('SIGTERM');
  save();

  // ---------- D2 : permission Bash → réponse "1" (Yes) ----------
  log('=== D2 permission Bash, approbation ===');
  const d2 = freshRepo('bash');
  const P = new ClaudeSession([], d2, 'perm-bash', CAPTURES);
  await P.waitReady(60000);
  await P.type('Run the bash command `echo spike-s3-marker-xyz` and show me its output. Do not do anything else.');
  const regP = await waitWaiting(P);
  await sleep(1200); // laisse Ink finir de peindre
  const screenP = P.tail(2500);
  const optsP = extractOptions(screenP);
  const respP = await respond(P, '1');
  let completed = false;
  try { await P.waitTurnDone(120000); completed = true; } catch {}
  results.d2_permBash = {
    waitingFor: regP.waitingFor ?? null,
    options: optsP,
    screenExcerpt: screenP.slice(-1100),
    respond: respP,
    turnCompleted: completed,
    markerInOutput: P.plain().includes('spike-s3-marker-xyz'),
  };
  P.kill('SIGTERM');
  save();

  // ---------- D3 : permission Write (édition) → réponse "1" ----------
  log('=== D3 permission Write ===');
  const d3 = freshRepo('write');
  const W = new ClaudeSession([], d3, 'perm-write', CAPTURES);
  await W.waitReady(60000);
  await W.type('Create a file named s3-edit.txt containing exactly the word hello. Do not do anything else.');
  const regW = await waitWaiting(W);
  await sleep(1200);
  const screenW = W.tail(2500);
  const respW = await respond(W, '1');
  let completedW = false;
  try { await W.waitTurnDone(120000); completedW = true; } catch {}
  results.d3_permWrite = {
    waitingFor: regW.waitingFor ?? null,
    options: extractOptions(screenW),
    screenExcerpt: screenW.slice(-1100),
    respond: respW,
    turnCompleted: completedW,
    fileCreated: existsSync(join(d3, 's3-edit.txt')),
  };
  W.kill('SIGTERM');
  save();

  // ---------- D4 : refus par Esc ----------
  log('=== D4 refus par Esc ===');
  const d4 = freshRepo('deny');
  const X = new ClaudeSession([], d4, 'deny-esc', CAPTURES);
  await X.waitReady(60000);
  await X.type('Run the bash command `echo should-be-denied` and show me its output. Do not do anything else.');
  await waitWaiting(X);
  await sleep(1200);
  X.write('\x1b'); // Esc
  await sleep(3500);
  const regX = X.registry();
  results.d4_denyEsc = {
    statusAfterEsc: regX?.status,
    denied: !X.plain().includes('should-be-denied\r\nshould-be-denied') /* marqueur non exécuté */,
    screenTail: X.tail(900),
  };
  X.kill('SIGTERM');
  save();

  // ---------- D5 : AskUserQuestion ----------
  log('=== D5 AskUserQuestion ===');
  const d5 = freshRepo('ask');
  const Q = new ClaudeSession([], d5, 'ask-question', CAPTURES);
  await Q.waitReady(60000);
  await Q.type('Use the AskUserQuestion tool to ask me whether I prefer red or blue. Offer exactly those two options.');
  const regQ = await waitWaiting(Q);
  await sleep(1200);
  const screenQ = Q.tail(2500);
  // Sur AskUserQuestion : essaie flèche bas + Entrée (sélection de la 2e option)
  Q.write('\x1b[B'); await sleep(400); Q.write('\r');
  await sleep(3000);
  let regQ2 = Q.registry();
  const arrowsWorked = regQ2?.status !== 'waiting';
  let digitTried = null;
  if (!arrowsWorked) { Q.write('2'); await sleep(2500); regQ2 = Q.registry(); digitTried = regQ2?.status; }
  let completedQ = false;
  try { await Q.waitTurnDone(90000); completedQ = true; } catch {}
  results.d5_askUserQuestion = {
    waitingFor: regQ.waitingFor ?? null,
    options: extractOptions(screenQ),
    screenExcerpt: screenQ.slice(-1100),
    arrowsEnterWorked: arrowsWorked,
    digitFallbackStatus: digitTried,
    turnCompleted: completedQ,
  };
  Q.kill('SIGTERM');
  save();

  log('=== S3 dialogues terminé ===');
} catch (err) {
  results.fatalError = String(err);
  save();
  log('ERREUR FATALE', { err: String(err) });
} finally {
  save();
  process.exit(0);
}
