#!/usr/bin/env node
/**
 * RUN THE BACKTEST HARNESS (Session 64).
 *
 * Joins model_snapshots (the model's INPUTS + prediction) to ledger_entries
 * (the single source of truth for OUTCOMES) on the natural key, and runs the
 * harness. Outcomes are NEVER denormalized onto snapshots.
 *
 * Join key: (sport, player_key, stat, line, side, game_date).
 * Verified empirically: 283 clean 1:1 joins, ZERO ambiguity. `game_id` is NOT
 * usable — 400/550 snapshot rows carry `UNK@UNK` because home/away team names
 * weren't threaded into the grader until Session 64 Order 1.6.
 *
 * Rows that don't join are EXPECTED, not errors: retention stores BOTH sides
 * of every prop plus refusals, while the ledger keeps only the graded side of
 * non-refused props.
 *
 *   node scripts/run-backtest.js <rows.json>   # rows exported via SQL
 */

const harness = require('../src/services/backtestHarness');

const rows = JSON.parse(require('fs').readFileSync(process.argv[2], 'utf8'));
const report = harness.runBacktest(rows, {});

const pad = (s, n) => String(s).padEnd(n);
console.log('══════════ VYNDR BACKTEST HARNESS ══════════');
console.log(`generated_at : ${report.generated_at}`);
console.log(`min_sample   : ${report.min_sample}`);
console.log(`VERDICT      : ${report.verdict}   (can_validate=${report.can_validate})`);
console.log('\n--- denominator ---');
Object.entries(report.counts).forEach(([k, v]) => console.log(`  ${pad(k, 22)} ${v}`));

console.log('\n--- grade buckets (4-letter) ---');
for (const b of report.grade_buckets.sort((a, z) => z.n - a.n)) {
  console.log(b.status === 'OK'
    ? `  ${pad(b.bucket, 4)} n=${pad(b.n, 5)} hit=${(b.hit_rate * 100).toFixed(1)}%  95% CI [${(b.ci_low * 100).toFixed(1)}, ${(b.ci_high * 100).toFixed(1)}]`
    : `  ${pad(b.bucket, 4)} n=${pad(b.n, 5)} INSUFFICIENT — need ${b.need} (short by ${b.short_by})`);
}

console.log('\n--- grade buckets (11-step) ---');
for (const b of report.grade_11_buckets.sort((a, z) => z.n - a.n)) {
  console.log(b.status === 'OK'
    ? `  ${pad(b.bucket, 4)} n=${pad(b.n, 5)} hit=${(b.hit_rate * 100).toFixed(1)}%`
    : `  ${pad(b.bucket, 4)} n=${pad(b.n, 5)} INSUFFICIENT`);
}

console.log(`\n--- monotonicity: ${report.monotonicity.verdict} ---`);
report.monotonicity.comparisons.forEach((c) => console.log(
  `  ${c.higher} vs ${c.lower}: ${c.distinguishable ? (c.holds ? 'HOLDS' : 'BROKEN') : 'not distinguishable on this sample'}`,
));

console.log('\n--- probability calibration ---');
console.log(report.probability.n
  ? `  n=${report.probability.n}  Brier=${report.probability.brier.toFixed(4)}`
  : '  n=0 — no stored p_win on any joinable settled row');

console.log('\n--- strata (never mixed) ---');
report.strata.forEach((s) => console.log(`  ${pad(s.sport, 6)} ${pad(s.model_version, 24)} n=${s.n}`));

require('fs').writeFileSync(
  process.argv[3] || '/tmp/backtest-report.json',
  JSON.stringify(report, null, 2),
);
console.log(`\nfull report → ${process.argv[3] || '/tmp/backtest-report.json'}`);
