#!/usr/bin/env npx tsx
/**
 * Fill Odds Gaps - Uses The Odds API to fill missing data after SGO backfill
 *
 * The Odds API has historical data back to June 2020, much further than SGO.
 * This script finds games missing odds and fills them using theoddsapi.
 *
 * Usage:
 *   npx tsx src/scripts/fillOddsGaps.ts                    # Fill all gaps
 *   npx tsx src/scripts/fillOddsGaps.ts --league=nba       # Single league
 *   npx tsx src/scripts/fillOddsGaps.ts --dry-run          # Preview only
 *   npx tsx src/scripts/fillOddsGaps.ts --after-sgo        # Wait for SGO backfill then run
 */

require('dotenv').config({ path: '.env.local' });
require('dotenv').config({ path: '.env' });

import { exec } from 'child_process';
import { promisify } from 'util';
import { isSportsDbEnabled, getSportsDb } from '../lib/sportsDb';

const execAsync = promisify(exec);

const THE_ODDS_API_KEY = process.env.THE_ODDS_API_KEY;
const PYTHON_SCRIPT = '/var/www/html/eventheodds/scripts/historical_backfill_theoddsapi.py';

interface GapStats {
  league: string;
  totalGames: number;
  missingSpread: number;
  missingMoneyline: number;
  missingTotal: number;
  missingAnyOdds: number;
}

interface FillResult {
  league: string;
  gamesProcessed: number;
  openingFilled: number;
  closingFilled: number;
  creditsUsed: number;
  error?: string;
}

async function getGapStats(prisma: any, league: string, startDate: string, endDate: string): Promise<GapStats> {
  const where = {
    league: league.toLowerCase(),
    gameDate: {
      gte: new Date(`${startDate}T00:00:00Z`),
      lte: new Date(`${endDate}T23:59:59Z`),
    },
  };

  const totalGames = await prisma.sportsGame.count({ where });

  const missingSpread = await prisma.sportsGame.count({
    where: { ...where, spreadHome: null },
  });

  const missingMoneyline = await prisma.sportsGame.count({
    where: { ...where, moneylineHome: null },
  });

  const missingTotal = await prisma.sportsGame.count({
    where: { ...where, total: null },
  });

  const missingAnyOdds = await prisma.sportsGame.count({
    where: {
      ...where,
      OR: [
        { spreadHome: null },
        { moneylineHome: null },
        { total: null },
      ],
    },
  });

  return {
    league,
    totalGames,
    missingSpread,
    missingMoneyline,
    missingTotal,
    missingAnyOdds,
  };
}

async function runTheoddsapiBackfill(
  league: string,
  startDate: string,
  endDate: string,
  maxGames: number = 100
): Promise<FillResult> {
  const result: FillResult = {
    league,
    gamesProcessed: 0,
    openingFilled: 0,
    closingFilled: 0,
    creditsUsed: 0,
  };

  try {
    const cmd = `python3 ${PYTHON_SCRIPT} --league=${league} --from=${startDate} --to=${endDate} --missing --max-games=${maxGames}`;
    console.log(`  Running: ${cmd}`);

    const { stdout, stderr } = await execAsync(cmd, {
      timeout: 600000, // 10 min timeout
      cwd: '/var/www/html/eventheodds',
    });

    // Parse output for stats
    const openingMatch = stdout.match(/Opening lines updated:\s*(\d+)/);
    const closingMatch = stdout.match(/Closing lines updated:\s*(\d+)/);
    const creditsMatch = stdout.match(/Credits used:\s*(\d+)/);
    const gamesMatch = stdout.match(/Processing\s*(\d+)\s*games/);

    if (openingMatch) result.openingFilled = parseInt(openingMatch[1]);
    if (closingMatch) result.closingFilled = parseInt(closingMatch[1]);
    if (creditsMatch) result.creditsUsed = parseInt(creditsMatch[1]);
    if (gamesMatch) result.gamesProcessed = parseInt(gamesMatch[1]);

    if (stderr && !stderr.includes('UserWarning')) {
      console.log(`  Warnings: ${stderr.slice(0, 200)}`);
    }

  } catch (error: any) {
    result.error = error.message || String(error);
    console.error(`  Error: ${result.error.slice(0, 200)}`);
  }

  return result;
}

async function waitForSgoBackfill(): Promise<boolean> {
  const outputFile = '/tmp/claude-1000/-home-administrator/tasks/b19661a.output';
  const maxWaitMs = 3600000; // 1 hour max wait
  const checkIntervalMs = 30000; // Check every 30 seconds

  console.log('⏳ Waiting for SGO backfill to complete...');

  const startTime = Date.now();

  while (Date.now() - startTime < maxWaitMs) {
    try {
      const { stdout } = await execAsync(`tail -10 ${outputFile} 2>/dev/null || echo "not found"`);

      if (stdout.includes('BACKFILL SUMMARY') || stdout.includes('TOTAL:')) {
        console.log('✅ SGO backfill completed!');
        return true;
      }

      // Check if process is still running
      const { stdout: psOut } = await execAsync('ps aux | grep backfillHistoricalProps | grep -v grep | wc -l');
      if (psOut.trim() === '0') {
        console.log('⚠️  SGO backfill process not running - proceeding anyway');
        return true;
      }

      const elapsed = Math.round((Date.now() - startTime) / 1000);
      process.stdout.write(`\r  Elapsed: ${elapsed}s, still running...`);

      await new Promise(resolve => setTimeout(resolve, checkIntervalMs));

    } catch {
      // File might not exist yet
      await new Promise(resolve => setTimeout(resolve, checkIntervalMs));
    }
  }

  console.log('\n⚠️  Timeout waiting for SGO backfill - proceeding anyway');
  return false;
}

function parseArgs(): {
  league?: string;
  dryRun: boolean;
  afterSgo: boolean;
  startDate?: string;
  endDate?: string;
  maxGames: number;
} {
  const args = process.argv.slice(2);
  const result = {
    dryRun: false,
    afterSgo: false,
    maxGames: 100,
  } as ReturnType<typeof parseArgs>;

  for (const arg of args) {
    if (arg.startsWith('--league=')) result.league = arg.split('=')[1].toLowerCase();
    else if (arg.startsWith('--start=')) result.startDate = arg.split('=')[1];
    else if (arg.startsWith('--end=')) result.endDate = arg.split('=')[1];
    else if (arg.startsWith('--max-games=')) result.maxGames = parseInt(arg.split('=')[1]);
    else if (arg === '--dry-run') result.dryRun = true;
    else if (arg === '--after-sgo') result.afterSgo = true;
  }

  return result;
}

async function main() {
  const args = parseArgs();

  console.log('🔧 Fill Odds Gaps - The Odds API Backfill');
  console.log('='.repeat(60));

  if (!THE_ODDS_API_KEY) {
    console.error('❌ THE_ODDS_API_KEY not set in environment');
    process.exit(1);
  }

  if (!isSportsDbEnabled()) {
    console.error('❌ SportsDB not enabled');
    process.exit(1);
  }

  // Wait for SGO backfill if requested
  if (args.afterSgo) {
    await waitForSgoBackfill();
    console.log('');
  }

  const prisma = getSportsDb();

  // Determine date range (default: last 2 years for historical coverage)
  const endDate = args.endDate || new Date().toISOString().slice(0, 10);
  const defaultStart = new Date();
  defaultStart.setFullYear(defaultStart.getFullYear() - 2);
  const startDate = args.startDate || defaultStart.toISOString().slice(0, 10);

  const leagues = args.league ? [args.league] : ['nba', 'nfl', 'nhl', 'mlb'];

  console.log(`📅 Date range: ${startDate} to ${endDate}`);
  console.log(`🏟️  Leagues: ${leagues.join(', ').toUpperCase()}`);
  console.log('');

  // First, analyze gaps
  console.log('📊 Analyzing odds gaps...\n');

  const allGaps: GapStats[] = [];
  for (const league of leagues) {
    const stats = await getGapStats(prisma, league, startDate, endDate);
    allGaps.push(stats);

    const pctMissing = stats.totalGames > 0
      ? Math.round((stats.missingAnyOdds / stats.totalGames) * 100)
      : 0;

    console.log(`  ${league.toUpperCase().padEnd(4)}: ${stats.totalGames} games, ${stats.missingAnyOdds} missing odds (${pctMissing}%)`);
    if (stats.missingAnyOdds > 0) {
      console.log(`        └─ spread: ${stats.missingSpread}, moneyline: ${stats.missingMoneyline}, total: ${stats.missingTotal}`);
    }
  }

  const totalMissing = allGaps.reduce((sum, g) => sum + g.missingAnyOdds, 0);

  if (totalMissing === 0) {
    console.log('\n✅ No gaps found! All games have odds data.');
    process.exit(0);
  }

  console.log(`\n📈 Total games missing odds: ${totalMissing}`);

  if (args.dryRun) {
    console.log('\n🔍 DRY RUN - No changes will be made');
    console.log('   Remove --dry-run to actually fill gaps');
    process.exit(0);
  }

  // Fill gaps using The Odds API
  console.log('\n🚀 Filling gaps with The Odds API...\n');

  const results: FillResult[] = [];
  let totalCredits = 0;

  for (const league of leagues) {
    const gapStats = allGaps.find(g => g.league === league);
    if (!gapStats || gapStats.missingAnyOdds === 0) {
      console.log(`  ${league.toUpperCase()}: No gaps to fill`);
      continue;
    }

    console.log(`\n📥 ${league.toUpperCase()}: Filling ${gapStats.missingAnyOdds} games...`);

    const result = await runTheoddsapiBackfill(league, startDate, endDate, args.maxGames);
    results.push(result);
    totalCredits += result.creditsUsed;

    if (result.error) {
      console.log(`  ❌ Error: ${result.error.slice(0, 100)}`);
    } else {
      console.log(`  ✅ Filled ${result.openingFilled} opening, ${result.closingFilled} closing lines (${result.creditsUsed} credits)`);
    }

    // Small delay between leagues to avoid rate limiting
    await new Promise(resolve => setTimeout(resolve, 2000));
  }

  // Summary
  console.log('\n' + '='.repeat(60));
  console.log('📋 FILL GAPS SUMMARY\n');

  let totalOpening = 0;
  let totalClosing = 0;

  for (const result of results) {
    if (result.openingFilled > 0 || result.closingFilled > 0) {
      console.log(`  ${result.league.toUpperCase()}: +${result.openingFilled} opening, +${result.closingFilled} closing`);
      totalOpening += result.openingFilled;
      totalClosing += result.closingFilled;
    }
  }

  console.log('-'.repeat(40));
  console.log(`  TOTAL: +${totalOpening} opening, +${totalClosing} closing lines`);
  console.log(`  Credits used: ${totalCredits}`);

  // Show remaining gaps
  console.log('\n📊 Remaining gaps after fill:');
  for (const league of leagues) {
    const stats = await getGapStats(prisma, league, startDate, endDate);
    if (stats.missingAnyOdds > 0) {
      console.log(`  ${league.toUpperCase()}: ${stats.missingAnyOdds} still missing`);
    }
  }

  console.log('\n✅ Done!');
}

main().catch(console.error);
