/**
 * One-time backfill: compute composite scores for all forecasts missing them.
 * Uses PIFF + DVP + Grok confidence (DIGIMON optional if file exists).
 *
 * Usage: npx tsx src/workers/backfill-composite.ts
 */
import 'dotenv/config';
import pool from '../db';
import { loadPiffPropsForDate, getPiffPropsForGame } from '../services/piff';
import { loadDigimonPicksForDate, getDigimonForGame } from '../services/digimon';
import { getDvpForMatchup } from '../services/dvp';
import { calculateComposite } from '../services/composite-score';
import { buildIntelligence, formatStoredModelSignals, mapToLegacyComposite } from '../services/rie';
import { getCurrentEtDateKey, getEtDateKey } from '../lib/league-windows';

const RIE_ONLY = process.env.RIE_ONLY === 'true';

async function main() {
  console.log('=== COMPOSITE BACKFILL ===');

  const piffMapsByDate = new Map<string, Record<string, any>>();
  const digimonMapsByDate = new Map<string, Record<string, any>>();

  // Get all forecasts missing composite data
  const { rows } = await pool.query(`
    SELECT id, event_id, league, home_team, away_team, confidence_score,
      forecast_data
    FROM rm_forecast_cache
    WHERE composite_confidence IS NULL
      AND confidence_score IS NOT NULL
    ORDER BY created_at DESC
  `);

  console.log(`Found ${rows.length} forecasts without composite scores`);

  let updated = 0;
  let failed = 0;

  for (const row of rows) {
    try {
      const grokConfidence = row.confidence_score;
      const grokValueRating = row.forecast_data?.value_rating || 5;
      const league = row.league || '';

      // Get team shorts from event or derive from team names
      let homeShort = '';
      let awayShort = '';

      // Try to get shorts from rm_events
      const { rows: evRows } = await pool.query(
        `SELECT home_short, away_short FROM rm_events
         WHERE home_team = $1 AND away_team = $2 LIMIT 1`,
        [row.home_team, row.away_team]
      );
      if (evRows[0]) {
        homeShort = evRows[0].home_short || '';
        awayShort = evRows[0].away_short || '';
      }

      const eventDateKey = getEtDateKey(row.forecast_data?.starts_at || '') || getCurrentEtDateKey();
      if (!piffMapsByDate.has(eventDateKey)) piffMapsByDate.set(eventDateKey, loadPiffPropsForDate(eventDateKey));
      if (!digimonMapsByDate.has(eventDateKey)) digimonMapsByDate.set(eventDateKey, loadDigimonPicksForDate(eventDateKey));

      // Load signals
      const piffProps = homeShort && awayShort
        ? getPiffPropsForGame(homeShort, awayShort, piffMapsByDate.get(eventDateKey), league)
        : [];
      const digimonPicks = homeShort && awayShort && league === 'nba'
        ? getDigimonForGame(homeShort, awayShort, digimonMapsByDate.get(eventDateKey))
        : [];
      const dvpData = league === 'nba' && homeShort && awayShort
        ? await getDvpForMatchup(homeShort, awayShort, league)
        : { home: null, away: null };

      let composite;
      let storedModelSignals;

      if (RIE_ONLY) {
        const intel = await buildIntelligence({
          event: row.forecast_data || {},
          league,
          homeTeam: row.home_team,
          awayTeam: row.away_team,
          homeShort,
          awayShort,
          startsAt: row.forecast_data?.starts_at || new Date().toISOString(),
          eventId: row.event_id,
          grokConfidence,
          grokValueRating,
        });
        composite = mapToLegacyComposite(intel, grokConfidence, grokValueRating);
        storedModelSignals = formatStoredModelSignals(intel, composite);
      } else {
        composite = calculateComposite({
          grokConfidence,
          grokValueRating,
          piffProps,
          digimonPicks,
          dvp: dvpData,
          league,
        });
        storedModelSignals = composite;
      }

      await pool.query(
        `UPDATE rm_forecast_cache
         SET composite_confidence = $1, model_signals = $2, composite_version = $3
         WHERE id = $4`,
        [composite.compositeConfidence, JSON.stringify(storedModelSignals || composite), composite.compositeVersion, row.id]
      );

      const piffLabel = piffProps.length > 0 ? `PIFF:${piffProps.length}` : 'no-PIFF';
      const digiLabel = digimonPicks.length > 0 ? `DIGI:${digimonPicks.length}` : '';
      const dvpLabel = dvpData.home || dvpData.away ? 'DVP' : '';
      const signals = [piffLabel, digiLabel, dvpLabel].filter(Boolean).join('+');

      console.log(`  [OK] ${row.away_team} @ ${row.home_team} (${league}) — composite: ${Math.round(composite.compositeConfidence * 100)}% [${signals}] Cat ${composite.stormCategory}`);
      updated++;
    } catch (err: any) {
      console.error(`  [FAIL] ${row.id}: ${err.message}`);
      failed++;
    }
  }

  console.log(`\n=== DONE: ${updated} updated, ${failed} failed ===`);
  await pool.end();
}

main().catch(err => { console.error('Fatal:', err); process.exit(1); });
