#!/usr/bin/env npx tsx
/**
 * Backfill MLB Historical Odds from SportsGameOdds API
 *
 * Usage:
 *   npx tsx scripts/backfill_mlb_odds_sgo.ts --from 2023-03-30 --to 2023-10-31
 *   npx tsx scripts/backfill_mlb_odds_sgo.ts --season 2024
 *   npx tsx scripts/backfill_mlb_odds_sgo.ts --season 2025
 */

import SportsGameOdds from 'sports-odds-api';
import { PrismaClient } from '../prisma_sports/generated/sports-client';
import * as fs from 'fs';
import * as path from 'path';
import * as crypto from 'crypto';

// Load env
const envPath = path.join('/var/www/html/eventheodds', '.env');
if (fs.existsSync(envPath)) {
  const envContent = fs.readFileSync(envPath, 'utf-8');
  envContent.split('\n').forEach(line => {
    const [key, ...valueParts] = line.split('=');
    if (key && valueParts.length) {
      const value = valueParts.join('=').replace(/^["']|["']$/g, '');
      process.env[key.trim()] = value.trim();
    }
  });
}

const API_KEY = process.env.SPORTSGAMEODDS_API_KEY;
if (!API_KEY) {
  console.error('ERROR: SPORTSGAMEODDS_API_KEY not set');
  process.exit(1);
}

const prisma = new PrismaClient({
  datasources: {
    db: { url: process.env.SPORTS_DATABASE_URL }
  }
});

const client = new SportsGameOdds({
  apiKeyHeader: API_KEY,
});

function generateGameId(league: string, gameDate: string, homeTeam: string, awayTeam: string): number {
  const key = `${league}:${gameDate}:${homeTeam}:${awayTeam}`;
  const hash = crypto.createHash('md5').update(key).digest();
  return hash.readUInt32BE(0) % (2 ** 31);
}

function americanToDecimal(american: number): number {
  if (american > 0) return (american / 100) + 1;
  return (100 / Math.abs(american)) + 1;
}

async function backfillMLBOdds(startDate: string, endDate: string) {
  console.log('='.repeat(60));
  console.log('BACKFILLING MLB ODDS FROM SPORTSGAMEODDS');
  console.log(`Date range: ${startDate} to ${endDate}`);
  console.log('='.repeat(60));
  console.log();

  let totalEvents = 0;
  let totalRecords = 0;

  try {
    let page = await client.events.get({
      leagueID: 'MLB',
      startsAfter: startDate,
      startsBefore: endDate,
      ended: true,
      limit: 100,
    });

    while (true) {
      const events = page.data || [];
      if (events.length === 0) break;

      console.log(`Processing batch of ${events.length} events...`);

      for (const event of events) {
        if (!event.teams?.home || !event.teams?.away) continue;
        if (!event.status?.startsAt) continue;

        const gameDate = new Date(event.status.startsAt);
        const gameDateStr = gameDate.toISOString().split('T')[0];

        const homeTeam = event.teams.home.names?.short || 'UNK';
        const awayTeam = event.teams.away.names?.short || 'UNK';

        if (!homeTeam || !awayTeam) continue;

        const gameId = generateGameId('mlb', gameDateStr, homeTeam, awayTeam);
        totalEvents++;

        const records: any[] = [];

        // Extract game-level odds
        for (const [key, odd] of Object.entries(event.odds || {})) {
          // Skip player props
          if (key.includes('batting_') || key.includes('pitching_') || (odd as any).playerID) continue;

          // Only game period odds
          if (!key.includes('-game-')) continue;

          const oddData = odd as any;

          // Moneyline
          if (key === 'points-home-game-ml-home') {
            const awayKey = 'points-away-game-ml-away';
            const awayOdd = (event.odds as any)?.[awayKey];
            if (oddData.bookOdds && awayOdd?.bookOdds) {
              records.push({
                league: 'mlb',
                gameDate,
                homeTeam,
                awayTeam,
                bookmaker: 'consensus',
                market: 'moneyline',
                lineValue: null,
                homeOdds: parseInt(oddData.bookOdds.replace('+', '')) || 0,
                awayOdds: parseInt(awayOdd.bookOdds.replace('+', '')) || 0,
                source: 'sportsgameodds',
                gameId,
              });
            }
          }

          // Total (game total, not team-specific)
          if (key === 'points-all-game-ou-over') {
            const underKey = 'points-all-game-ou-under';
            const underOdd = (event.odds as any)?.[underKey];
            if (oddData.bookOverUnder) {
              records.push({
                league: 'mlb',
                gameDate,
                homeTeam,
                awayTeam,
                bookmaker: 'consensus',
                market: 'total',
                lineValue: parseFloat(oddData.bookOverUnder) || null,
                homeOdds: parseInt((oddData.bookOdds || '-110').replace('+', '')) || -110,
                awayOdds: parseInt((underOdd?.bookOdds || '-110').replace('+', '')) || -110,
                source: 'sportsgameodds',
                gameId,
              });
            }
          }

          // Spread (run line) - look for spread markets
          if (key.includes('-spread-') && key.includes('-home')) {
            const awaySpreadKey = key.replace('-home', '-away');
            const awaySpread = (event.odds as any)?.[awaySpreadKey];
            if (oddData.bookSpread) {
              records.push({
                league: 'mlb',
                gameDate,
                homeTeam,
                awayTeam,
                bookmaker: 'consensus',
                market: 'spread',
                lineValue: parseFloat(oddData.bookSpread) || null,
                homeOdds: parseInt((oddData.bookOdds || '-110').replace('+', '')) || -110,
                awayOdds: parseInt((awaySpread?.bookOdds || '-110').replace('+', '')) || -110,
                source: 'sportsgameodds',
                gameId,
              });
            }
          }
        }

        // Insert records
        for (const r of records) {
          try {
            await prisma.bookmakerOdds.upsert({
              where: {
                gameId_bookmaker_market_lineValue: {
                  gameId: r.gameId,
                  bookmaker: r.bookmaker,
                  market: r.market,
                  lineValue: r.lineValue,
                }
              },
              create: {
                league: r.league,
                gameDate: r.gameDate,
                homeTeam: r.homeTeam,
                awayTeam: r.awayTeam,
                bookmaker: r.bookmaker,
                market: r.market,
                lineValue: r.lineValue,
                homeOdds: r.homeOdds,
                awayOdds: r.awayOdds,
                source: r.source,
                gameId: r.gameId,
                fetchedAt: new Date(),
                createdAt: new Date(),
              },
              update: {}
            });
            totalRecords++;
          } catch (e: any) {
            // Ignore duplicate errors
          }
        }
      }

      // Check if there's a next page
      if (!page.hasNextPage()) break;

      console.log(`  Processed ${totalEvents} events, ${totalRecords} records so far...`);
      page = await page.getNextPage();
    }
  } catch (e: any) {
    console.error('Error fetching events:', e.message);
  }

  console.log();
  console.log('='.repeat(60));
  console.log(`BACKFILL COMPLETE`);
  console.log(`Events processed: ${totalEvents}`);
  console.log(`Records inserted: ${totalRecords}`);
  console.log('='.repeat(60));

  await prisma.$disconnect();
}

// Parse command line arguments
const args = process.argv.slice(2);
let startDate = '';
let endDate = '';

for (let i = 0; i < args.length; i++) {
  if (args[i] === '--from' && args[i + 1]) {
    startDate = args[i + 1];
  } else if (args[i] === '--to' && args[i + 1]) {
    endDate = args[i + 1];
  } else if (args[i] === '--season' && args[i + 1]) {
    const year = parseInt(args[i + 1]);
    startDate = `${year}-03-20`;
    endDate = `${year}-11-05`;
  }
}

if (!startDate || !endDate) {
  console.log('Usage:');
  console.log('  npx tsx scripts/backfill_mlb_odds_sgo.ts --from 2023-03-30 --to 2023-10-31');
  console.log('  npx tsx scripts/backfill_mlb_odds_sgo.ts --season 2024');
  process.exit(1);
}

backfillMLBOdds(startDate, endDate);
