#!/usr/bin/env node
/**
 * Pure reader of router-gate v4 runtime signals, scoped to one episode's
 * [startMs, endMs] turn window. Feeds observer-transcript-parser (new
 * episode.v4_signals block + task_cost.judge_spend_usd). No exec/exit.
 *
 * Sources (all in ~/.claude/runtime/, per-session):
 *   - rationalization-flags-<sess>.jsonl  ({ts, kind, evidence})
 *   - llm-judge-verdicts-<sess>.jsonl     ({ts, tool, verdict})
 *   - safe-baseline-actions-<sess>.jsonl  ({ts, tool, action})
 *   - llm-judge-budget-<sess>.json        ({calls})
 */
import { readFileSync, existsSync } from 'node:fs';
import { join } from 'node:path';
import { homedir } from 'node:os';

function rtDir(override) {
  return override || join(homedir(), '.claude', 'runtime');
}

function readJsonl(path) {
  if (!existsSync(path)) return [];
  try {
    return readFileSync(path, 'utf-8').split('\n').filter(Boolean).map((l) => {
      try { return JSON.parse(l); } catch { return null; }
    }).filter(Boolean);
  } catch { return []; }
}

function inWindow(rec, startMs, endMs) {
  const ms = Date.parse(rec && rec.ts);
  return Number.isFinite(ms) && ms >= startMs && ms <= endMs;
}

// Severity order: hard_block > soft_flag > allow.
const ACTION_RANK = { allow: 0, soft_flag: 1, hard_block: 2 };
function worstAction(records) {
  let best = null;
  for (const r of records) {
    const a = r && r.action;
    if (a == null) continue;
    if (best === null || (ACTION_RANK[a] ?? -1) > (ACTION_RANK[best] ?? -1)) best = a;
  }
  return best;
}

export function extractV4Signals(sessionId, { startMs, endMs, baseDir } = {}) {
  const dir = rtDir(baseDir);
  const sess = sessionId || 'unknown';

  const flags = readJsonl(join(dir, `rationalization-flags-${sess}.jsonl`))
    .filter((r) => inWindow(r, startMs, endMs));

  const verdicts = readJsonl(join(dir, `llm-judge-verdicts-${sess}.jsonl`))
    .filter((r) => inWindow(r, startMs, endMs));
  const judge_verdict = verdicts.length ? (verdicts[verdicts.length - 1].verdict ?? null) : null;

  const actions = readJsonl(join(dir, `safe-baseline-actions-${sess}.jsonl`))
    .filter((r) => inWindow(r, startMs, endMs));
  const safe_baseline_action = worstAction(actions);

  let judge_calls = 0;
  const bp = join(dir, `llm-judge-budget-${sess}.json`);
  if (existsSync(bp)) {
    try { judge_calls = Number(JSON.parse(readFileSync(bp, 'utf-8')).calls) || 0; } catch { /* keep 0 */ }
  }

  return {
    rationalization_flag_count: flags.length,
    judge_verdict,
    safe_baseline_action,
    judge_calls,
  };
}
