#!/usr/bin/env node
/**
 * PostToolUse(AskUserQuestion) wrapper — schema bridge between Stream E
 * pure parser (askuser-answer-parser.mjs::toApprovalRecord) and Stream B
 * approval reader (shell-content-rules.mjs::loadApprovedGitOps).
 *
 * For each question/answer pair: if the answer matches a git pattern,
 * append an approve_git_operation record to
 * ~/.claude/runtime/askuser-decisions-<sess>.jsonl.
 *
 * Fail-open observability (never blocks AskUserQuestion).
 *
 * Stream H Task 6 — retires the manual approval-write workaround used by
 * the controller throughout Stream H Tasks 1-5.
 */
import { appendFileSync, mkdirSync } from 'node:fs';
import { homedir } from 'node:os';
import { join, dirname } from 'node:path';
import { toApprovalRecord, toFloorEscapeRecord, signFloorEscapeRecord } from './askuser-answer-parser.mjs';
import { resolveReceiptKey } from './receipt-key-config.mjs';

/**
 * Pure event processor for test-injection of runtimeDir + nowMs.
 *
 * @param {object} event - PostToolUse payload {session_id, tool_input, tool_response}
 * @param {object} [opts]
 * @param {string} [opts.runtimeDir] - override default ~/.claude/runtime
 * @param {number} [opts.nowMs]      - override timestamp for test determinism
 */
export function processEvent(event, { runtimeDir, nowMs, keyImpl = resolveReceiptKey, fsImpl = { appendFileSync, mkdirSync } } = {}) {
  try {
    const sessionId = event && event.session_id;
    const toolInput = event && event.tool_input;
    const toolResponse = event && event.tool_response;
    if (!sessionId || !toolInput || !toolResponse) return;

    const questions = toolInput.questions || [];
    const answers = toolResponse.answers || {};
    // M6 FIX-5: резолв ключа подписи один раз на событие (fail-safe — ошибка резолва
    // → key=null → floor_escape пишется неподписанным, PostToolUse-наблюдаемость цела).
    let key = null; try { key = keyImpl(); } catch { key = null; }

    const dir = runtimeDir || join(homedir(), '.claude', 'runtime');
    const path = join(dir, `askuser-decisions-${sessionId}.jsonl`);

    let wroteAny = false;
    for (const q of questions) {
      if (!q || !q.question) continue;
      const ans = answers[q.question];
      if (!ans) continue;
      const rec = toApprovalRecord(ans, { question: q.question, nowMs });
      // M6 FIX-5: подписываем ТОЛЬКО floor_escape (§2.2 — approve_git_operation не трогаем).
      let esc = toFloorEscapeRecord(ans, { nowMs });
      if (esc && key) esc = signFloorEscapeRecord(esc, key);
      for (const out of [rec, esc]) {
        if (!out) continue;
        if (!wroteAny) {
          try { fsImpl.mkdirSync(dirname(path), { recursive: true }); } catch { /* ignore */ }
          wroteAny = true;
        }
        try { fsImpl.appendFileSync(path, JSON.stringify(out) + '\n'); } catch { /* fail-open */ }
      }
    }
  } catch {
    // fail-open observability — never throw from PostToolUse handler
  }
}

async function main() {
  let input = '';
  for await (const chunk of process.stdin) input += chunk;
  let payload;
  try { payload = JSON.parse(input); } catch { return; }
  processEvent(payload);
}

if (import.meta.url === `file://${process.argv[1].replace(/\\/g, '/')}` || (process.argv[1] || '').endsWith('enforce-askuser-answer-parser.mjs')) {
  main().catch(() => process.exit(0)); // fail-open observability
}
