#!/usr/bin/env npx tsx
/**
 * Link metrics to canonical players by name matching
 */

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

const prisma = new PrismaClient();

function normalizePlayerName(name: string): string {
  if (!name) return '';
  // Handle 'Last, First' format (common in MLB data)
  if (name.includes(',')) {
    const parts = name.split(',').map(s => s.trim());
    name = parts[1] + ' ' + parts[0];
  }
  return name.toLowerCase()
    .normalize('NFD').replace(/[\u0300-\u036f]/g, '')
    .replace(/\s+(jr\.?|sr\.?|iii|ii|iv|v)$/i, '')
    .replace(/\./g, '')
    .replace(/\s+/g, ' ')
    .trim();
}

async function linkByName() {
  console.log('='.repeat(60));
  console.log('LINKING METRICS BY PLAYER NAME');
  console.log('='.repeat(60) + '\n');

  for (const league of ['nba', 'nfl', 'nhl', 'mlb']) {
    console.log(`\nProcessing ${league.toUpperCase()}...`);

    // Get distinct player names from unlinked metrics
    const unlinkedNames = await prisma.playerGameMetric.findMany({
      where: { league, canonicalPlayerId: null, playerName: { not: null } },
      select: { playerName: true, playerExternalId: true },
      distinct: ['playerName'],
    });

    console.log(`  Found ${unlinkedNames.length} distinct unlinked player names`);

    let linked = 0;
    let aliasesCreated = 0;

    for (const { playerName, playerExternalId } of unlinkedNames) {
      if (!playerName) continue;
      const normalized = normalizePlayerName(playerName);
      if (!normalized) continue;

      // Find canonical player
      const canonical = await prisma.canonicalPlayer.findUnique({
        where: { league_normalizedName: { league, normalizedName: normalized } }
      });

      if (canonical) {
        // Update all metrics with this name
        const updated = await prisma.playerGameMetric.updateMany({
          where: { league, playerName, canonicalPlayerId: null },
          data: { canonicalPlayerId: canonical.id }
        });
        linked += updated.count;

        // Create alias for this external ID if not exists
        const aliasExists = await prisma.playerAlias.findFirst({
          where: { alias: playerExternalId }
        });
        if (!aliasExists && playerExternalId) {
          try {
            await prisma.playerAlias.create({
              data: {
                playerId: canonical.id,
                source: league === 'mlb' ? 'mlb_com' : 'bdl',
                alias: playerExternalId,
                aliasType: 'external_id'
              }
            });
            aliasesCreated++;
          } catch (e) { /* ignore duplicates */ }
        }
      }
    }

    console.log(`  Linked: ${linked.toLocaleString()} metrics`);
    console.log(`  Created: ${aliasesCreated} new aliases`);
  }

  // Final report
  console.log('\n' + '='.repeat(60));
  console.log('FINAL COVERAGE');
  console.log('='.repeat(60) + '\n');

  for (const league of ['nba', 'nfl', 'nhl', 'mlb']) {
    const total = await prisma.playerGameMetric.count({ where: { league } });
    const linked = await prisma.playerGameMetric.count({
      where: { league, NOT: { canonicalPlayerId: null } }
    });
    const pct = total > 0 ? ((linked / total) * 100).toFixed(1) : '0.0';
    console.log(`${league.toUpperCase()} Metrics: ${linked.toLocaleString()}/${total.toLocaleString()} (${pct}%)`);
  }

  await prisma.$disconnect();
}

linkByName().catch((e) => {
  console.error('Failed:', e);
  process.exit(1);
});
