/**
 * Smart Resolver - Autonomous entity resolution
 * Learns from the database instead of hardcoded mappings
 */

import { PLAYER_NICKNAMES } from './types';

// Common last names that are ambiguous across multiple notable players
// When a query uses only a last name from this list, we should ask for clarification
const AMBIGUOUS_LAST_NAMES = new Set([
  'green',      // Draymond Green, Jalen Green, Jeff Green, Danny Green, A.J. Green
  'smith',      // multiple across all sports
  'johnson',    // multiple across all sports
  'brown',      // Jaylen Brown, Jayson Brown, Antonio Brown, A.J. Brown
  'jones',      // multiple across all sports
  'williams',   // multiple across all sports
  'davis',      // Anthony Davis, Gervonta Davis, Corey Davis
  'thomas',     // multiple across all sports
  'jackson',    // multiple across all sports
  'white',      // Coby White, Derrick White, Tre'Davious White
  'harris',     // Tobias Harris, Damien Harris, Najee Harris
  'robinson',   // Duncan Robinson, Mitchell Robinson
  'hill',       // Tyreek Hill, George Hill, Solomon Hill
  'martin',     // multiple across all sports
  'allen',      // Josh Allen, Ray Allen, Jarrett Allen
  'miller',     // multiple across all sports
  'wilson',     // Russell Wilson, Zach Wilson, Robert Wilson
  'moore',      // DJ Moore, Rondale Moore, Elijah Moore
  'taylor',     // Jonathan Taylor, Tyrod Taylor
  'walker',     // Kemba Walker, P.J. Walker
  'young',      // Trae Young, Bryce Young, Nick Young
  'cook',       // Dalvin Cook, Jared Cook
  'edwards',    // Anthony Edwards, Donovan Edwards
  'mitchell',   // Donovan Mitchell, multiple
  'gordon',     // Aaron Gordon, Melvin Gordon, Josh Gordon
  'fox',        // De'Aaron Fox, Adam Fox (NHL)
]);

// Comprehensive team name mappings (nickname/city → abbreviation)
const TEAM_MAPPINGS: Record<string, { abbrev: string; league: string; fullName: string }> = {
  // NBA Teams
  'lakers': { abbrev: 'LAL', league: 'nba', fullName: 'Los Angeles Lakers' },
  'los angeles lakers': { abbrev: 'LAL', league: 'nba', fullName: 'Los Angeles Lakers' },
  'celtics': { abbrev: 'BOS', league: 'nba', fullName: 'Boston Celtics' },
  'boston celtics': { abbrev: 'BOS', league: 'nba', fullName: 'Boston Celtics' },
  'warriors': { abbrev: 'GSW', league: 'nba', fullName: 'Golden State Warriors' },
  'golden state': { abbrev: 'GSW', league: 'nba', fullName: 'Golden State Warriors' },
  'nets': { abbrev: 'BKN', league: 'nba', fullName: 'Brooklyn Nets' },
  'brooklyn nets': { abbrev: 'BKN', league: 'nba', fullName: 'Brooklyn Nets' },
  'knicks': { abbrev: 'NYK', league: 'nba', fullName: 'New York Knicks' },
  'new york knicks': { abbrev: 'NYK', league: 'nba', fullName: 'New York Knicks' },
  'heat': { abbrev: 'MIA', league: 'nba', fullName: 'Miami Heat' },
  'miami heat': { abbrev: 'MIA', league: 'nba', fullName: 'Miami Heat' },
  'bulls': { abbrev: 'CHI', league: 'nba', fullName: 'Chicago Bulls' },
  'chicago bulls': { abbrev: 'CHI', league: 'nba', fullName: 'Chicago Bulls' },
  'cavaliers': { abbrev: 'CLE', league: 'nba', fullName: 'Cleveland Cavaliers' },
  'cavs': { abbrev: 'CLE', league: 'nba', fullName: 'Cleveland Cavaliers' },
  'cleveland cavaliers': { abbrev: 'CLE', league: 'nba', fullName: 'Cleveland Cavaliers' },
  'mavericks': { abbrev: 'DAL', league: 'nba', fullName: 'Dallas Mavericks' },
  'mavs': { abbrev: 'DAL', league: 'nba', fullName: 'Dallas Mavericks' },
  'dallas mavericks': { abbrev: 'DAL', league: 'nba', fullName: 'Dallas Mavericks' },
  'nuggets': { abbrev: 'DEN', league: 'nba', fullName: 'Denver Nuggets' },
  'denver nuggets': { abbrev: 'DEN', league: 'nba', fullName: 'Denver Nuggets' },
  'pistons': { abbrev: 'DET', league: 'nba', fullName: 'Detroit Pistons' },
  'detroit pistons': { abbrev: 'DET', league: 'nba', fullName: 'Detroit Pistons' },
  'rockets': { abbrev: 'HOU', league: 'nba', fullName: 'Houston Rockets' },
  'houston rockets': { abbrev: 'HOU', league: 'nba', fullName: 'Houston Rockets' },
  'pacers': { abbrev: 'IND', league: 'nba', fullName: 'Indiana Pacers' },
  'indiana pacers': { abbrev: 'IND', league: 'nba', fullName: 'Indiana Pacers' },
  'clippers': { abbrev: 'LAC', league: 'nba', fullName: 'Los Angeles Clippers' },
  'la clippers': { abbrev: 'LAC', league: 'nba', fullName: 'Los Angeles Clippers' },
  'grizzlies': { abbrev: 'MEM', league: 'nba', fullName: 'Memphis Grizzlies' },
  'memphis grizzlies': { abbrev: 'MEM', league: 'nba', fullName: 'Memphis Grizzlies' },
  'bucks': { abbrev: 'MIL', league: 'nba', fullName: 'Milwaukee Bucks' },
  'milwaukee bucks': { abbrev: 'MIL', league: 'nba', fullName: 'Milwaukee Bucks' },
  'timberwolves': { abbrev: 'MIN', league: 'nba', fullName: 'Minnesota Timberwolves' },
  'wolves': { abbrev: 'MIN', league: 'nba', fullName: 'Minnesota Timberwolves' },
  'minnesota timberwolves': { abbrev: 'MIN', league: 'nba', fullName: 'Minnesota Timberwolves' },
  'pelicans': { abbrev: 'NOP', league: 'nba', fullName: 'New Orleans Pelicans' },
  'new orleans pelicans': { abbrev: 'NOP', league: 'nba', fullName: 'New Orleans Pelicans' },
  'thunder': { abbrev: 'OKC', league: 'nba', fullName: 'Oklahoma City Thunder' },
  'oklahoma city thunder': { abbrev: 'OKC', league: 'nba', fullName: 'Oklahoma City Thunder' },
  'magic': { abbrev: 'ORL', league: 'nba', fullName: 'Orlando Magic' },
  'orlando magic': { abbrev: 'ORL', league: 'nba', fullName: 'Orlando Magic' },
  '76ers': { abbrev: 'PHI', league: 'nba', fullName: 'Philadelphia 76ers' },
  'sixers': { abbrev: 'PHI', league: 'nba', fullName: 'Philadelphia 76ers' },
  'philadelphia 76ers': { abbrev: 'PHI', league: 'nba', fullName: 'Philadelphia 76ers' },
  'suns': { abbrev: 'PHX', league: 'nba', fullName: 'Phoenix Suns' },
  'phoenix suns': { abbrev: 'PHX', league: 'nba', fullName: 'Phoenix Suns' },
  'trail blazers': { abbrev: 'POR', league: 'nba', fullName: 'Portland Trail Blazers' },
  'blazers': { abbrev: 'POR', league: 'nba', fullName: 'Portland Trail Blazers' },
  'portland trail blazers': { abbrev: 'POR', league: 'nba', fullName: 'Portland Trail Blazers' },
  'kings': { abbrev: 'SAC', league: 'nba', fullName: 'Sacramento Kings' },
  'sacramento kings': { abbrev: 'SAC', league: 'nba', fullName: 'Sacramento Kings' },
  'spurs': { abbrev: 'SAS', league: 'nba', fullName: 'San Antonio Spurs' },
  'san antonio spurs': { abbrev: 'SAS', league: 'nba', fullName: 'San Antonio Spurs' },
  'raptors': { abbrev: 'TOR', league: 'nba', fullName: 'Toronto Raptors' },
  'toronto raptors': { abbrev: 'TOR', league: 'nba', fullName: 'Toronto Raptors' },
  'jazz': { abbrev: 'UTA', league: 'nba', fullName: 'Utah Jazz' },
  'utah jazz': { abbrev: 'UTA', league: 'nba', fullName: 'Utah Jazz' },
  'wizards': { abbrev: 'WAS', league: 'nba', fullName: 'Washington Wizards' },
  'washington wizards': { abbrev: 'WAS', league: 'nba', fullName: 'Washington Wizards' },
  'hornets': { abbrev: 'CHA', league: 'nba', fullName: 'Charlotte Hornets' },
  'charlotte hornets': { abbrev: 'CHA', league: 'nba', fullName: 'Charlotte Hornets' },
  'hawks': { abbrev: 'ATL', league: 'nba', fullName: 'Atlanta Hawks' },
  'atlanta hawks': { abbrev: 'ATL', league: 'nba', fullName: 'Atlanta Hawks' },

  // NFL Teams
  'chiefs': { abbrev: 'KC', league: 'nfl', fullName: 'Kansas City Chiefs' },
  'kansas city chiefs': { abbrev: 'KC', league: 'nfl', fullName: 'Kansas City Chiefs' },
  'kansas city': { abbrev: 'KC', league: 'nfl', fullName: 'Kansas City Chiefs' },
  'bills': { abbrev: 'BUF', league: 'nfl', fullName: 'Buffalo Bills' },
  'buffalo bills': { abbrev: 'BUF', league: 'nfl', fullName: 'Buffalo Bills' },
  'ravens': { abbrev: 'BAL', league: 'nfl', fullName: 'Baltimore Ravens' },
  'baltimore ravens': { abbrev: 'BAL', league: 'nfl', fullName: 'Baltimore Ravens' },
  'eagles': { abbrev: 'PHI', league: 'nfl', fullName: 'Philadelphia Eagles' },
  'philadelphia eagles': { abbrev: 'PHI', league: 'nfl', fullName: 'Philadelphia Eagles' },
  'cowboys': { abbrev: 'DAL', league: 'nfl', fullName: 'Dallas Cowboys' },
  'dallas cowboys': { abbrev: 'DAL', league: 'nfl', fullName: 'Dallas Cowboys' },
  '49ers': { abbrev: 'SF', league: 'nfl', fullName: 'San Francisco 49ers' },
  'niners': { abbrev: 'SF', league: 'nfl', fullName: 'San Francisco 49ers' },
  'san francisco 49ers': { abbrev: 'SF', league: 'nfl', fullName: 'San Francisco 49ers' },
  'dolphins': { abbrev: 'MIA', league: 'nfl', fullName: 'Miami Dolphins' },
  'miami dolphins': { abbrev: 'MIA', league: 'nfl', fullName: 'Miami Dolphins' },
  'lions': { abbrev: 'DET', league: 'nfl', fullName: 'Detroit Lions' },
  'detroit lions': { abbrev: 'DET', league: 'nfl', fullName: 'Detroit Lions' },
  'packers': { abbrev: 'GB', league: 'nfl', fullName: 'Green Bay Packers' },
  'green bay packers': { abbrev: 'GB', league: 'nfl', fullName: 'Green Bay Packers' },
  'green bay': { abbrev: 'GB', league: 'nfl', fullName: 'Green Bay Packers' },
  'bengals': { abbrev: 'CIN', league: 'nfl', fullName: 'Cincinnati Bengals' },
  'cincinnati bengals': { abbrev: 'CIN', league: 'nfl', fullName: 'Cincinnati Bengals' },
  'jaguars': { abbrev: 'JAX', league: 'nfl', fullName: 'Jacksonville Jaguars' },
  'jags': { abbrev: 'JAX', league: 'nfl', fullName: 'Jacksonville Jaguars' },
  'jacksonville jaguars': { abbrev: 'JAX', league: 'nfl', fullName: 'Jacksonville Jaguars' },
  'texans': { abbrev: 'HOU', league: 'nfl', fullName: 'Houston Texans' },
  'houston texans': { abbrev: 'HOU', league: 'nfl', fullName: 'Houston Texans' },
  'colts': { abbrev: 'IND', league: 'nfl', fullName: 'Indianapolis Colts' },
  'indianapolis colts': { abbrev: 'IND', league: 'nfl', fullName: 'Indianapolis Colts' },
  'titans': { abbrev: 'TEN', league: 'nfl', fullName: 'Tennessee Titans' },
  'tennessee titans': { abbrev: 'TEN', league: 'nfl', fullName: 'Tennessee Titans' },
  'broncos': { abbrev: 'DEN', league: 'nfl', fullName: 'Denver Broncos' },
  'denver broncos': { abbrev: 'DEN', league: 'nfl', fullName: 'Denver Broncos' },
  'chargers': { abbrev: 'LAC', league: 'nfl', fullName: 'Los Angeles Chargers' },
  'la chargers': { abbrev: 'LAC', league: 'nfl', fullName: 'Los Angeles Chargers' },
  'los angeles chargers': { abbrev: 'LAC', league: 'nfl', fullName: 'Los Angeles Chargers' },
  'raiders': { abbrev: 'LV', league: 'nfl', fullName: 'Las Vegas Raiders' },
  'las vegas raiders': { abbrev: 'LV', league: 'nfl', fullName: 'Las Vegas Raiders' },
  'steelers': { abbrev: 'PIT', league: 'nfl', fullName: 'Pittsburgh Steelers' },
  'pittsburgh steelers': { abbrev: 'PIT', league: 'nfl', fullName: 'Pittsburgh Steelers' },
  'browns': { abbrev: 'CLE', league: 'nfl', fullName: 'Cleveland Browns' },
  'cleveland browns': { abbrev: 'CLE', league: 'nfl', fullName: 'Cleveland Browns' },
  'commanders': { abbrev: 'WAS', league: 'nfl', fullName: 'Washington Commanders' },
  'washington commanders': { abbrev: 'WAS', league: 'nfl', fullName: 'Washington Commanders' },
  'giants': { abbrev: 'NYG', league: 'nfl', fullName: 'New York Giants' },
  'new york giants': { abbrev: 'NYG', league: 'nfl', fullName: 'New York Giants' },
  'jets': { abbrev: 'NYJ', league: 'nfl', fullName: 'New York Jets' },
  'new york jets': { abbrev: 'NYJ', league: 'nfl', fullName: 'New York Jets' },
  'patriots': { abbrev: 'NE', league: 'nfl', fullName: 'New England Patriots' },
  'new england patriots': { abbrev: 'NE', league: 'nfl', fullName: 'New England Patriots' },
  'bears': { abbrev: 'CHI', league: 'nfl', fullName: 'Chicago Bears' },
  'chicago bears': { abbrev: 'CHI', league: 'nfl', fullName: 'Chicago Bears' },
  'vikings': { abbrev: 'MIN', league: 'nfl', fullName: 'Minnesota Vikings' },
  'minnesota vikings': { abbrev: 'MIN', league: 'nfl', fullName: 'Minnesota Vikings' },
  'saints': { abbrev: 'NO', league: 'nfl', fullName: 'New Orleans Saints' },
  'new orleans saints': { abbrev: 'NO', league: 'nfl', fullName: 'New Orleans Saints' },
  'falcons': { abbrev: 'ATL', league: 'nfl', fullName: 'Atlanta Falcons' },
  'atlanta falcons': { abbrev: 'ATL', league: 'nfl', fullName: 'Atlanta Falcons' },
  'buccaneers': { abbrev: 'TB', league: 'nfl', fullName: 'Tampa Bay Buccaneers' },
  'bucs': { abbrev: 'TB', league: 'nfl', fullName: 'Tampa Bay Buccaneers' },
  'tampa bay buccaneers': { abbrev: 'TB', league: 'nfl', fullName: 'Tampa Bay Buccaneers' },
  'panthers': { abbrev: 'CAR', league: 'nfl', fullName: 'Carolina Panthers' },
  'carolina panthers': { abbrev: 'CAR', league: 'nfl', fullName: 'Carolina Panthers' },
  'seahawks': { abbrev: 'SEA', league: 'nfl', fullName: 'Seattle Seahawks' },
  'seattle seahawks': { abbrev: 'SEA', league: 'nfl', fullName: 'Seattle Seahawks' },
  'rams': { abbrev: 'LAR', league: 'nfl', fullName: 'Los Angeles Rams' },
  'la rams': { abbrev: 'LAR', league: 'nfl', fullName: 'Los Angeles Rams' },
  'los angeles rams': { abbrev: 'LAR', league: 'nfl', fullName: 'Los Angeles Rams' },
  'cardinals': { abbrev: 'ARI', league: 'nfl', fullName: 'Arizona Cardinals' },
  'arizona cardinals': { abbrev: 'ARI', league: 'nfl', fullName: 'Arizona Cardinals' },

  // MLB Teams
  'yankees': { abbrev: 'NYY', league: 'mlb', fullName: 'New York Yankees' },
  'new york yankees': { abbrev: 'NYY', league: 'mlb', fullName: 'New York Yankees' },
  'red sox': { abbrev: 'BOS', league: 'mlb', fullName: 'Boston Red Sox' },
  'boston red sox': { abbrev: 'BOS', league: 'mlb', fullName: 'Boston Red Sox' },
  'dodgers': { abbrev: 'LAD', league: 'mlb', fullName: 'Los Angeles Dodgers' },
  'los angeles dodgers': { abbrev: 'LAD', league: 'mlb', fullName: 'Los Angeles Dodgers' },
  'mets': { abbrev: 'NYM', league: 'mlb', fullName: 'New York Mets' },
  'new york mets': { abbrev: 'NYM', league: 'mlb', fullName: 'New York Mets' },
  'cubs': { abbrev: 'CHC', league: 'mlb', fullName: 'Chicago Cubs' },
  'chicago cubs': { abbrev: 'CHC', league: 'mlb', fullName: 'Chicago Cubs' },
  'white sox': { abbrev: 'CHW', league: 'mlb', fullName: 'Chicago White Sox' },
  'chicago white sox': { abbrev: 'CHW', league: 'mlb', fullName: 'Chicago White Sox' },
  'astros': { abbrev: 'HOU', league: 'mlb', fullName: 'Houston Astros' },
  'houston astros': { abbrev: 'HOU', league: 'mlb', fullName: 'Houston Astros' },
  'braves': { abbrev: 'ATL', league: 'mlb', fullName: 'Atlanta Braves' },
  'atlanta braves': { abbrev: 'ATL', league: 'mlb', fullName: 'Atlanta Braves' },
  'phillies': { abbrev: 'PHI', league: 'mlb', fullName: 'Philadelphia Phillies' },
  'philadelphia phillies': { abbrev: 'PHI', league: 'mlb', fullName: 'Philadelphia Phillies' },
  'padres': { abbrev: 'SD', league: 'mlb', fullName: 'San Diego Padres' },
  'san diego padres': { abbrev: 'SD', league: 'mlb', fullName: 'San Diego Padres' },
  'mariners': { abbrev: 'SEA', league: 'mlb', fullName: 'Seattle Mariners' },
  'seattle mariners': { abbrev: 'SEA', league: 'mlb', fullName: 'Seattle Mariners' },
  'blue jays': { abbrev: 'TOR', league: 'mlb', fullName: 'Toronto Blue Jays' },
  'toronto blue jays': { abbrev: 'TOR', league: 'mlb', fullName: 'Toronto Blue Jays' },
  'twins': { abbrev: 'MIN', league: 'mlb', fullName: 'Minnesota Twins' },
  'minnesota twins': { abbrev: 'MIN', league: 'mlb', fullName: 'Minnesota Twins' },
  'guardians': { abbrev: 'CLE', league: 'mlb', fullName: 'Cleveland Guardians' },
  'cleveland guardians': { abbrev: 'CLE', league: 'mlb', fullName: 'Cleveland Guardians' },
  'orioles': { abbrev: 'BAL', league: 'mlb', fullName: 'Baltimore Orioles' },
  'baltimore orioles': { abbrev: 'BAL', league: 'mlb', fullName: 'Baltimore Orioles' },
  'rays': { abbrev: 'TB', league: 'mlb', fullName: 'Tampa Bay Rays' },
  'tampa bay rays': { abbrev: 'TB', league: 'mlb', fullName: 'Tampa Bay Rays' },
  'rangers': { abbrev: 'TEX', league: 'mlb', fullName: 'Texas Rangers' },
  'texas rangers': { abbrev: 'TEX', league: 'mlb', fullName: 'Texas Rangers' },
  'angels': { abbrev: 'LAA', league: 'mlb', fullName: 'Los Angeles Angels' },
  'los angeles angels': { abbrev: 'LAA', league: 'mlb', fullName: 'Los Angeles Angels' },
  'athletics': { abbrev: 'OAK', league: 'mlb', fullName: 'Oakland Athletics' },
  'oakland athletics': { abbrev: 'OAK', league: 'mlb', fullName: 'Oakland Athletics' },
  'diamondbacks': { abbrev: 'ARI', league: 'mlb', fullName: 'Arizona Diamondbacks' },
  'dbacks': { abbrev: 'ARI', league: 'mlb', fullName: 'Arizona Diamondbacks' },
  'arizona diamondbacks': { abbrev: 'ARI', league: 'mlb', fullName: 'Arizona Diamondbacks' },
  'rockies': { abbrev: 'COL', league: 'mlb', fullName: 'Colorado Rockies' },
  'colorado rockies': { abbrev: 'COL', league: 'mlb', fullName: 'Colorado Rockies' },
  'marlins': { abbrev: 'MIA', league: 'mlb', fullName: 'Miami Marlins' },
  'miami marlins': { abbrev: 'MIA', league: 'mlb', fullName: 'Miami Marlins' },
  'brewers': { abbrev: 'MIL', league: 'mlb', fullName: 'Milwaukee Brewers' },
  'milwaukee brewers': { abbrev: 'MIL', league: 'mlb', fullName: 'Milwaukee Brewers' },
  'pirates': { abbrev: 'PIT', league: 'mlb', fullName: 'Pittsburgh Pirates' },
  'pittsburgh pirates': { abbrev: 'PIT', league: 'mlb', fullName: 'Pittsburgh Pirates' },
  'reds': { abbrev: 'CIN', league: 'mlb', fullName: 'Cincinnati Reds' },
  'cincinnati reds': { abbrev: 'CIN', league: 'mlb', fullName: 'Cincinnati Reds' },
  'royals': { abbrev: 'KC', league: 'mlb', fullName: 'Kansas City Royals' },
  'kansas city royals': { abbrev: 'KC', league: 'mlb', fullName: 'Kansas City Royals' },
  'tigers': { abbrev: 'DET', league: 'mlb', fullName: 'Detroit Tigers' },
  'detroit tigers': { abbrev: 'DET', league: 'mlb', fullName: 'Detroit Tigers' },
  'nationals': { abbrev: 'WAS', league: 'mlb', fullName: 'Washington Nationals' },
  'washington nationals': { abbrev: 'WAS', league: 'mlb', fullName: 'Washington Nationals' },

  // NHL Teams
  'bruins': { abbrev: 'BOS', league: 'nhl', fullName: 'Boston Bruins' },
  'boston bruins': { abbrev: 'BOS', league: 'nhl', fullName: 'Boston Bruins' },
  'maple leafs': { abbrev: 'TOR', league: 'nhl', fullName: 'Toronto Maple Leafs' },
  'leafs': { abbrev: 'TOR', league: 'nhl', fullName: 'Toronto Maple Leafs' },
  'toronto maple leafs': { abbrev: 'TOR', league: 'nhl', fullName: 'Toronto Maple Leafs' },
  'canadiens': { abbrev: 'MTL', league: 'nhl', fullName: 'Montreal Canadiens' },
  'habs': { abbrev: 'MTL', league: 'nhl', fullName: 'Montreal Canadiens' },
  'montreal canadiens': { abbrev: 'MTL', league: 'nhl', fullName: 'Montreal Canadiens' },
  'penguins': { abbrev: 'PIT', league: 'nhl', fullName: 'Pittsburgh Penguins' },
  'pens': { abbrev: 'PIT', league: 'nhl', fullName: 'Pittsburgh Penguins' },
  'pittsburgh penguins': { abbrev: 'PIT', league: 'nhl', fullName: 'Pittsburgh Penguins' },
  'red wings': { abbrev: 'DET', league: 'nhl', fullName: 'Detroit Red Wings' },
  'detroit red wings': { abbrev: 'DET', league: 'nhl', fullName: 'Detroit Red Wings' },
  'blackhawks': { abbrev: 'CHI', league: 'nhl', fullName: 'Chicago Blackhawks' },
  'chicago blackhawks': { abbrev: 'CHI', league: 'nhl', fullName: 'Chicago Blackhawks' },
  'flyers': { abbrev: 'PHI', league: 'nhl', fullName: 'Philadelphia Flyers' },
  'philadelphia flyers': { abbrev: 'PHI', league: 'nhl', fullName: 'Philadelphia Flyers' },
  'oilers': { abbrev: 'EDM', league: 'nhl', fullName: 'Edmonton Oilers' },
  'edmonton oilers': { abbrev: 'EDM', league: 'nhl', fullName: 'Edmonton Oilers' },
  'avalanche': { abbrev: 'COL', league: 'nhl', fullName: 'Colorado Avalanche' },
  'avs': { abbrev: 'COL', league: 'nhl', fullName: 'Colorado Avalanche' },
  'colorado avalanche': { abbrev: 'COL', league: 'nhl', fullName: 'Colorado Avalanche' },
  'lightning': { abbrev: 'TB', league: 'nhl', fullName: 'Tampa Bay Lightning' },
  'tampa bay lightning': { abbrev: 'TB', league: 'nhl', fullName: 'Tampa Bay Lightning' },
  'golden knights': { abbrev: 'VGK', league: 'nhl', fullName: 'Vegas Golden Knights' },
  'vegas golden knights': { abbrev: 'VGK', league: 'nhl', fullName: 'Vegas Golden Knights' },
  'kraken': { abbrev: 'SEA', league: 'nhl', fullName: 'Seattle Kraken' },
  'seattle kraken': { abbrev: 'SEA', league: 'nhl', fullName: 'Seattle Kraken' },
  'flames': { abbrev: 'CGY', league: 'nhl', fullName: 'Calgary Flames' },
  'calgary flames': { abbrev: 'CGY', league: 'nhl', fullName: 'Calgary Flames' },
  'canucks': { abbrev: 'VAN', league: 'nhl', fullName: 'Vancouver Canucks' },
  'vancouver canucks': { abbrev: 'VAN', league: 'nhl', fullName: 'Vancouver Canucks' },
  'sharks': { abbrev: 'SJ', league: 'nhl', fullName: 'San Jose Sharks' },
  'san jose sharks': { abbrev: 'SJ', league: 'nhl', fullName: 'San Jose Sharks' },
  'ducks': { abbrev: 'ANA', league: 'nhl', fullName: 'Anaheim Ducks' },
  'anaheim ducks': { abbrev: 'ANA', league: 'nhl', fullName: 'Anaheim Ducks' },
  'wild': { abbrev: 'MIN', league: 'nhl', fullName: 'Minnesota Wild' },
  'minnesota wild': { abbrev: 'MIN', league: 'nhl', fullName: 'Minnesota Wild' },
  'predators': { abbrev: 'NSH', league: 'nhl', fullName: 'Nashville Predators' },
  'preds': { abbrev: 'NSH', league: 'nhl', fullName: 'Nashville Predators' },
  'nashville predators': { abbrev: 'NSH', league: 'nhl', fullName: 'Nashville Predators' },
  'blues': { abbrev: 'STL', league: 'nhl', fullName: 'St. Louis Blues' },
  'st louis blues': { abbrev: 'STL', league: 'nhl', fullName: 'St. Louis Blues' },
  'stars': { abbrev: 'DAL', league: 'nhl', fullName: 'Dallas Stars' },
  'dallas stars': { abbrev: 'DAL', league: 'nhl', fullName: 'Dallas Stars' },
  'hurricanes': { abbrev: 'CAR', league: 'nhl', fullName: 'Carolina Hurricanes' },
  'canes': { abbrev: 'CAR', league: 'nhl', fullName: 'Carolina Hurricanes' },
  'carolina hurricanes': { abbrev: 'CAR', league: 'nhl', fullName: 'Carolina Hurricanes' },
  'islanders': { abbrev: 'NYI', league: 'nhl', fullName: 'New York Islanders' },
  'new york islanders': { abbrev: 'NYI', league: 'nhl', fullName: 'New York Islanders' },
  'sabres': { abbrev: 'BUF', league: 'nhl', fullName: 'Buffalo Sabres' },
  'buffalo sabres': { abbrev: 'BUF', league: 'nhl', fullName: 'Buffalo Sabres' },
  'blue jackets': { abbrev: 'CBJ', league: 'nhl', fullName: 'Columbus Blue Jackets' },
  'columbus blue jackets': { abbrev: 'CBJ', league: 'nhl', fullName: 'Columbus Blue Jackets' },
  'devils': { abbrev: 'NJ', league: 'nhl', fullName: 'New Jersey Devils' },
  'new jersey devils': { abbrev: 'NJ', league: 'nhl', fullName: 'New Jersey Devils' },
  'capitals': { abbrev: 'WAS', league: 'nhl', fullName: 'Washington Capitals' },
  'caps': { abbrev: 'WAS', league: 'nhl', fullName: 'Washington Capitals' },
  'washington capitals': { abbrev: 'WAS', league: 'nhl', fullName: 'Washington Capitals' },
  'coyotes': { abbrev: 'ARI', league: 'nhl', fullName: 'Arizona Coyotes' },
  'arizona coyotes': { abbrev: 'ARI', league: 'nhl', fullName: 'Arizona Coyotes' },
  'senators': { abbrev: 'OTT', league: 'nhl', fullName: 'Ottawa Senators' },
  'sens': { abbrev: 'OTT', league: 'nhl', fullName: 'Ottawa Senators' },
  'ottawa senators': { abbrev: 'OTT', league: 'nhl', fullName: 'Ottawa Senators' },
  'jets': { abbrev: 'WPG', league: 'nhl', fullName: 'Winnipeg Jets' },
  'winnipeg jets': { abbrev: 'WPG', league: 'nhl', fullName: 'Winnipeg Jets' },
};

// Set of known team names to avoid fuzzy matching to players
const KNOWN_TEAM_NAMES = new Set(Object.keys(TEAM_MAPPINGS));

// Prisma clients - lazy loaded
let mainPrisma: any = null;
let sportsPrisma: any = null;

async function getMainPrisma() {
  if (!mainPrisma) {
    try {
      const { PrismaClient } = require('@prisma/client');
      mainPrisma = new PrismaClient();
    } catch (e) {
      console.warn('[SmartResolver] Main Prisma not available');
      return null;
    }
  }
  return mainPrisma;
}

async function getSportsPrisma() {
  if (!sportsPrisma) {
    try {
      const sportsMod = require('../../../prisma_sports/generated/sports-client');
      sportsPrisma = new sportsMod.PrismaClient();
    } catch {
      // Fallback to main Prisma
      sportsPrisma = await getMainPrisma();
    }
  }
  return sportsPrisma;
}

// In-memory cache for discovered data
interface CacheEntry<T> {
  data: T;
  expiry: number;
}

class SmartCache {
  private cache: Map<string, CacheEntry<any>> = new Map();
  private defaultTTL = 10 * 60 * 1000; // 10 minutes

  get<T>(key: string): T | null {
    const entry = this.cache.get(key);
    if (entry && entry.expiry > Date.now()) {
      return entry.data;
    }
    this.cache.delete(key);
    return null;
  }

  set<T>(key: string, data: T, ttl?: number): void {
    this.cache.set(key, {
      data,
      expiry: Date.now() + (ttl || this.defaultTTL),
    });
  }

  clear(): void {
    this.cache.clear();
  }
}

const cache = new SmartCache();

/**
 * Levenshtein distance for fuzzy matching
 */
function levenshtein(a: string, b: string): number {
  const matrix: number[][] = [];
  for (let i = 0; i <= b.length; i++) matrix[i] = [i];
  for (let j = 0; j <= a.length; j++) matrix[0][j] = j;

  for (let i = 1; i <= b.length; i++) {
    for (let j = 1; j <= a.length; j++) {
      if (b.charAt(i - 1) === a.charAt(j - 1)) {
        matrix[i][j] = matrix[i - 1][j - 1];
      } else {
        matrix[i][j] = Math.min(
          matrix[i - 1][j - 1] + 1,
          matrix[i][j - 1] + 1,
          matrix[i - 1][j] + 1
        );
      }
    }
  }
  return matrix[b.length][a.length];
}

function similarity(a: string, b: string): number {
  const aLower = a.toLowerCase().trim();
  const bLower = b.toLowerCase().trim();
  if (aLower === bLower) return 1;
  const maxLen = Math.max(aLower.length, bLower.length);
  if (maxLen === 0) return 1;
  return 1 - levenshtein(aLower, bLower) / maxLen;
}

/**
 * Extract potential entity names from text using NLP-like patterns
 */
function extractPotentialNames(text: string): string[] {
  // Common words to skip (not names)
  const skipWords = new Set([
    'how', 'what', 'when', 'where', 'why', 'who', 'which', 'the', 'and', 'for',
    'are', 'was', 'were', 'will', 'has', 'have', 'had', 'can', 'could', 'would',
    'should', 'did', 'does', 'compare', 'show', 'get', 'find', 'tell', 'give',
    'do', 'in', 'on', 'at', 'to', 'is', 'it', 'of', 'or', 'if', 'so', 'as',
    'be', 'by', 'we', 'an', 'no', 'my', 'up', 'all', 'out', 'about', 'their',
    'last', 'game', 'games', 'odds', 'prediction', 'stats', 'tonight', 'today',
    'yesterday', 'tomorrow', 'this', 'that', 'with', 'from', 'they', 'been',
    'not', 'but', 'his', 'her', 'its', 'our', 'you', 'your', 'she', 'him',
    'nfl', 'nba', 'mlb', 'nhl', 'mls', 'vs'
  ]);

  // First, extract ALL capitalized words with their positions
  const words: Array<{ word: string; start: number; end: number }> = [];
  const wordPattern = /\b([A-Z][a-zA-Z]+)\b/g;
  let match;
  while ((match = wordPattern.exec(text)) !== null) {
    words.push({
      word: match[1],
      start: match.index,
      end: match.index + match[1].length,
    });
  }

  // Now find consecutive pairs that could be names (First Last)
  const names: string[] = [];
  const usedPositions = new Set<number>();

  for (let i = 0; i < words.length - 1; i++) {
    const w1 = words[i];
    const w2 = words[i + 1];

    // Check if words are adjacent (only whitespace between them)
    const between = text.slice(w1.end, w2.start);
    if (/^\s+$/.test(between)) {
      // This is a potential two-word name
      const isW1Skip = skipWords.has(w1.word.toLowerCase());
      const isW2Skip = skipWords.has(w2.word.toLowerCase());

      if (!isW1Skip && !isW2Skip) {
        // Both words are valid name parts - add as full name
        names.push(w1.word + ' ' + w2.word);
        usedPositions.add(i);
        usedPositions.add(i + 1);
      } else if (!isW2Skip && isW1Skip) {
        // First word is skip, but second is valid - might be start of a name
        // Check if w2 and w3 form a name
        if (i + 2 < words.length) {
          const w3 = words[i + 2];
          const between2 = text.slice(w2.end, w3.start);
          if (/^\s+$/.test(between2) && !skipWords.has(w3.word.toLowerCase())) {
            names.push(w2.word + ' ' + w3.word);
            usedPositions.add(i + 1);
            usedPositions.add(i + 2);
          }
        }
      }
    }
  }

  // Add single words that weren't used in pairs and aren't skip words
  for (let i = 0; i < words.length; i++) {
    if (!usedPositions.has(i)) {
      const word = words[i].word;
      if (!skipWords.has(word.toLowerCase()) && word.length > 2) {
        names.push(word);
      }
    }
  }

  // Dedupe and sort by length descending
  const uniqueNames = [...new Set(names)];
  uniqueNames.sort((a, b) => b.length - a.length);

  // Filter out single-word names if they're part of a longer name
  const result: string[] = [];
  for (const name of uniqueNames) {
    const nameLower = name.toLowerCase();
    const isPartOfLongerName = result.some(longer =>
      longer.toLowerCase().includes(nameLower) && longer !== name
    );
    if (!isPartOfLongerName) {
      result.push(name);
    }
  }

  return result;
}

export class SmartResolver {
  private playerIndex: Map<string, any[]> = new Map();
  private teamIndex: Map<string, any[]> = new Map();
  private initialized = false;

  /**
   * Initialize indexes from database
   */
  async initialize(): Promise<void> {
    if (this.initialized) return;

    try {
      await this.buildPlayerIndex();
      await this.buildTeamIndex();
      this.initialized = true;
      console.log('[SmartResolver] Initialized with database data');
    } catch (error) {
      console.error('[SmartResolver] Initialization error:', error);
    }
  }

  /**
   * Build player search index from database
   */
  private async buildPlayerIndex(): Promise<void> {
    const cacheKey = 'player_index';
    const cached = cache.get<Map<string, any[]>>(cacheKey);
    if (cached) {
      this.playerIndex = cached;
      return;
    }

    try {
      const prisma = await getSportsPrisma();
      if (!prisma) {
        console.warn('[SmartResolver] No database connection for player index');
        return;
      }

      // Get all players from sports database
      const players = await prisma.player.findMany({
        select: {
          id: true,
          name: true,
          league: true,
          team: true,
          position: true,
          externalPlayerId: true,
        },
        take: 10000,
      });

      // Index by name parts
      for (const player of players) {
        const nameParts = player.name.toLowerCase().split(/\s+/);

        // Index by full name
        const fullKey = player.name.toLowerCase();
        if (!this.playerIndex.has(fullKey)) {
          this.playerIndex.set(fullKey, []);
        }
        this.playerIndex.get(fullKey)!.push(player);

        // Index by last name
        if (nameParts.length > 1) {
          const lastName = nameParts[nameParts.length - 1];
          if (!this.playerIndex.has(lastName)) {
            this.playerIndex.set(lastName, []);
          }
          this.playerIndex.get(lastName)!.push(player);
        }

        // Index by first name
        const firstName = nameParts[0];
        if (!this.playerIndex.has(firstName)) {
          this.playerIndex.set(firstName, []);
        }
        this.playerIndex.get(firstName)!.push(player);
      }

      // Also get NBA players from main database
      try {
        const mainDb = await getMainPrisma();
        const nbaPlayers = await mainDb.nBAPlayer.findMany({
          select: {
            id: true,
            fullName: true,
            firstName: true,
            lastName: true,
            isActive: true,
          },
          take: 1000,
        });

        for (const player of nbaPlayers) {
          const normalized = {
            id: player.id,
            name: player.fullName,
            league: 'NBA',
            team: null,
            position: null,
            externalPlayerId: player.id,
            isActive: player.isActive,
          };

          const fullKey = player.fullName.toLowerCase();
          if (!this.playerIndex.has(fullKey)) {
            this.playerIndex.set(fullKey, []);
          }
          this.playerIndex.get(fullKey)!.push(normalized);

          // Index by last name
          const lastKey = player.lastName.toLowerCase();
          if (!this.playerIndex.has(lastKey)) {
            this.playerIndex.set(lastKey, []);
          }
          this.playerIndex.get(lastKey)!.push(normalized);
        }
      } catch {
        // NBA table might not exist
      }

      cache.set(cacheKey, this.playerIndex, 30 * 60 * 1000); // 30 min cache
      console.log(`[SmartResolver] Indexed ${this.playerIndex.size} player name variants`);
    } catch (error) {
      console.error('[SmartResolver] Error building player index:', error);
    }
  }

  /**
   * Build team search index from database
   */
  private async buildTeamIndex(): Promise<void> {
    const cacheKey = 'team_index';
    const cached = cache.get<Map<string, any[]>>(cacheKey);
    if (cached) {
      this.teamIndex = cached;
      return;
    }

    // First, add all hardcoded team mappings (nicknames, cities, full names)
    for (const [key, mapping] of Object.entries(TEAM_MAPPINGS)) {
      const team = {
        name: mapping.abbrev,
        fullName: mapping.fullName,
        league: mapping.league,
        abbreviation: mapping.abbrev,
      };
      if (!this.teamIndex.has(key)) {
        this.teamIndex.set(key, []);
      }
      this.teamIndex.get(key)!.push(team);

      // Also index by abbreviation
      const abbrevKey = mapping.abbrev.toLowerCase();
      if (!this.teamIndex.has(abbrevKey)) {
        this.teamIndex.set(abbrevKey, []);
      }
      // Avoid duplicates
      if (!this.teamIndex.get(abbrevKey)!.some(t => t.name === team.name && t.league === team.league)) {
        this.teamIndex.get(abbrevKey)!.push(team);
      }
    }

    try {
      const prisma = await getSportsPrisma();
      if (!prisma) {
        console.warn('[SmartResolver] No database connection for team index');
        cache.set(cacheKey, this.teamIndex, 30 * 60 * 1000);
        console.log(`[SmartResolver] Indexed ${this.teamIndex.size} team name variants (from mappings only)`);
        return;
      }

      // Get unique teams from games
      const games = await prisma.sportsGame.findMany({
        select: {
          homeTeam: true,
          awayTeam: true,
          league: true,
        },
        distinct: ['homeTeam', 'awayTeam'],
        take: 1000,
      });

      const teams = new Map<string, { name: string; league: string }>();

      for (const game of games) {
        if (!teams.has(game.homeTeam)) {
          teams.set(game.homeTeam, { name: game.homeTeam, league: game.league });
        }
        if (!teams.has(game.awayTeam)) {
          teams.set(game.awayTeam, { name: game.awayTeam, league: game.league });
        }
      }

      // Build index from database teams
      for (const [name, team] of teams) {
        const nameParts = name.toLowerCase().split(/\s+/);

        // Full name
        const fullKey = name.toLowerCase();
        if (!this.teamIndex.has(fullKey)) {
          this.teamIndex.set(fullKey, []);
        }
        // Avoid duplicates
        if (!this.teamIndex.get(fullKey)!.some(t => t.name === team.name && t.league === team.league)) {
          this.teamIndex.get(fullKey)!.push(team);
        }

        // Each word
        for (const part of nameParts) {
          if (part.length > 2) {
            if (!this.teamIndex.has(part)) {
              this.teamIndex.set(part, []);
            }
            if (!this.teamIndex.get(part)!.some(t => t.name === team.name && t.league === team.league)) {
              this.teamIndex.get(part)!.push(team);
            }
          }
        }
      }

      // Also get NBA teams from main database
      try {
        const mainDb = await getMainPrisma();
        const nbaTeams = await mainDb.nBATeam.findMany({
          select: {
            id: true,
            fullName: true,
            nickname: true,
            abbreviation: true,
            city: true,
          },
        });

        for (const team of nbaTeams) {
          const normalized = {
            name: team.fullName,
            league: 'NBA',
            abbreviation: team.abbreviation,
            city: team.city,
          };

          // Index by full name, nickname, abbreviation, city
          for (const key of [team.fullName, team.nickname, team.abbreviation, team.city]) {
            if (key) {
              const lowerKey = key.toLowerCase();
              if (!this.teamIndex.has(lowerKey)) {
                this.teamIndex.set(lowerKey, []);
              }
              this.teamIndex.get(lowerKey)!.push(normalized);
            }
          }
        }
      } catch {
        // NBA table might not exist
      }

      cache.set(cacheKey, this.teamIndex, 30 * 60 * 1000);
      console.log(`[SmartResolver] Indexed ${this.teamIndex.size} team name variants`);
    } catch (error) {
      console.error('[SmartResolver] Error building team index:', error);
    }
  }

  /**
   * Smart player lookup - searches database intelligently
   */
  /**
   * Check if a player has been traded recently
   */
  private async checkPlayerTransaction(playerName: string, league: string, currentTeam?: string): Promise<{
    wasTradedRecently: boolean;
    fromTeam?: string;
    toTeam?: string;
    transactionDate?: Date;
    transactionType?: string;
  } | null> {
    try {
      const prisma = await getSportsPrisma();

      // Look for recent transactions for this player (within last 12 months)
      const oneYearAgo = new Date();
      oneYearAgo.setFullYear(oneYearAgo.getFullYear() - 1);

      const transaction = await prisma.playerTransaction.findFirst({
        where: {
          playerName: { equals: playerName, mode: 'insensitive' },
          league: league.toLowerCase(),
          transactionDate: { gte: oneYearAgo },
        },
        orderBy: { transactionDate: 'desc' },
      });

      if (transaction) {
        return {
          wasTradedRecently: true,
          fromTeam: transaction.fromTeam || undefined,
          toTeam: transaction.toTeam,
          transactionDate: transaction.transactionDate,
          transactionType: transaction.transactionType,
        };
      }

      return null;
    } catch {
      return null;
    }
  }

  async findPlayer(query: string, leagueHint?: string): Promise<{
    player: any;
    confidence: number;
    alternatives: any[];
    isAmbiguous?: boolean;
    ambiguityMessage?: string;
    tradeInfo?: {
      wasTradedRecently: boolean;
      fromTeam?: string;
      toTeam?: string;
      transactionDate?: Date;
      transactionType?: string;
    };
  } | null> {
    await this.initialize();

    let queryLower = query.toLowerCase().trim();
    const candidates: Array<{ player: any; score: number }> = [];

    // NICKNAME RESOLUTION: Check if query matches a known nickname
    // This allows "Steph Curry" to resolve to "Stephen Curry", etc.
    const nicknameResolved = PLAYER_NICKNAMES[queryLower];
    if (nicknameResolved) {
      queryLower = nicknameResolved.toLowerCase();
    }

    // Check if this is an ambiguous last name without first name
    const isSingleWord = !queryLower.includes(' ') && !queryLower.includes("'");
    const isAmbiguousName = AMBIGUOUS_LAST_NAMES.has(queryLower);

    // SUFFIX HANDLING: Detect name suffixes (Jr, Sr, II, III, IV)
    // When a suffix is present, require EXACT match to prevent "LeBron James Jr" → "LeBron James"
    const suffixPattern = /\s+(jr\.?|sr\.?|ii|iii|iv|v|junior|senior)$/i;
    const hasSuffix = suffixPattern.test(queryLower);
    const queryWithoutSuffix = queryLower.replace(suffixPattern, '').trim();
    const querySuffix = queryLower.match(suffixPattern)?.[1]?.toLowerCase().replace('.', '') || '';

    // Direct lookup
    const directMatch = this.playerIndex.get(queryLower);
    if (directMatch) {
      for (const player of directMatch) {
        if (!leagueHint || player.league?.toLowerCase() === leagueHint.toLowerCase()) {
          candidates.push({ player, score: 1.0 });
        } else {
          candidates.push({ player, score: 0.8 }); // Lower score if league doesn't match
        }
      }
    }

    // If no direct match, try database search FIRST (more accurate than fuzzy)
    // Use queryLower which has nickname resolution applied
    if (candidates.length === 0) {
      const dbResult = await this.searchPlayerInDatabase(queryLower, leagueHint);
      if (dbResult && dbResult.confidence > 0.8) {
        // If query has suffix, verify the matched player also has that suffix
        if (hasSuffix) {
          const matchedName = dbResult.player.name?.toLowerCase() || '';
          const matchedHasSuffix = suffixPattern.test(matchedName);
          if (!matchedHasSuffix) {
            // Query has suffix but match doesn't - this is likely a different person
            // Return null to trigger "not found" response
            return null;
          }
        }

        // Check if this is an ambiguous single name that matched multiple
        if (isSingleWord && isAmbiguousName && dbResult.alternatives.length > 0) {
          return {
            ...dbResult,
            confidence: 0.35, // Low confidence to trigger clarification
            isAmbiguous: true,
            ambiguityMessage: `"${query}" could refer to: ${dbResult.player.name}${dbResult.alternatives.slice(0, 3).map((a: any) => `, ${a.name}`).join('')}. Please specify which player.`,
          };
        }
        return dbResult;
      }
    }

    // Fuzzy search if no direct match and database didn't find exact match
    // IMPORTANT: Skip fuzzy matching entirely if query has a suffix
    // This prevents "LeBron James Jr" from fuzzy matching to "LeBron James"
    if (candidates.length === 0 && !hasSuffix) {
      // Skip fuzzy matching if query is a known team name
      if (!KNOWN_TEAM_NAMES.has(queryLower)) {
        for (const [key, players] of this.playerIndex) {
          const score = similarity(queryLower, key);
          // STRICT THRESHOLD: 0.85 to prevent "LeBron" → "LeRon" type matches
          // The old 0.75 was too permissive
          if (score > 0.85) {
            for (const player of players) {
              // STRICT LEAGUE PENALTY: If leagueHint is provided, heavily penalize wrong-league matches
              // This prevents cross-sport confusion like LeBron (NBA) → LeRon McCoy (NFL)
              let leagueBonus = 0;
              if (leagueHint) {
                if (player.league?.toLowerCase() === leagueHint.toLowerCase()) {
                  leagueBonus = 0.1; // Boost correct league
                } else {
                  leagueBonus = -0.5; // Heavy penalty for wrong league
                }
              }
              candidates.push({ player, score: score + leagueBonus });
            }
          }
        }
      }
    }

    // ADDITIONAL CHECK: If we have candidates but also have a league hint,
    // filter out candidates from wrong leagues entirely if there are correct-league alternatives
    if (leagueHint && candidates.length > 0) {
      const correctLeagueCandidates = candidates.filter(
        c => c.player.league?.toLowerCase() === leagueHint.toLowerCase()
      );
      if (correctLeagueCandidates.length > 0) {
        // Only keep correct-league candidates
        candidates.length = 0;
        candidates.push(...correctLeagueCandidates);
      }
    }

    // Sort by score
    candidates.sort((a, b) => b.score - a.score);

    // Dedupe by player ID
    const seen = new Set<string>();
    const unique = candidates.filter(c => {
      const key = `${c.player.id}-${c.player.league}`;
      if (seen.has(key)) return false;
      seen.add(key);
      return true;
    });

    if (unique.length === 0) {
      // Final fallback to database search with lower threshold
      return this.searchPlayerInDatabase(query, leagueHint);
    }

    // If this is an ambiguous name with multiple candidates, flag it
    if (isSingleWord && isAmbiguousName && unique.length > 1) {
      const topPlayers = unique.slice(0, 4);
      const tradeInfo = await this.checkPlayerTransaction(
        unique[0].player.name,
        unique[0].player.league || leagueHint || ''
      );
      return {
        player: unique[0].player,
        confidence: 0.35, // Low confidence to trigger clarification
        alternatives: unique.slice(1, 4).map(c => c.player),
        isAmbiguous: true,
        ambiguityMessage: `"${query}" could refer to: ${topPlayers.map(p => `${p.player.name} (${p.player.team || p.player.league})`).join(', ')}. Please specify which player.`,
        tradeInfo: tradeInfo || undefined,
      };
    }

    // Check if the found player has been traded recently
    const tradeInfo = await this.checkPlayerTransaction(
      unique[0].player.name,
      unique[0].player.league || leagueHint || '',
      unique[0].player.team
    );

    return {
      player: unique[0].player,
      confidence: unique[0].score,
      alternatives: unique.slice(1, 4).map(c => c.player),
      tradeInfo: tradeInfo || undefined,
    };
  }

  /**
   * Direct database search as fallback
   */
  private async searchPlayerInDatabase(query: string, leagueHint?: string): Promise<{
    player: any;
    confidence: number;
    alternatives: any[];
    tradeInfo?: {
      wasTradedRecently: boolean;
      fromTeam?: string;
      toTeam?: string;
      transactionDate?: Date;
      transactionType?: string;
    };
  } | null> {
    try {
      const prisma = await getSportsPrisma();

      // Handle league case-insensitively (database may store as lowercase or uppercase)
      const where: any = {
        name: { contains: query, mode: 'insensitive' },
      };
      if (leagueHint) {
        where.league = { in: [leagueHint.toLowerCase(), leagueHint.toUpperCase()] };
      }

      const players = await prisma.player.findMany({
        where,
        take: 5,
      });

      if (players.length === 0) return null;

      // Check for recent trades
      const tradeInfo = await this.checkPlayerTransaction(
        players[0].name,
        players[0].league || leagueHint || '',
        players[0].team
      );

      return {
        player: players[0],
        confidence: similarity(query, players[0].name),
        alternatives: players.slice(1),
        tradeInfo: tradeInfo || undefined,
      };
    } catch {
      return null;
    }
  }

  /**
   * Smart team lookup
   */
  async findTeam(query: string, leagueHint?: string): Promise<{
    team: any;
    confidence: number;
    alternatives: any[];
  } | null> {
    await this.initialize();

    const queryLower = query.toLowerCase().trim();
    const candidates: Array<{ team: any; score: number }> = [];

    // Check hardcoded mappings first for exact match
    const mappedTeam = TEAM_MAPPINGS[queryLower];
    if (mappedTeam) {
      const team = {
        name: mappedTeam.abbrev,
        fullName: mappedTeam.fullName,
        league: mappedTeam.league,
        abbreviation: mappedTeam.abbrev,
      };
      if (!leagueHint || mappedTeam.league === leagueHint.toLowerCase()) {
        return {
          team,
          confidence: 1.0,
          alternatives: [],
        };
      }
      candidates.push({ team, score: 0.85 });
    }

    // Direct lookup in index
    const directMatch = this.teamIndex.get(queryLower);
    if (directMatch) {
      for (const team of directMatch) {
        if (!leagueHint || team.league?.toLowerCase() === leagueHint.toLowerCase()) {
          candidates.push({ team, score: 1.0 });
        } else {
          candidates.push({ team, score: 0.7 });
        }
      }
    }

    // Fuzzy search (increased threshold from 0.7 to 0.85)
    if (candidates.length === 0) {
      for (const [key, teams] of this.teamIndex) {
        const score = similarity(queryLower, key);
        if (score > 0.85) {
          for (const team of teams) {
            const leagueBonus = (!leagueHint || team.league?.toLowerCase() === leagueHint.toLowerCase()) ? 0 : -0.15;
            candidates.push({ team, score: score + leagueBonus });
          }
        }
      }
    }

    candidates.sort((a, b) => b.score - a.score);

    const seen = new Set<string>();
    const unique = candidates.filter(c => {
      const key = `${c.team.name}-${c.team.league}`;
      if (seen.has(key)) return false;
      seen.add(key);
      return true;
    });

    if (unique.length === 0) return null;

    return {
      team: unique[0].team,
      confidence: unique[0].score,
      alternatives: unique.slice(1, 4).map(c => c.team),
    };
  }

  /**
   * Auto-detect league from context
   */
  async detectLeague(text: string): Promise<string | null> {
    await this.initialize();

    const textLower = text.toLowerCase();

    // Explicit league mentions
    const leaguePatterns: Record<string, RegExp> = {
      'nba': /\b(nba|basketball)\b/i,
      'nfl': /\b(nfl|football)\b/i,
      'mlb': /\b(mlb|baseball)\b/i,
      'nhl': /\b(nhl|hockey)\b/i,
      'ncaab': /\b(ncaa\s*b|college\s*basketball|march\s*madness)\b/i,
      'ncaaf': /\b(ncaa\s*f|college\s*football|cfb)\b/i,
      'ufc': /\b(ufc|mma|octagon)\b/i,
      'epl': /\b(premier\s*league|epl)\b/i,
    };

    for (const [league, pattern] of Object.entries(leaguePatterns)) {
      if (pattern.test(textLower)) {
        return league;
      }
    }

    // Check for known team names in hardcoded mappings first
    const words = textLower.split(/\s+/);
    for (let i = 0; i < words.length; i++) {
      // Try single word
      const word = words[i];
      if (TEAM_MAPPINGS[word]) {
        return TEAM_MAPPINGS[word].league;
      }
      // Try two-word combinations
      if (i < words.length - 1) {
        const twoWord = `${word} ${words[i + 1]}`;
        if (TEAM_MAPPINGS[twoWord]) {
          return TEAM_MAPPINGS[twoWord].league;
        }
      }
      // Try three-word combinations
      if (i < words.length - 2) {
        const threeWord = `${word} ${words[i + 1]} ${words[i + 2]}`;
        if (TEAM_MAPPINGS[threeWord]) {
          return TEAM_MAPPINGS[threeWord].league;
        }
      }
    }

    // Try to infer from team/player names via database
    const names = extractPotentialNames(text);
    for (const name of names) {
      const teamResult = await this.findTeam(name);
      if (teamResult && teamResult.confidence > 0.8) {
        return teamResult.team.league?.toLowerCase();
      }

      const playerResult = await this.findPlayer(name);
      if (playerResult && playerResult.confidence > 0.8) {
        return playerResult.player.league?.toLowerCase();
      }
    }

    return null;
  }

  /**
   * Get recent games for context
   */
  async getRecentGames(teamName?: string, playerName?: string, league?: string, limit = 5): Promise<any[]> {
    try {
      const prisma = await getSportsPrisma();

      const where: any = {};

      if (league) {
        // Handle both lowercase and uppercase league names
        where.league = { in: [league.toLowerCase(), league.toUpperCase()] };
      }

      if (teamName) {
        // Search for team in both home and away, case-insensitive
        where.OR = [
          { homeTeam: { contains: teamName, mode: 'insensitive' } },
          { awayTeam: { contains: teamName, mode: 'insensitive' } },
        ];
      }

      const games = await prisma.sportsGame.findMany({
        where,
        orderBy: { gameDate: 'desc' },
        take: limit,
      });

      return games;
    } catch (err) {
      console.error('[SmartResolver] getRecentGames error:', err);
      return [];
    }
  }

  /**
   * Get player stats for context
   * @param playerName - Name of the player to look up
   * @param league - Optional league filter (nba, nfl, etc.)
   * @param gameCount - Number of games to fetch (default 10, max 100)
   * @param dateRange - Optional date range to filter stats (prevents data leakage)
   */
  async getPlayerStats(
    playerName: string,
    league?: string,
    gameCount = 10,
    dateRange?: { start: Date; end: Date }
  ): Promise<any[]> {
    try {
      const playerResult = await this.findPlayer(playerName, league);
      if (!playerResult) return [];

      const prisma = await getSportsPrisma();

      // Build where clause with league filter to prevent cross-sport matching
      const playerLeague = league?.toLowerCase() || playerResult.player.league?.toLowerCase();
      const whereClause: any = {
        OR: [
          { playerExternalId: playerResult.player.externalPlayerId },
          { playerName: { contains: playerResult.player.name, mode: 'insensitive' } },
        ],
      };

      // Add league filter if available (critical to avoid matching NHL Fox stats for NBA Fox)
      if (playerLeague) {
        whereClause.league = { in: [playerLeague, playerLeague.toUpperCase()] };
      }

      // CRITICAL: Add date range filter to prevent data leakage
      // If a historical date range is specified, ONLY return data from that range
      if (dateRange) {
        whereClause.gameDate = {
          gte: dateRange.start,
          lte: dateRange.end,
        };
      }

      // Fetch enough stat entries to cover requested games
      // Each game can have 10-15 stat entries, so fetch gameCount * 15 entries
      const statEntriesNeeded = Math.min(gameCount * 15, 1500);

      const stats = await prisma.playerGameMetric.findMany({
        where: whereClause,
        orderBy: { gameDate: 'desc' },
        take: statEntriesNeeded,
      });

      return stats;
    } catch {
      return [];
    }
  }

  /**
   * Smart context builder - figures out what data is relevant
   */
  async buildContext(query: string): Promise<{
    league: string | null;
    players: any[];
    teams: any[];
    recentGames: any[];
    playerStats: any[];
    timeframe: { start: Date; end: Date } | null;
    unresolvedNames: string[];
    requestedGameCount: number;
    historicalYear: number | null;
    dateRange: { start: Date; end: Date } | undefined;
  }> {
    const league = await this.detectLeague(query);
    const names = extractPotentialNames(query);

    const players: any[] = [];
    const teams: any[] = [];
    const resolvedNames = new Set<string>();

    for (const name of names.slice(0, 5)) {
      const nameLower = name.toLowerCase();

      // Check if this is a known team name first (prioritize teams)
      if (KNOWN_TEAM_NAMES.has(nameLower)) {
        const teamResult = await this.findTeam(name, league || undefined);
        if (teamResult && teamResult.confidence > 0.6) {
          teams.push({
            ...teamResult.team,
            confidence: teamResult.confidence,
            alternatives: teamResult.alternatives,
          });
          resolvedNames.add(nameLower);
          continue;
        }
      }

      // Try team lookup first for any name
      const teamResult = await this.findTeam(name, league || undefined);
      if (teamResult && teamResult.confidence > 0.8) {
        teams.push({
          ...teamResult.team,
          confidence: teamResult.confidence,
          alternatives: teamResult.alternatives,
        });
        resolvedNames.add(nameLower);
        continue;
      }

      // Then try player lookup
      const playerResult = await this.findPlayer(name, league || undefined);
      // Accept high confidence OR ambiguous matches (we want to show clarification for ambiguous)
      // INCREASED THRESHOLD: 0.7 -> 0.85 to prevent "LeBron" -> "LeRon McCoy" (0.83) errors
      if (playerResult && (playerResult.confidence > 0.85 || playerResult.isAmbiguous)) {
        players.push({
          ...playerResult.player,
          confidence: playerResult.confidence,
          alternatives: playerResult.alternatives,
          isAmbiguous: playerResult.isAmbiguous,
          ambiguityMessage: playerResult.ambiguityMessage,
        });
        resolvedNames.add(nameLower);
        continue;
      }

      // Fall back to team with lower confidence if no player found
      if (teamResult && teamResult.confidence > 0.6) {
        teams.push({
          ...teamResult.team,
          confidence: teamResult.confidence,
          alternatives: teamResult.alternatives,
        });
        resolvedNames.add(nameLower);
      }
      // If nothing was resolved, this name is unknown
    }

    // Track unresolved names (potential fake/unknown entities)
    const unresolvedNames = names
      .filter(name => !resolvedNames.has(name.toLowerCase()))
      .filter(name => name.length > 3 && !/^(the|what|how|who|which|can|does|will|is|are|today|tonight|yesterday)$/i.test(name));

    // Parse requested game count from query (e.g., "last 20 games" → 20)
    const requestedGameCount = this.parseGameCount(query);

    // CRITICAL: Parse timeframe FIRST to use for date filtering
    // This prevents data leakage (e.g., 2026 data appearing in 2023 queries)
    const timeframe = this.parseTimeframe(query);
    const historicalYear = this.parseHistoricalYear(query);

    // Prepare date range for filtering (if historical query)
    let dateRange: { start: Date; end: Date } | undefined;
    if (timeframe && (timeframe as any).isHistorical) {
      dateRange = { start: timeframe.start, end: timeframe.end };
    } else if (historicalYear) {
      // Explicit historical year - set date range for that year
      dateRange = {
        start: new Date(historicalYear, 0, 1),
        end: new Date(historicalYear, 11, 31, 23, 59, 59, 999),
      };
    }

    // Get recent games (with date filtering if historical)
    let recentGames: any[] = [];
    if (teams.length > 0) {
      recentGames = await this.getRecentGames(teams[0].name, undefined, league || undefined, requestedGameCount);
    } else if (league) {
      recentGames = await this.getRecentGames(undefined, undefined, league, requestedGameCount);
    }

    // Get player stats - use the parsed game count AND date range filter
    let playerStats: any[] = [];
    if (players.length > 0) {
      playerStats = await this.getPlayerStats(players[0].name, league || undefined, requestedGameCount, dateRange);
    }

    return {
      league,
      players,
      teams,
      recentGames,
      playerStats,
      timeframe,
      unresolvedNames,
      requestedGameCount,
      historicalYear,
      dateRange,
    };
  }

  /**
   * Parse requested game count from query (e.g., "last 20 games" → 20)
   */
  parseGameCount(query: string): number {
    const queryLower = query.toLowerCase();

    // Match "last X games" pattern
    const match = queryLower.match(/\blast\s*(\d+)\s*games?\b/i);
    if (match) {
      // Cap at 100 games max to prevent excessive data fetching
      return Math.min(parseInt(match[1], 10), 100);
    }

    // Match "past X games"
    const pastMatch = queryLower.match(/\bpast\s*(\d+)\s*games?\b/i);
    if (pastMatch) {
      return Math.min(parseInt(pastMatch[1], 10), 100);
    }

    // Match "X game average" or "X-game average"
    const avgMatch = queryLower.match(/\b(\d+)[\s-]*games?\s*(?:average|avg|mean)\b/i);
    if (avgMatch) {
      return Math.min(parseInt(avgMatch[1], 10), 100);
    }

    // Single game queries
    if (/\blast\s*game\b|\byesterday\b|\blast\s*night\b/i.test(queryLower)) {
      return 1;
    }

    // Default to 10 games
    return 10;
  }

  /**
   * Parse timeframe from query
   */
  private parseTimeframe(query: string): { start: Date; end: Date; isHistorical?: boolean; requestedYear?: number } | null {
    const now = new Date();
    const queryLower = query.toLowerCase();

    // Relative dates
    if (/\b(today|tonight)\b/.test(queryLower)) {
      const start = new Date(now);
      start.setHours(0, 0, 0, 0);
      const end = new Date(now);
      end.setHours(23, 59, 59, 999);
      return { start, end };
    }

    if (/\byesterday\b|\blast\s*night\b/.test(queryLower)) {
      const start = new Date(now);
      start.setDate(start.getDate() - 1);
      start.setHours(0, 0, 0, 0);
      const end = new Date(start);
      end.setHours(23, 59, 59, 999);
      return { start, end };
    }

    if (/\blast\s*game\b/.test(queryLower)) {
      const end = new Date(now);
      const start = new Date(now);
      start.setDate(start.getDate() - 7);
      return { start, end };
    }

    if (/\b(this|last)\s*week\b/.test(queryLower)) {
      const isLast = /\blast\b/.test(queryLower);
      const start = new Date(now);
      start.setDate(start.getDate() - start.getDay() - (isLast ? 7 : 0));
      start.setHours(0, 0, 0, 0);
      const end = new Date(start);
      end.setDate(end.getDate() + 6);
      end.setHours(23, 59, 59, 999);
      return { start, end };
    }

    if (/\b(this|last)\s*month\b/.test(queryLower)) {
      const isLast = /\blast\b/.test(queryLower);
      const start = new Date(now.getFullYear(), now.getMonth() - (isLast ? 1 : 0), 1);
      const end = new Date(now.getFullYear(), now.getMonth() + (isLast ? 0 : 1), 0, 23, 59, 59, 999);
      return { start, end };
    }

    // Season patterns (e.g., "2023 season", "2023-24 season")
    const seasonMatch = queryLower.match(/\b(20\d{2})(?:\s*-\s*(20)?\d{2})?\s*(?:season)?\b/);
    if (seasonMatch) {
      const year = parseInt(seasonMatch[1]);
      const currentYear = now.getFullYear();

      // HISTORICAL DATA CHECK: If querying for a past season, flag it
      if (year < currentYear - 1) {
        // This is a historical query - set strict date bounds
        const start = new Date(year, 9, 1); // October of season start
        const end = new Date(year + 1, 5, 30); // June of season end
        return { start, end, isHistorical: true, requestedYear: year };
      }

      const start = new Date(year, 9, 1); // October
      const end = new Date(year + 1, 5, 30); // June
      return { start, end };
    }

    // Explicit year mention without "season" (e.g., "in 2023", "2024 props")
    const yearOnlyMatch = queryLower.match(/\b(in\s+)?(20(?:2[0-5]|1\d))\b/);
    if (yearOnlyMatch) {
      const year = parseInt(yearOnlyMatch[2]);
      const currentYear = now.getFullYear();

      // If querying for a historical year
      if (year < currentYear) {
        const start = new Date(year, 0, 1); // January 1
        const end = new Date(year, 11, 31, 23, 59, 59, 999); // December 31
        return { start, end, isHistorical: true, requestedYear: year };
      }
    }

    // Default to last 30 days
    const start = new Date(now);
    start.setDate(start.getDate() - 30);
    return { start, end: now };
  }

  /**
   * Parse historical year from query - returns year if query references a specific past year
   */
  parseHistoricalYear(query: string): number | null {
    const queryLower = query.toLowerCase();
    const now = new Date();
    const currentYear = now.getFullYear();

    // Match patterns like "2023", "in 2024", "2023 season", "2023-24"
    const yearMatch = queryLower.match(/\b(20(?:2[0-5]|1\d))\b/);
    if (yearMatch) {
      const year = parseInt(yearMatch[1]);
      // Only return if it's actually a historical year (before current year)
      if (year < currentYear) {
        return year;
      }
    }

    return null;
  }
}

export const smartResolver = new SmartResolver();
