#!/usr/bin/env npx tsx
/**
 * Link Abbreviated Player Names to Canonical Players
 * Handles formats like "M.Lewis" -> "Marcus Lewis", "Florial, Estevan" -> "Estevan Florial"
 */

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

const prisma = new PrismaClient();

function normalizePlayerName(name: string): string {
  if (!name) return '';

  // Handle "Last, First" format
  if (name.includes(',')) {
    const parts = name.split(',').map(s => s.trim());
    if (parts.length === 2 && parts[1]) {
      name = parts[1] + ' ' + parts[0];
    }
  }

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

function expandAbbreviatedName(name: string): string[] {
  // "M.Lewis" or "M Lewis" -> try matching "M* Lewis"
  const parts = name.replace(/\./g, ' ').trim().split(/\s+/);
  if (parts.length < 2) return [normalizePlayerName(name)];

  const variants: string[] = [normalizePlayerName(name)];

  // If first part is single letter (initial), create pattern for matching
  if (parts[0].length === 1) {
    // "M Lewis" -> pattern for "m* lewis"
    variants.push(`${parts[0].toLowerCase()}% ${parts.slice(1).join(' ').toLowerCase()}`);
  }

  // If last part is single letter, expand backwards
  if (parts[parts.length - 1].length === 1) {
    const lastInitial = parts[parts.length - 1].toLowerCase();
    const rest = parts.slice(0, -1).join(' ').toLowerCase();
    variants.push(`${rest} ${lastInitial}%`);
  }

  return variants;
}

async function linkByAbbreviatedName() {
  console.log('='.repeat(60));
  console.log('LINKING ABBREVIATED NAMES TO CANONICAL PLAYERS');
  console.log('='.repeat(60) + '\n');

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

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

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

    let linked = 0;
    let aliasesCreated = 0;

    for (const { playerName, playerExternalId } of unlinked) {
      if (!playerName) continue;

      const normalized = normalizePlayerName(playerName);
      if (!normalized) continue;

      // Try exact match first
      let canonical = await prisma.canonicalPlayer.findUnique({
        where: { league_normalizedName: { league, normalizedName: normalized } }
      });

      // If no exact match, try abbreviated name expansion
      if (!canonical && playerName.includes('.')) {
        // Handle "M.Lewis" style - try to find by last name
        const parts = playerName.split('.');
        const lastName = parts[parts.length - 1].trim().toLowerCase();
        if (lastName.length > 2) {
          // Search for players ending with this last name
          const candidates = await prisma.canonicalPlayer.findMany({
            where: {
              league,
              normalizedName: { endsWith: ` ${lastName}` }
            },
            take: 10
          });

          // If only one match, use it
          if (candidates.length === 1) {
            canonical = candidates[0];
          } else if (candidates.length > 1) {
            // Try to match initial
            const initial = parts[0].trim().toLowerCase();
            const match = candidates.find(c => c.normalizedName.startsWith(initial));
            if (match) canonical = match;
          }
        }
      }

      // Try "Last, First" format
      if (!canonical && playerName.includes(',')) {
        const parts = playerName.split(',').map(s => s.trim());
        if (parts.length === 2) {
          const reordered = normalizePlayerName(`${parts[1]} ${parts[0]}`);
          canonical = await prisma.canonicalPlayer.findUnique({
            where: { league_normalizedName: { league, normalizedName: reordered } }
          });
        }
      }

      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 if needed
        if (playerExternalId) {
          const aliasExists = await prisma.playerAlias.findFirst({
            where: { alias: playerExternalId }
          });
          if (!aliasExists) {
            try {
              await prisma.playerAlias.create({
                data: {
                  playerId: canonical.id,
                  source: 'abbrev_match',
                  alias: playerExternalId,
                  aliasType: 'external_id'
                }
              });
              aliasesCreated++;
            } catch (e) { /* ignore duplicates */ }
          }
        }
      }
    }

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

  // Final stats
  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';
    console.log(`${league.toUpperCase()} Metrics: ${linked.toLocaleString()}/${total.toLocaleString()} (${pct}%)`);
  }

  await prisma.$disconnect();
}

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