/**
 * Canonical Player Name Resolver
 *
 * Loads the ESPN-reconciled player list and provides fast O(1) name
 * normalization. When a forecast pipeline produces "Jimmy Butler", this
 * resolves it to "Jimmy Butler III" (the ESPN canonical form).
 *
 * Loads once at import time from rainmaker_players.json, which is kept
 * fresh by the roster-reconciler cron (3 AM ET daily).
 */

import fs from 'fs';

const PLAYERS_JSON = '/home/administrator/rainmaker_players.json';

// Normalized name → ESPN canonical display name, per league
// Key = `${league}:${normalizedName}`
const canonicalMap = new Map<string, string>();

// Global fallback (no league scope)
const globalMap = new Map<string, string>();

// ESPN roster: league:teamShort → player names (for prompt injection)
// Populated during loadCanonicalNames() from the same JSON.
const rosterMap = new Map<string, string[]>();

// Team abbreviation lookup: league:teamName → teamShort (e.g. "nba:Atlanta Hawks" → "ATL")
const teamAbbrMap = new Map<string, string>();
// Reverse: league:teamShort → teamName (e.g. "nba:ATL" → "Atlanta Hawks")
const teamNameMap = new Map<string, string>();

function normalize(name: string): string {
  return name
    .normalize('NFD')
    .replace(/[\u0300-\u036f]/g, '')
    .toLowerCase()
    .replace(/[^a-z ]/g, '')
    .replace(/\s+/g, ' ')
    .trim();
}

function stripSuffix(norm: string): string {
  return norm.replace(/\s+(?:jr|sr|ii|iii|iv|v)$/, '').trim();
}

function loadCanonicalNames(): void {
  try {
    const raw = JSON.parse(fs.readFileSync(PLAYERS_JSON, 'utf8'));
    let count = 0;

    for (const [leagueKey, leagueData] of Object.entries(raw.leagues || {}) as any) {
      // Build team abbreviation maps from league.teams[]
      const teams = leagueData.teams || [];
      for (const t of teams) {
        const short = t.shortName || t.abbreviation || '';
        const full = t.fullName || t.name || '';
        if (short && full) {
          teamAbbrMap.set(`${leagueKey}:${full}`, short);
          teamNameMap.set(`${leagueKey}:${short}`, full);
        }
      }

      const players = leagueData.players || leagueData.fighters || [];
      for (const p of players) {
        const displayName = p.name || p.fighterID || '';
        if (!displayName) continue;

        const norm = normalize(displayName);
        const suff = stripSuffix(norm);

        // League-scoped entries
        canonicalMap.set(`${leagueKey}:${norm}`, displayName);
        if (suff !== norm) {
          canonicalMap.set(`${leagueKey}:${suff}`, displayName);
        }

        // Global fallback
        if (!globalMap.has(norm)) {
          globalMap.set(norm, displayName);
        }
        if (suff !== norm && !globalMap.has(suff)) {
          globalMap.set(suff, displayName);
        }

        // Roster map: group players by team abbreviation
        const teamName = p.teamName || '';
        const teamShort = teamAbbrMap.get(`${leagueKey}:${teamName}`) || '';
        if (teamShort) {
          const rosterKey = `${leagueKey}:${teamShort}`;
          if (!rosterMap.has(rosterKey)) rosterMap.set(rosterKey, []);
          rosterMap.get(rosterKey)!.push(displayName);
        }

        count++;
      }
    }

    console.log(`[CANONICAL] Loaded ${count} player names, ${rosterMap.size} team rosters from ${PLAYERS_JSON}`);
  } catch (err: any) {
    console.log(`[CANONICAL] Warning: Could not load ${PLAYERS_JSON}: ${err.message}`);
    console.log('[CANONICAL] Player name resolution will pass through unchanged.');
  }
}

// Load on first import
loadCanonicalNames();

/**
 * Resolve a player name to its ESPN canonical form.
 * Returns the canonical name if found, otherwise returns the original unchanged.
 *
 * @param name   Raw player name from Grok/PIFF/SGO output
 * @param league Optional league key for scoped lookup (e.g. 'nba', 'nfl')
 */
export function resolveCanonicalName(name: string, league?: string): string {
  if (!name) return name;

  const norm = normalize(name);
  const suff = stripSuffix(norm);

  // Try league-scoped exact match
  if (league) {
    const exact = canonicalMap.get(`${league}:${norm}`);
    if (exact) return exact;
    const suffMatch = canonicalMap.get(`${league}:${suff}`);
    if (suffMatch) return suffMatch;
  }

  // Try global fallback
  const globalExact = globalMap.get(norm);
  if (globalExact) return globalExact;
  const globalSuff = globalMap.get(suff);
  if (globalSuff) return globalSuff;

  // No match — return original
  return name;
}

/**
 * Force reload the canonical name map (e.g. after roster-reconciler runs).
 */
export function reloadCanonicalNames(): void {
  canonicalMap.clear();
  globalMap.clear();
  rosterMap.clear();
  teamAbbrMap.clear();
  teamNameMap.clear();
  loadCanonicalNames();
}

/**
 * Get the current ESPN roster for a team.
 * Returns player names sorted alphabetically, or empty array if unknown.
 *
 * @param teamShort  Team abbreviation (e.g. "LAC", "CLE")
 * @param league     League key (e.g. "nba", "nfl")
 */
export function getTeamRoster(teamShort: string, league: string): string[] {
  const key = `${league}:${teamShort.toUpperCase()}`;
  return rosterMap.get(key) || [];
}

/**
 * Resolve a team full name to its abbreviation.
 * e.g. "Atlanta Hawks" → "ATL"
 */
export function getTeamAbbr(teamFullName: string, league: string): string {
  return teamAbbrMap.get(`${league}:${teamFullName}`) || '';
}

/**
 * Resolve a team abbreviation to its full name.
 * e.g. "ATL" → "Atlanta Hawks"
 */
export function getTeamName(teamShort: string, league: string): string {
  return teamNameMap.get(`${league}:${teamShort}`) || '';
}

/**
 * Check if a player is on a specific team according to ESPN.
 * Useful for validating LLM output against real rosters.
 */
export function isPlayerOnTeam(playerName: string, teamShort: string, league: string): boolean {
  const roster = getTeamRoster(teamShort, league);
  if (roster.length === 0) return true; // no data → don't block
  const norm = normalize(playerName);
  return roster.some(r => normalize(r) === norm || stripSuffix(normalize(r)) === stripSuffix(norm));
}

/**
 * Format a team roster as a prompt section for LLM injection.
 * Returns top players (by position grouping) to keep prompt concise.
 */
export function formatRosterForPrompt(teamShort: string, teamName: string, league: string): string {
  const roster = getTeamRoster(teamShort, league);
  if (roster.length === 0) return '';
  return `CURRENT ${teamName.toUpperCase()} ROSTER (${roster.length} players, per ESPN):\n${roster.join(', ')}`;
}
