/**
 * Backfill historical games and odds from SportsGameOdds API
 * Uses the SDK to fetch games for date ranges
 */

import * as dotenv from 'dotenv';
dotenv.config();

import SportsGameOdds from 'sports-odds-api';
import { PrismaClient } from '../prisma_sports/generated/sports-client';
import { normalizeTeamFull } from '../src/lib/teamNormalization';

const prisma = new PrismaClient();
const API_KEY = process.env.SPORTSGAMEODDS_API_KEY || '';
console.log('API Key loaded:', Boolean(API_KEY));

if (!API_KEY) {
  console.error('SPORTSGAMEODDS_API_KEY not set');
  process.exit(1);
}

const client = new SportsGameOdds({
  apiKeyHeader: API_KEY,
  maxRetries: 3,
  timeout: 30000,
});

const LEAGUES = ['nba', 'nfl', 'nhl', 'mlb', 'ncaab', 'ncaaf'];

function sleep(ms: number): Promise<void> {
  return new Promise(resolve => setTimeout(resolve, ms));
}

async function fetchAndStoreGames(league: string, startDate: Date, endDate: Date) {
  console.log(`\n[${league.toUpperCase()}] Fetching ${startDate.toISOString().split('T')[0]} to ${endDate.toISOString().split('T')[0]}`);

  let gamesAdded = 0;
  let oddsAdded = 0;

  try {
    const events: any[] = [];
    for await (const event of client.events.get({
      leagueID: league.toUpperCase(),
      startsAfter: startDate.toISOString(),
      startsBefore: endDate.toISOString(),
    })) {
      events.push(event);
    }

    console.log(`  Found ${events.length} events`);

    for (const event of events) {
      try {
        const gameDate = event.status?.startsAt ? new Date(event.status.startsAt) : new Date();
        const homeTeam = normalizeTeamFull(event.teams?.home?.names?.long || event.teams?.home?.names?.short || '', league) || '';
        const awayTeam = normalizeTeamFull(event.teams?.away?.names?.long || event.teams?.away?.names?.short || '', league) || '';
        const externalGameId = event.eventID;
        const seasonYear = gameDate.getFullYear();

        // Check if game exists (by externalGameId or by unique key)
        let existing = await prisma.sportsGame.findFirst({
          where: { externalGameId },
        });

        // Also check by composite key if not found by externalGameId
        if (!existing) {
          existing = await prisma.sportsGame.findFirst({
            where: {
              league: league.toLowerCase(),
              season: seasonYear,
              gameDate,
              homeTeam,
              awayTeam,
            },
          });
        }

        if (existing) {
          // Update odds if we have them
          if (event.odds && Object.keys(event.odds).length > 0) {
            const moneylines = Object.values(event.odds).find((o: any) => o.oddType === 'moneyline');
            if (moneylines) {
              const homeOdds = (moneylines as any).home?.odds;
              const awayOdds = (moneylines as any).away?.odds;
              if (homeOdds || awayOdds) {
                await prisma.sportsGame.update({
                  where: { id: existing.id },
                  data: {
                    moneylineHome: homeOdds ? parseInt(homeOdds) : undefined,
                    moneylineAway: awayOdds ? parseInt(awayOdds) : undefined,
                    oddsUpdatedAt: new Date(),
                    updatedAt: new Date(),
                  },
                });
                oddsAdded++;
              }
            }
          }
          continue;
        }

        // Extract odds
        let moneylineHome: number | null = null;
        let moneylineAway: number | null = null;
        let spreadHome: number | null = null;
        let total: number | null = null;

        if (event.odds) {
          for (const odd of Object.values(event.odds)) {
            const o = odd as any;
            if (o.oddType === 'moneyline') {
              moneylineHome = o.home?.odds ? parseInt(o.home.odds) : null;
              moneylineAway = o.away?.odds ? parseInt(o.away.odds) : null;
            }
            if (o.oddType === 'spread') {
              spreadHome = o.home?.spread ? parseFloat(o.home.spread) : null;
            }
            if (o.oddType === 'total') {
              total = o.over?.total ? parseFloat(o.over.total) : null;
            }
          }
        }

        // Extract scores if available
        let homeScore: number | null = null;
        let awayScore: number | null = null;
        if (event.results?.home?.points !== undefined) {
          homeScore = parseInt(event.results.home.points);
        }
        if (event.results?.away?.points !== undefined) {
          awayScore = parseInt(event.results.away.points);
        }

        // Create new game
        await prisma.sportsGame.create({
          data: {
            league: league.toLowerCase(),
            season: seasonYear,
            gameDate,
            homeTeam,
            awayTeam,
            externalGameId,
            homeScore,
            awayScore,
            moneylineHome,
            moneylineAway,
            spreadHome,
            total,
            oddsUpdatedAt: moneylineHome ? new Date() : undefined,
            oddsSource: 'sportsgameodds',
            raw: { sportsgameodds: event },
            updatedAt: new Date(),
          },
        });
        gamesAdded++;
        if (moneylineHome) oddsAdded++;

      } catch (e: any) {
        console.warn(`  Error processing event:`, e.message);
      }
    }

    console.log(`  Added: ${gamesAdded} games, ${oddsAdded} with odds`);

  } catch (e: any) {
    console.error(`  API Error: ${e.message}`);
  }

  return { gamesAdded, oddsAdded };
}

async function backfillLeague(league: string) {
  console.log(`\n${'='.repeat(50)}`);
  console.log(`  BACKFILLING ${league.toUpperCase()}`);
  console.log(`${'='.repeat(50)}`);

  // Determine date range based on sport season
  const now = new Date();
  const ranges: Array<{ start: Date; end: Date }> = [];

  // 2025 season (varies by sport)
  if (['nba', 'nhl'].includes(league)) {
    // NBA/NHL: Oct 2024 - Apr 2025 (2025 season), Oct 2025 - present (2026 season)
    ranges.push({ start: new Date('2024-10-01'), end: new Date('2025-06-30') });
    ranges.push({ start: new Date('2025-10-01'), end: now });
  } else if (league === 'nfl') {
    // NFL: Sep 2025 - Feb 2026
    ranges.push({ start: new Date('2025-09-01'), end: new Date('2026-02-15') });
  } else if (league === 'mlb') {
    // MLB: Mar-Oct 2025, Mar-Oct 2026
    ranges.push({ start: new Date('2025-03-01'), end: new Date('2025-10-31') });
    ranges.push({ start: new Date('2026-03-01'), end: new Date('2026-10-31') });
  } else if (league === 'ncaab') {
    // NCAAB: Nov 2024 - Mar 2025, Nov 2025 - present
    ranges.push({ start: new Date('2024-11-01'), end: new Date('2025-04-15') });
    ranges.push({ start: new Date('2025-11-01'), end: now });
  } else if (league === 'ncaaf') {
    // NCAAF: Aug 2025 - Jan 2026
    ranges.push({ start: new Date('2025-08-01'), end: new Date('2026-01-31') });
  }

  let totalGames = 0;
  let totalOdds = 0;

  for (const range of ranges) {
    // Split into monthly chunks to avoid API limits
    let current = new Date(range.start);
    while (current < range.end) {
      const chunkEnd = new Date(current);
      chunkEnd.setDate(chunkEnd.getDate() + 30);
      if (chunkEnd > range.end) chunkEnd.setTime(range.end.getTime());

      const result = await fetchAndStoreGames(league, current, chunkEnd);
      totalGames += result.gamesAdded;
      totalOdds += result.oddsAdded;

      // Rate limiting - wait between chunks
      await sleep(2000);

      current = new Date(chunkEnd);
      current.setDate(current.getDate() + 1);
    }
  }

  console.log(`\n  ${league.toUpperCase()} TOTAL: ${totalGames} games, ${totalOdds} with odds`);
  return { totalGames, totalOdds };
}

async function main() {
  console.log('========================================');
  console.log('  SGO HISTORICAL BACKFILL');
  console.log('  ' + new Date().toISOString());
  console.log('========================================');

  const results: Record<string, { games: number; odds: number }> = {};

  for (const league of LEAGUES) {
    try {
      const result = await backfillLeague(league);
      results[league] = { games: result.totalGames, odds: result.totalOdds };
    } catch (e: any) {
      console.error(`Failed to backfill ${league}: ${e.message}`);
      results[league] = { games: 0, odds: 0 };
    }
    // Pause between leagues
    await sleep(5000);
  }

  console.log('\n========================================');
  console.log('  SUMMARY');
  console.log('========================================');
  for (const [league, stats] of Object.entries(results)) {
    console.log(`  ${league.toUpperCase().padEnd(8)}: ${stats.games} games, ${stats.odds} with odds`);
  }

  await prisma.$disconnect();
}

main().catch(console.error);
