#!/usr/bin/env node
/**
 * Tank01 daily prefetch (Session 15).
 *
 *   trigger: n8n "Morning Ops" workflow OR cron `0 7 * * *` (7am UTC,
 *            ~3am ET — before US slates publish but after overnight
 *            box scores finalize).
 *   call:    node scripts/tank01-prefetch.js [--max=80] [--dry-run]
 *
 * Why this exists: Session 14 wired `src/services/intelligence/tank01Augment.js`
 * to read `tank01:nba:*` and `tank01:mlb:*` cache keys, but nothing
 * populated those keys. Session 15 closes that loop with a daily
 * pull. The augmentor returns empty objects when the keys are
 * missing — grades still work today, they just don't include the
 * Tank01 signals. Once this script runs, grades pick up the new
 * data automatically.
 *
 * Budget posture: free tier is 1,000 req/month per RapidAPI account.
 * Even with NBA + MLB combined, one daily run targets ≤80 requests.
 * 80 * 30 days = 2,400 → would burn the budget. So default cap is 80
 * total across both sports, with a hard stop. Override via `--max`.
 *
 * What it writes:
 *   tank01:nba:games:{ymd}             — daily schedule
 *   tank01:nba:boxscore:{gameId}       — per-game final box scores
 *   tank01:nba:odds:{ymd}              — book-by-book lines
 *   tank01:mlb:scoreboard:{ymd}        — daily schedule
 *   tank01:mlb:boxscore:{gameId}       — per-game lines for finals
 *   tank01:mlb:bvp:{batterId}:{pitcherId} — historical matchups
 *
 * The adapters (Session 9) own the `cacheSet` calls — this script
 * is the orchestrator that decides what to fetch. Adapter calls are
 * idempotent: re-running the script won't double-spend the budget
 * because each adapter checks its cache before hitting RapidAPI.
 */

const tankNba = require('../src/services/adapters/tank01NbaAdapter');
const tankMlb = require('../src/services/adapters/tank01MlbAdapter');

const DEFAULT_BUDGET = 80;

function parseArgs(argv) {
  const args = { maxRequests: DEFAULT_BUDGET, dryRun: false, sports: ['nba', 'mlb'] };
  for (const a of argv.slice(2)) {
    if (a.startsWith('--max=')) {
      const n = Number(a.slice('--max='.length));
      if (Number.isFinite(n) && n > 0) args.maxRequests = Math.floor(n);
    } else if (a === '--dry-run') {
      args.dryRun = true;
    } else if (a.startsWith('--sports=')) {
      args.sports = a.slice('--sports='.length).split(',').map((s) => s.trim().toLowerCase()).filter(Boolean);
    }
  }
  return args;
}

function todayYMD() {
  const d = new Date();
  return `${d.getUTCFullYear()}${String(d.getUTCMonth() + 1).padStart(2, '0')}${String(d.getUTCDate()).padStart(2, '0')}`;
}

// Simple counter — the adapters cache internally, so a "request" here
// is "I asked the adapter for X; whether it hit cache or network is
// out of our control." For budgeting we treat every adapter call as
// 1 potential request and stop when the cap is hit.
function makeBudget(maxRequests) {
  let spent = 0;
  return {
    canSpend: () => spent < maxRequests,
    spend: () => { spent += 1; return spent; },
    spent: () => spent,
  };
}

async function processNba(args, budget, summary) {
  if (!tankNba.hasApiKey()) {
    summary.nba.skipped = 'no_key';
    return;
  }
  const ymd = todayYMD();

  if (!budget.canSpend()) return;
  budget.spend();
  const games = await tankNba.getNBAGamesForDate(ymd);
  if (!Array.isArray(games)) {
    summary.nba.games = 0;
    return;
  }
  summary.nba.games = games.length;

  // Box scores for every final game — captures the post-game stat
  // lines the augmentor surfaces to the analyze path.
  for (const g of games) {
    if (!budget.canSpend()) break;
    if (!g.gameId) continue;
    const status = String(g.gameStatus || '').toLowerCase();
    if (!status.includes('final')) continue;
    budget.spend();
    await tankNba.getNBABoxScore(g.gameId);
    summary.nba.boxscores += 1;
  }

  // One odds pull for the day — surfaces the t01_market_present
  // marker the trap detector reads alongside odds-api.
  if (budget.canSpend()) {
    budget.spend();
    await tankNba.getNBABettingOdds(ymd);
    summary.nba.odds = true;
  }
}

async function processMlb(args, budget, summary) {
  if (!tankMlb.hasApiKey()) {
    summary.mlb.skipped = 'no_key';
    return;
  }
  const ymd = todayYMD();

  if (!budget.canSpend()) return;
  budget.spend();
  const slate = await tankMlb.getMLBDailyScoreboard(ymd);
  if (!Array.isArray(slate)) {
    summary.mlb.games = 0;
    return;
  }
  summary.mlb.games = slate.length;

  // Box scores for finished games.
  for (const g of slate) {
    if (!budget.canSpend()) break;
    if (!g.gameId) continue;
    const status = String(g.gameStatus || '').toLowerCase();
    if (!status.includes('final') && !status.includes('completed')) continue;
    budget.spend();
    await tankMlb.getMLBBoxScore(g.gameId);
    summary.mlb.boxscores += 1;
  }

  // BvP matchups — only when probable pitchers are exposed on the
  // scoreboard payload (Tank01 includes them for upcoming games). We
  // can't safely run BvP without batter/pitcher IDs, so the pass is
  // a no-op until the scoreboard projection grows those fields. The
  // augmentor handles the empty-cache case gracefully.
  summary.mlb.bvp_skipped_reason = 'awaiting_starter_ids_on_scoreboard';
}

async function main(argv = process.argv) {
  const args = parseArgs(argv);
  const budget = makeBudget(args.maxRequests);
  const summary = {
    nba: { games: 0, boxscores: 0, odds: false, skipped: null },
    mlb: { games: 0, boxscores: 0, bvp: 0, skipped: null, bvp_skipped_reason: null },
    requestsSpent: 0,
    dryRun: args.dryRun,
  };

  console.log(`[tank01-prefetch] starting — max_requests=${args.maxRequests} sports=${args.sports.join(',')} dry_run=${args.dryRun}`);

  if (args.dryRun) {
    summary.nba.skipped = 'dry_run';
    summary.mlb.skipped = 'dry_run';
    console.log('[tank01-prefetch] dry-run — adapter calls suppressed');
  } else {
    if (args.sports.includes('nba')) await processNba(args, budget, summary);
    if (args.sports.includes('mlb')) await processMlb(args, budget, summary);
  }

  summary.requestsSpent = budget.spent();
  console.log(`[tank01-prefetch] done — nba.games=${summary.nba.games} nba.box=${summary.nba.boxscores} mlb.games=${summary.mlb.games} mlb.box=${summary.mlb.boxscores} spent=${summary.requestsSpent}/${args.maxRequests}`);

  return summary;
}

if (require.main === module) {
  main().then(() => process.exit(0)).catch((err) => {
    console.error('[tank01-prefetch] fatal:', err);
    process.exit(1);
  });
}

module.exports = {
  main,
  __internals: { parseArgs, makeBudget, todayYMD, processNba, processMlb, DEFAULT_BUDGET },
};
