#!/usr/bin/env npx tsx
/**
 * Full Roster Update from ESPN
 * Fetches all NBA rosters from ESPN and updates canonical players
 */

import { PrismaClient } from '../prisma_sports/generated/sports-client';

const prisma = new PrismaClient();

const ESPN_BASE = 'https://site.api.espn.com/apis/site/v2/sports';

// ESPN abbreviations that differ from our canonical abbreviations
const ESPN_ABBR_MAP: Record<string, string> = {
  'GS': 'GSW',
  'NO': 'NOP',
  'NY': 'NYK',
  'SA': 'SAS',
  'UTAH': 'UTA',
  'WSH': 'WAS',
};

function mapEspnAbbr(espnAbbr: string): string {
  return ESPN_ABBR_MAP[espnAbbr] || espnAbbr;
}

function normalizePlayerName(name: string): string {
  if (!name) return '';
  return name
    .toLowerCase()
    .normalize('NFD')
    .replace(/[\u0300-\u036f]/g, '')
    .replace(/\s+(jr\.?|sr\.?|iii|ii|iv|v)$/i, '')
    .replace(/\./g, '')
    .replace(/\s+/g, ' ')
    .trim();
}

interface TeamInfo {
  id: string;
  abbr: string;
  name: string;
}

interface PlayerInfo {
  name: string;
  position: string;
}

async function fetchTeams(): Promise<TeamInfo[]> {
  const url = `${ESPN_BASE}/basketball/nba/teams?limit=50`;

  try {
    const response = await fetch(url);
    if (!response.ok) {
      console.error(`Failed to fetch teams: ${response.status}`);
      return [];
    }
    const data = await response.json();
    const teams = data?.sports?.[0]?.leagues?.[0]?.teams || [];

    return teams.map((t: any) => ({
      id: t.team?.id || '',
      abbr: t.team?.abbreviation || '',
      name: t.team?.displayName || '',
    }));
  } catch (e) {
    console.error('Error fetching teams:', e);
    return [];
  }
}

async function fetchRoster(espnTeamId: string): Promise<PlayerInfo[]> {
  const url = `${ESPN_BASE}/basketball/nba/teams/${espnTeamId}/roster`;

  try {
    const response = await fetch(url);
    if (!response.ok) return [];

    const data = await response.json();
    const athletes = data?.athletes || [];
    const roster: PlayerInfo[] = [];

    for (const item of athletes) {
      if (item?.fullName || item?.displayName) {
        roster.push({
          name: item?.fullName || item?.displayName || '',
          position: item?.position?.abbreviation || '',
        });
      } else if (item?.items) {
        for (const player of item.items) {
          roster.push({
            name: player?.fullName || player?.displayName || '',
            position: player?.position?.abbreviation || '',
          });
        }
      }
    }

    return roster;
  } catch (e) {
    return [];
  }
}

async function main() {
  console.log('='.repeat(60));
  console.log('FULL NBA ROSTER UPDATE FROM ESPN');
  console.log('='.repeat(60));
  console.log(`Date: ${new Date().toISOString()}\n`);

  // Get our canonical teams
  const canonicalTeams = await prisma.canonicalTeam.findMany({
    where: { league: 'nba' },
    select: { id: true, abbr: true, fullName: true },
  });
  const teamByAbbr = new Map(canonicalTeams.map(t => [t.abbr, t]));

  console.log(`Loaded ${canonicalTeams.length} canonical NBA teams\n`);

  // Fetch ESPN teams
  const espnTeams = await fetchTeams();
  console.log(`Found ${espnTeams.length} ESPN teams\n`);

  const allChanges: Array<{
    player: string;
    from: string;
    to: string;
    toFull: string;
  }> = [];

  const teamRosters: Record<string, string[]> = {};
  let totalUpdated = 0;
  let totalNotFound = 0;

  for (const espnTeam of espnTeams) {
    const mappedAbbr = mapEspnAbbr(espnTeam.abbr);
    const canonicalTeam = teamByAbbr.get(mappedAbbr);

    if (!canonicalTeam) {
      console.log(`[WARN] No canonical team for ESPN ${espnTeam.abbr} -> ${mappedAbbr}`);
      continue;
    }

    // Fetch roster
    const roster = await fetchRoster(espnTeam.id);
    if (roster.length === 0) {
      console.log(`[WARN] Empty roster for ${espnTeam.name}`);
      continue;
    }

    teamRosters[mappedAbbr] = [];
    let teamUpdated = 0;

    for (const player of roster) {
      const normalized = normalizePlayerName(player.name);
      if (!normalized) continue;

      teamRosters[mappedAbbr].push(player.name);

      // Find existing player
      const existingPlayer = await prisma.canonicalPlayer.findFirst({
        where: { league: 'nba', normalizedName: normalized },
        include: { team: true },
      });

      if (existingPlayer) {
        if (existingPlayer.teamId !== canonicalTeam.id) {
          const oldTeam = existingPlayer.team?.abbr || 'NONE';

          allChanges.push({
            player: player.name,
            from: oldTeam,
            to: mappedAbbr,
            toFull: espnTeam.name,
          });

          await prisma.canonicalPlayer.update({
            where: { id: existingPlayer.id },
            data: {
              teamId: canonicalTeam.id,
              position: player.position || existingPlayer.position,
            },
          });
          teamUpdated++;
        }
      } else {
        totalNotFound++;
      }
    }

    if (teamUpdated > 0) {
      console.log(`${mappedAbbr} (${espnTeam.name}): ${teamUpdated} players updated`);
      totalUpdated += teamUpdated;
    }

    // Small delay to avoid rate limiting
    await new Promise(r => setTimeout(r, 150));
  }

  // Print summary
  console.log('\n' + '='.repeat(60));
  console.log('SUMMARY');
  console.log('='.repeat(60));
  console.log(`Total players updated: ${totalUpdated}`);
  console.log(`Players not in canonical table: ${totalNotFound}`);

  if (allChanges.length > 0) {
    console.log('\n' + '='.repeat(60));
    console.log('ALL TEAM CHANGES');
    console.log('='.repeat(60) + '\n');

    allChanges.forEach(c => {
      console.log(`${c.player}: ${c.from} → ${c.to} (${c.toFull})`);
    });
  }

  // Save roster summary to JSON
  const fs = await import('fs');
  const summaryPath = '/var/www/html/eventheodds/data/nba_current_rosters.json';

  const summary = {
    lastUpdated: new Date().toISOString(),
    source: 'ESPN',
    totalChanges: allChanges.length,
    changes: allChanges,
    rosters: teamRosters,
  };

  fs.writeFileSync(summaryPath, JSON.stringify(summary, null, 2));
  console.log(`\nRoster summary saved to: ${summaryPath}`);

  await prisma.$disconnect();
}

main().catch((e) => {
  console.error('Update failed:', e);
  process.exit(1);
});
