#!/usr/bin/env node
/**
 * MANUAL REGRADE TRIGGER (Session 64, Phase 1).
 *
 * Fires the snapshot pipeline on demand so a fix can be verified in minutes
 * instead of waiting for the 14/19/22/01/03 UTC cron. Runs INSIDE the API
 * container, so it needs no VYNDR_INTERNAL_KEY and no open HTTP surface:
 *
 *     docker exec <api-container> node scripts/run-snapshot.js mlb
 *     docker exec <api-container> node scripts/run-snapshot.js all
 *     docker exec <api-container> node scripts/run-snapshot.js mlb --settle
 *
 * It runs the SAME `snapshotService.runSnapshot` the cron runs — including the
 * team-stats refresh that populates `opp_rank_stat` — so what you verify is what
 * production does, not a parallel code path.
 *
 * `--settle` additionally runs the outcome + ledger settle pass first, matching
 * the scheduler's real order (settle yesterday, then grade today).
 *
 * Prints a grade-distribution summary at the end, which is the thing you
 * actually want when verifying a grading change.
 */

const args = process.argv.slice(2);
const target = (args[0] || 'all').toLowerCase();
const doSettle = args.includes('--settle');

function tally(list, key) {
  return list.reduce((m, g) => { const k = g?.[key] ?? 'null'; m[k] = (m[k] || 0) + 1; return m; }, {});
}

(async () => {
  const snapshotService = require('../src/services/snapshotService');

  if (doSettle) {
    console.log('--- settle pass (outcomes + ledger) ---');
    try {
      const outcomeService = require('../src/services/outcomeService');
      for (const sp of ['mlb', 'wnba', 'nba']) {
        try {
          const r = await outcomeService.settleSnapshot(sp);
          console.log(`  ${sp}: ${JSON.stringify(r)}`);
        } catch (e) { console.warn(`  ${sp}: settle failed — ${e.message}`); }
      }
      const ledger = require('../src/services/ledgerService');
      if (typeof ledger.settleLedger === 'function') {
        for (const sp of ['mlb', 'wnba']) {
          try { console.log(`  ledger ${sp}: ${JSON.stringify(await ledger.settleLedger(sp))}`); }
          catch (e) { console.warn(`  ledger ${sp}: ${e.message}`); }
        }
      }
    } catch (e) { console.warn('settle pass failed:', e.message); }
  }

  const sports = target === 'all' ? ['mlb', 'wnba', 'nba', 'soccer'] : [target];
  console.log(`--- snapshot: ${sports.join(', ')} ---`);

  const results = [];
  for (const sp of sports) {
    const t = Date.now();
    try {
      const r = await snapshotService.runSnapshot(sp);
      console.log(`  ${sp}: status=${r.status} grades=${r.gradeCount}${r.reason ? ` reason=${r.reason}` : ''} (${Math.round((Date.now() - t) / 1000)}s)`);
      results.push({ sp, r });
    } catch (e) {
      console.error(`  ${sp}: THREW — ${e.message}`);
    }
  }

  // Distribution — the point of running this by hand.
  console.log('\n--- GRADE DISTRIBUTION (from the freshly written cache) ---');
  const { cacheGet } = require('../src/utils/redis');
  for (const { sp } of results) {
    try {
      const snap = await cacheGet(`snapshot:${sp}:latest`);
      const grades = (snap && Array.isArray(snap.grades)) ? snap.grades : [];
      if (!grades.length) { console.log(`  ${sp}: (no grades)`); continue; }
      const withEv = grades.filter((g) => Number.isFinite(Number(g.ev_pct))).length;
      const withP = grades.filter((g) => Number.isFinite(Number(g.p_win))).length;
      const withOpp = grades.filter((g) => g.opp_rank_stat != null).length;
      console.log(`  ${sp}: n=${grades.length} ${JSON.stringify(tally(grades, 'grade'))}`);
      console.log(`      confidence: ${JSON.stringify(tally(grades, 'confidence'))}`);
      console.log(`      p_win present: ${withP}/${grades.length} · ev_pct present: ${withEv}/${grades.length} · opp_rank on grade: ${withOpp}`);
    } catch (e) { console.warn(`  ${sp}: could not read cache — ${e.message}`); }
  }

  await new Promise((r) => process.stdout.write('', r));
  process.exit(0);
})().catch((e) => { console.error('run-snapshot failed:', e); process.exit(1); });
