/**
 * Hybrid LoL Stats API Client:
 *   1. Riot API for match history (if RIOT_API_KEY set) — shows ALL game modes
 *   2. op.gg REST API for profile/ranked stats + renewal — no key needed
 *   3. MCP fallback for edge cases
 */

import type {
  RegionInfo, SummonerProfile, MatchEntry, MatchParticipant,
  MatchParticipantStats, TeamStat,
} from './types.js';

const OPGG_API = 'https://lol-api-summoner.op.gg/api';
const MCP_URL = 'https://mcp-api.op.gg/mcp';
let rpcId = 1;

// ── Riot API Config ──

const RIOT_API_KEY = process.env.RIOT_API_KEY ?? '';

/** Map our region codes to Riot regional routing (for Account-V1, Match-V5) */
const REGION_TO_ROUTING: Record<string, string> = {
  EUW: 'europe', EUNE: 'europe', TR: 'europe', RU: 'europe',
  NA: 'americas', BR: 'americas', LAN: 'americas', LAS: 'americas',
  KR: 'asia', JP: 'asia',
  OCE: 'sea', PH: 'sea', SG: 'sea', TH: 'sea', TW: 'sea', VN: 'sea',
};

/** Map queue IDs to human-readable names */
const QUEUE_NAMES: Record<number, string> = {
  420: 'SOLORANKED', 440: 'FLEXRANKED', 400: 'NORMAL', 430: 'NORMAL',
  450: 'ARAM', 900: 'URF', 1020: 'ONE_FOR_ALL', 1300: 'NEXUS_BLITZ',
  1400: 'ULTIMATE_SPELLBOOK', 1700: 'ARENA', 1900: 'URF',
  720: 'ARAM', // ARAM Clash
  0: 'CUSTOM',
};

// ── Regions ──

export const REGIONS: RegionInfo[] = [
  { code: 'EUW', label: 'EU West' },
  { code: 'EUNE', label: 'EU Nordic & East' },
  { code: 'NA', label: 'North America' },
  { code: 'KR', label: 'Korea' },
  { code: 'BR', label: 'Brazil' },
  { code: 'LAN', label: 'Latin America North' },
  { code: 'LAS', label: 'Latin America South' },
  { code: 'TR', label: 'Turkey' },
  { code: 'RU', label: 'Russia' },
  { code: 'JP', label: 'Japan' },
  { code: 'OCE', label: 'Oceania' },
  { code: 'PH', label: 'Philippines' },
  { code: 'SG', label: 'Singapore' },
  { code: 'TH', label: 'Thailand' },
  { code: 'TW', label: 'Taiwan' },
  { code: 'VN', label: 'Vietnam' },
];

// ── DDragon Champion Mapping ──
// Maps champion_id → champion_name for image URLs

let champMap: Map<number, string> = new Map();
let champMapLoaded = false;

async function loadChampionMap(): Promise<void> {
  if (champMapLoaded) return;
  try {
    // Get latest DDragon version
    const verRes = await fetch('https://ddragon.leagueoflegends.com/api/versions.json');
    const versions = await verRes.json() as string[];
    const ver = versions[0];

    // Fetch champion data
    const champRes = await fetch(`https://ddragon.leagueoflegends.com/cdn/${ver}/data/en_US/champion.json`);
    const champData = await champRes.json() as any;

    for (const [, champ] of Object.entries<any>(champData.data)) {
      champMap.set(Number(champ.key), champ.id); // e.g., 119 → "Draven"
    }

    champMapLoaded = true;
    console.log(`[LoLStats] Loaded ${champMap.size} champions from DDragon v${ver}`);
  } catch (e: any) {
    console.error('[LoLStats] Failed to load DDragon champions:', e.message);
  }
}

function champName(id: number): string {
  return champMap.get(id) ?? `Champion${id}`;
}

// ── Summoner Lookup ──

interface SummonerLookup {
  summoner_id: string;
  id: number;
  puuid: string;
  game_name: string;
  tagline: string;
  level: number;
  profile_image_url: string;
  updated_at: string;
}

async function lookupSummoner(
  gameName: string, tagLine: string, region: string,
): Promise<SummonerLookup> {
  const regionLower = region.toLowerCase();
  // op.gg REST API requires riot_id with # separator (URL-encoded as %23)
  const riotId = `${gameName}#${tagLine}`;
  const url = `${OPGG_API}/v3/${regionLower}/summoners?riot_id=${encodeURIComponent(riotId)}&hl=en_US`;

  console.log(`[LoLStats] Summoner lookup: ${gameName}#${tagLine} @ ${region}`);

  const res = await fetch(url, {
    headers: { 'Accept': 'application/json' },
  });

  if (!res.ok) {
    throw new Error(`op.gg summoner lookup HTTP ${res.status}`);
  }

  const json = await res.json() as any;
  const data = json?.data;

  // Response is an array of matches
  const entry = Array.isArray(data) ? data[0] : data;
  if (!entry?.summoner_id) {
    throw new Error('Summoner not found on op.gg');
  }

  return {
    summoner_id: entry.summoner_id,
    id: entry.id,
    puuid: entry.puuid,
    game_name: entry.game_name ?? gameName,
    tagline: entry.tagline ?? tagLine,
    level: entry.level ?? 0,
    profile_image_url: entry.profile_image_url ?? '',
    updated_at: entry.updated_at ?? '',
  };
}

// ── Profile via REST API ──

export async function getProfile(
  gameName: string, tagLine: string, region: string,
): Promise<SummonerProfile> {
  await loadChampionMap();
  const regionLower = region.toLowerCase();

  // Step 1: Lookup summoner to get summoner_id
  const summoner = await lookupSummoner(gameName, tagLine, region);

  // Step 2: Fetch summary (ranked stats, most champions)
  const summaryUrl = `${OPGG_API}/${regionLower}/summoners/${encodeURIComponent(summoner.summoner_id)}/summary?hl=en_US`;
  let leagueStats: any[] = [];
  let ladderRank = { rank: null as number | null, total: null as number | null };
  let mostChampions: any = { game_type: '', play: 0, win: 0, lose: 0, champion_stats: [] };

  try {
    const res = await fetch(summaryUrl, { headers: { 'Accept': 'application/json' } });
    if (res.ok) {
      const json = await res.json() as any;
      const sum = json?.data?.summoner ?? json?.data;

      if (sum?.league_stats) {
        leagueStats = sum.league_stats;
      }
      if (sum?.ladder_rank) {
        ladderRank = sum.ladder_rank;
      }
      if (sum?.most_champions) {
        // Transform champion stats to include champion_name
        const mc = sum.most_champions;
        if (mc?.champion_stats) {
          mc.champion_stats = mc.champion_stats.map((cs: any) => ({
            ...cs,
            champion_name: cs.champion_name ?? champName(cs.id ?? cs.champion_id ?? 0),
          }));
        }
        mostChampions = mc;
      }
    }
  } catch (e: any) {
    console.warn('[LoLStats] Summary fetch failed:', e.message);
  }

  return {
    game_name: summoner.game_name,
    tagline: summoner.tagline,
    level: summoner.level,
    profile_image_url: summoner.profile_image_url,
    league_stats: leagueStats,
    ladder_rank: ladderRank,
    most_champions: mostChampions,
    updated_at: summoner.updated_at,
  };
}

// ── Match History — Riot API (primary) or op.gg REST (fallback) ──

export async function getMatches(
  gameName: string, tagLine: string, region: string, limit = 10,
): Promise<MatchEntry[]> {
  await loadChampionMap();

  // Prefer Riot API if key is available — shows ALL game modes (URF, Brawl, etc.)
  if (RIOT_API_KEY) {
    try {
      return await getMatchesRiot(gameName, tagLine, region, limit);
    } catch (e: any) {
      console.warn(`[LoLStats] Riot API failed (${e.message}), falling back to op.gg`);
    }
  }

  // Fallback: op.gg REST API (only standard modes)
  return getMatchesOpgg(gameName, tagLine, region, limit);
}

// ── Riot API Match History ──

async function riotFetch(url: string): Promise<any> {
  const res = await fetch(url, {
    headers: { 'X-Riot-Token': RIOT_API_KEY },
  });
  if (res.status === 403) throw new Error('Riot API key expired or invalid');
  if (res.status === 429) throw new Error('Riot API rate limited');
  if (!res.ok) throw new Error(`Riot API HTTP ${res.status}`);
  return res.json();
}

async function getMatchesRiot(
  gameName: string, tagLine: string, region: string, limit = 10,
): Promise<MatchEntry[]> {
  const routing = REGION_TO_ROUTING[region.toUpperCase()] ?? 'europe';
  const baseUrl = `https://${routing}.api.riotgames.com`;

  console.log(`[LoLStats] Fetching matches via Riot API (${routing})`);

  // Step 1: Get PUUID
  const account = await riotFetch(
    `${baseUrl}/riot/account/v1/accounts/by-riot-id/${encodeURIComponent(gameName)}/${encodeURIComponent(tagLine)}`,
  );
  const puuid = account.puuid;
  if (!puuid) throw new Error('PUUID not found');

  // Step 2: Get match IDs
  const matchIds: string[] = await riotFetch(
    `${baseUrl}/lol/match/v5/matches/by-puuid/${puuid}/ids?start=0&count=${Math.min(limit, 20)}`,
  );

  if (!matchIds.length) {
    console.log('[LoLStats] No match IDs from Riot API');
    return [];
  }

  console.log(`[LoLStats] Got ${matchIds.length} match IDs, fetching details...`);

  // Step 3: Fetch match details in parallel (batches of 5 to respect rate limits)
  const results: MatchEntry[] = [];
  const batchSize = 5;

  for (let i = 0; i < matchIds.length; i += batchSize) {
    const batch = matchIds.slice(i, i + batchSize);
    const details = await Promise.all(
      batch.map(id =>
        riotFetch(`${baseUrl}/lol/match/v5/matches/${id}`)
          .catch(e => { console.warn(`[LoLStats] Failed to fetch ${id}:`, e.message); return null; }),
      ),
    );

    for (const d of details) {
      if (d?.info) results.push(transformRiotMatch(d));
    }
  }

  console.log(`[LoLStats] Processed ${results.length} matches from Riot API`);
  return results;
}

function transformRiotMatch(data: any): MatchEntry {
  const info = data.info;
  const matchId = data.metadata?.matchId ?? '';

  const participants: MatchParticipant[] = (info.participants ?? []).map((p: any) => ({
    champion_name: p.championName ?? champName(p.championId ?? 0),
    champion_id: p.championId ?? 0,
    items: [p.item0, p.item1, p.item2, p.item3, p.item4, p.item5, p.item6].filter((x: any) => x != null),
    items_names: [],
    position: p.teamPosition ?? p.individualPosition ?? '',
    team_key: p.teamId === 100 ? 'BLUE' : 'RED',
    spells: [p.summoner1Id, p.summoner2Id].filter((x: any) => x != null),
    stats: {
      kill: p.kills ?? 0,
      death: p.deaths ?? 0,
      assist: p.assists ?? 0,
      minion_kill: p.totalMinionsKilled ?? 0,
      neutral_minion_kill: p.neutralMinionsKilled ?? 0,
      gold_earned: p.goldEarned ?? 0,
      total_damage_dealt_to_champions: p.totalDamageDealtToChampions ?? 0,
      total_damage_taken: p.totalDamageTaken ?? 0,
      ward_place: p.wardsPlaced ?? 0,
      vision_wards_bought_in_game: p.visionWardsBoughtInGame ?? 0,
      champion_level: p.champLevel ?? 0,
      op_score: 0,
      op_score_rank: 0,
      result: p.win ? 'WIN' : 'LOSE',
    },
    summoner: {
      game_name: p.riotIdGameName ?? p.summonerName ?? '',
      tagline: p.riotIdTagline ?? '',
    },
  }));

  // Build team stats from participant data
  const teamKills: Record<string, number> = { BLUE: 0, RED: 0 };
  const teamGold: Record<string, number> = { BLUE: 0, RED: 0 };
  const teamWin: Record<string, boolean> = { BLUE: false, RED: false };

  for (const p of participants) {
    const tk = p.team_key;
    teamKills[tk] = (teamKills[tk] ?? 0) + p.stats.kill;
    teamGold[tk] = (teamGold[tk] ?? 0) + p.stats.gold_earned;
    if (p.stats.result === 'WIN') teamWin[tk] = true;
  }

  const riotTeams = info.teams ?? [];
  const teams: TeamStat[] = ['BLUE', 'RED'].map(key => {
    const rTeam = riotTeams.find((t: any) => (t.teamId === 100 ? 'BLUE' : 'RED') === key);
    return {
      key,
      banned_champions_names: (rTeam?.bans ?? []).map((b: any) => champName(b.championId ?? 0)),
      game_stat: {
        is_win: teamWin[key] ?? false,
        baron_kill: rTeam?.objectives?.baron?.kills ?? 0,
        dragon_kill: rTeam?.objectives?.dragon?.kills ?? 0,
        tower_kill: rTeam?.objectives?.tower?.kills ?? 0,
        champion_kill: teamKills[key] ?? 0,
        gold_earned: teamGold[key] ?? 0,
        inhibitor_kill: rTeam?.objectives?.inhibitor?.kills ?? 0,
        rift_herald_kill: rTeam?.objectives?.riftHerald?.kills ?? 0,
      },
    };
  });

  // Determine game type from queue ID
  const queueId = info.queueId ?? 0;
  const gameType = QUEUE_NAMES[queueId] ?? info.gameMode ?? 'UNKNOWN';

  return {
    id: matchId,
    created_at: new Date(info.gameCreation ?? 0).toISOString(),
    game_length_second: info.gameDuration ?? 0,
    game_type: gameType,
    game_map: info.mapId === 12 ? 'HOWLING_ABYSS' : info.mapId === 11 ? 'SUMMONERS_RIFT' : `MAP_${info.mapId}`,
    average_tier_info: undefined,
    participants,
    teams,
  };
}

// ── op.gg REST API Match History ──

async function getMatchesOpgg(
  gameName: string, tagLine: string, region: string, limit = 10,
): Promise<MatchEntry[]> {
  const regionLower = region.toLowerCase();
  const summoner = await lookupSummoner(gameName, tagLine, region);

  const gamesUrl = `${OPGG_API}/${regionLower}/summoners/${encodeURIComponent(summoner.summoner_id)}/games?limit=${Math.min(limit, 20)}&game_type=total&hl=en_US`;

  console.log(`[LoLStats] Fetching matches via op.gg REST API`);

  const res = await fetch(gamesUrl, { headers: { 'Accept': 'application/json' } });

  if (!res.ok) {
    console.warn(`[LoLStats] REST games HTTP ${res.status}, falling back to MCP`);
    return getMatchesMCP(gameName, tagLine, region, limit);
  }

  const json = await res.json() as any;
  const games = json?.data;

  if (!Array.isArray(games) || games.length === 0) {
    console.log('[LoLStats] No games from op.gg REST API');
    return [];
  }

  console.log(`[LoLStats] Got ${games.length} games from op.gg REST API`);
  return games.map((game: any) => transformGame(game));
}

function transformGame(game: any): MatchEntry {
  const participants: MatchParticipant[] = (game.participants ?? []).map((p: any) => ({
    champion_name: champName(p.champion_id ?? 0),
    champion_id: p.champion_id ?? 0,
    items: p.items ?? [],
    items_names: (p.items ?? []).map(() => ''), // REST API doesn't include item names
    position: p.position ?? '',
    team_key: p.team_key ?? '',
    spells: p.spells ?? [],
    stats: {
      kill: p.stats?.kill ?? 0,
      death: p.stats?.death ?? 0,
      assist: p.stats?.assist ?? 0,
      minion_kill: p.stats?.minion_kill ?? 0,
      neutral_minion_kill: p.stats?.neutral_minion_kill ?? 0,
      gold_earned: p.stats?.gold_earned ?? 0,
      total_damage_dealt_to_champions: p.stats?.total_damage_dealt_to_champions ?? 0,
      total_damage_taken: p.stats?.total_damage_taken ?? 0,
      ward_place: p.stats?.ward_place ?? 0,
      vision_wards_bought_in_game: p.stats?.vision_wards_bought_in_game ?? 0,
      champion_level: p.stats?.champion_level ?? 0,
      op_score: p.stats?.op_score ?? 0,
      op_score_rank: p.stats?.op_score_rank ?? 0,
      result: p.stats?.result ?? '',
    },
    summoner: {
      game_name: p.summoner?.game_name ?? '',
      tagline: p.summoner?.tagline ?? '',
    },
  }));

  const teams: TeamStat[] = (game.teams ?? []).map((t: any) => ({
    key: t.key ?? '',
    banned_champions_names: t.banned_champions_names ?? [],
    game_stat: {
      is_win: t.game_stat?.is_win ?? false,
      baron_kill: t.game_stat?.baron_kill ?? 0,
      dragon_kill: t.game_stat?.dragon_kill ?? 0,
      tower_kill: t.game_stat?.tower_kill ?? 0,
      champion_kill: t.game_stat?.champion_kill ?? 0,
      gold_earned: t.game_stat?.gold_earned ?? 0,
      inhibitor_kill: t.game_stat?.inhibitor_kill ?? 0,
      rift_herald_kill: t.game_stat?.rift_herald_kill ?? 0,
    },
  }));

  return {
    id: game.id ?? '',
    created_at: game.created_at ?? '',
    game_length_second: game.game_length_second ?? 0,
    game_type: game.game_type ?? '',
    game_map: game.game_map ?? '',
    average_tier_info: game.average_tier_info ?? undefined,
    participants,
    teams,
  };
}

// ── Match Detail (REST API returns all 10 players already) ──

export async function getMatchDetail(
  gameId: string, createdAt: string, region: string,
): Promise<MatchEntry | null> {
  // The REST API already returns all 10 participants per game,
  // so for "detail" we don't need a separate call. This function
  // is kept for compatibility but uses the MCP as fallback.
  try {
    return await getMatchDetailMCP(gameId, createdAt, region);
  } catch (e: any) {
    console.warn('[LoLStats] Match detail MCP fallback failed:', e.message);
    return null;
  }
}

// ── Summoner Renewal ──

export async function renewSummoner(
  gameName: string, tagLine: string, region: string,
): Promise<{ renewed: boolean; last_updated_at: string; message?: string }> {
  const regionLower = region.toLowerCase();

  // Step 1: Get summoner_id
  let summoner: SummonerLookup;
  try {
    summoner = await lookupSummoner(gameName, tagLine, region);
  } catch (e: any) {
    console.error('[LoLStats] Summoner lookup failed:', e.message);
    throw new Error(`Summoner not found on op.gg: ${e.message}`);
  }

  console.log(`[LoLStats] Triggering renewal for ${summoner.game_name}#${summoner.tagline} (${summoner.summoner_id})`);

  // Step 2: POST renewal
  const renewUrl = `${OPGG_API}/${regionLower}/summoners/${encodeURIComponent(summoner.summoner_id)}/renewal`;
  const maxPolls = 10;

  for (let attempt = 0; attempt < maxPolls; attempt++) {
    const res = await fetch(renewUrl, {
      method: 'POST',
      headers: { 'Accept': 'application/json' },
    });

    const json = await res.json() as any;

    // Cooldown (already renewed recently)
    if (json.message?.includes('Already renewed') || json.message?.includes('already')) {
      console.log(`[LoLStats] Already renewed recently, last_updated_at=${json.last_updated_at}`);
      return {
        renewed: true,
        last_updated_at: json.last_updated_at ?? '',
        message: json.message,
      };
    }

    // Check if finish
    const data = json.data ?? json;
    if (data.finish === true) {
      console.log(`[LoLStats] Renewal complete, last_updated_at=${data.last_updated_at}`);
      return {
        renewed: true,
        last_updated_at: data.last_updated_at ?? '',
      };
    }

    // Not finished — wait and poll again
    const delay = Math.min(data.delay ?? 1000, 3000);
    console.log(`[LoLStats] Renewal in progress, waiting ${delay}ms (attempt ${attempt + 1}/${maxPolls})`);
    await new Promise(r => setTimeout(r, delay));
  }

  console.warn('[LoLStats] Renewal polling timeout');
  return {
    renewed: false,
    last_updated_at: '',
    message: 'Renewal timed out — data may still be updating',
  };
}

// ══════════════════════════════════════════════════════════════
// MCP Fallback — kept for match detail and edge cases
// ══════════════════════════════════════════════════════════════

async function mcpCall(toolName: string, args: Record<string, any>): Promise<any> {
  const id = rpcId++;
  const body = {
    jsonrpc: '2.0',
    id,
    method: 'tools/call',
    params: { name: toolName, arguments: args },
  };

  const res = await fetch(MCP_URL, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Accept': 'application/json, text/event-stream',
    },
    body: JSON.stringify(body),
  });

  if (!res.ok) throw new Error(`MCP HTTP ${res.status}: ${res.statusText}`);

  const json = await res.json() as any;
  if (json.error) throw new Error(json.error.message ?? JSON.stringify(json.error));

  const text = json.result?.content?.[0]?.text;
  if (!text) throw new Error('Empty MCP response');
  return text;
}

// ── MCP class notation parser ──

function parseClassNotation(text: string): any {
  const lines = text.split('\n');
  const classDefs: Map<string, string[]> = new Map();
  let dataLine = '';

  for (const line of lines) {
    const classMatch = line.match(/^class (\w+): (.+)$/);
    if (classMatch) {
      classDefs.set(classMatch[1], classMatch[2].split(','));
      continue;
    }
    if (line.trim()) dataLine = line.trim();
  }
  if (!dataLine) throw new Error('No data in MCP response');
  return parseConstructor(dataLine, classDefs);
}

function parseConstructor(input: string, classDefs: Map<string, string[]>): any {
  const ctorMatch = input.match(/^(\w+)\((.+)\)$/s);
  if (!ctorMatch) return parsePrimitive(input);

  const className = ctorMatch[1];
  const innerContent = ctorMatch[2];
  const fields = classDefs.get(className);
  if (!fields) return parseConstructor(innerContent, classDefs);

  const values = splitTopLevel(innerContent);
  const obj: Record<string, any> = {};
  for (let i = 0; i < fields.length && i < values.length; i++) {
    const fieldName = fields[i].trim();
    const rawVal = values[i].trim();
    if (fieldName.endsWith('[]')) {
      obj[fieldName.replace('[]', '')] = parseArrayValue(rawVal, classDefs);
    } else {
      obj[fieldName] = parseValue(rawVal, classDefs);
    }
  }
  return obj;
}

function parseArrayValue(input: string, classDefs: Map<string, string[]>): any[] {
  if (input === '[]' || input === 'null' || input === 'None') return [];
  if (input.startsWith('[') && input.endsWith(']')) {
    const inner = input.slice(1, -1).trim();
    if (!inner) return [];
    return splitTopLevel(inner).map(item => parseValue(item.trim(), classDefs));
  }
  return [parseValue(input, classDefs)];
}

function parseValue(input: string, classDefs: Map<string, string[]>): any {
  if (!input) return null;
  const ctorMatch = input.match(/^(\w+)\(/);
  if (ctorMatch && classDefs.has(ctorMatch[1])) return parseConstructor(input, classDefs);
  if (input.startsWith('[') && input.endsWith(']')) return parseArrayValue(input, classDefs);
  return parsePrimitive(input);
}

function parsePrimitive(input: string): any {
  if (input === 'null' || input === 'None') return null;
  if (input === 'true' || input === 'True') return true;
  if (input === 'false' || input === 'False') return false;
  if ((input.startsWith('"') && input.endsWith('"')) || (input.startsWith("'") && input.endsWith("'")))
    return input.slice(1, -1);
  const num = Number(input);
  if (!isNaN(num) && input !== '') return num;
  return input;
}

function splitTopLevel(input: string): string[] {
  const results: string[] = [];
  let depth = 0, inString = false, stringChar = '', current = '';
  for (let i = 0; i < input.length; i++) {
    const ch = input[i];
    const prev = i > 0 ? input[i - 1] : '';
    if (inString) { current += ch; if (ch === stringChar && prev !== '\\') inString = false; continue; }
    if (ch === '"' || ch === "'") { inString = true; stringChar = ch; current += ch; continue; }
    if (ch === '(' || ch === '[') { depth++; current += ch; continue; }
    if (ch === ')' || ch === ']') { depth--; current += ch; continue; }
    if (ch === ',' && depth === 0) { results.push(current); current = ''; continue; }
    current += ch;
  }
  if (current.trim()) results.push(current);
  return results;
}

// ── MCP-based functions (fallback) ──

async function getMatchesMCP(
  gameName: string, tagLine: string, region: string, limit = 10,
): Promise<MatchEntry[]> {
  console.log('[LoLStats] Using MCP fallback for matches');
  const raw = await mcpCall('lol_list_summoner_matches', {
    game_name: gameName, tag_line: tagLine, region, lang: 'en_US', limit,
    desired_output_fields: [
      'data.game_history[].{id,created_at,game_length_second,game_type,game_map}',
      'data.game_history[].average_tier_info.{tier,division}',
      'data.game_history[].participants[].{champion_name,champion_id,items[],items_names[],position,team_key,spells[]}',
      'data.game_history[].participants[].stats.{kill,death,assist,minion_kill,neutral_minion_kill,gold_earned,total_damage_dealt_to_champions,total_damage_taken,ward_place,vision_wards_bought_in_game,champion_level,op_score,op_score_rank,result}',
      'data.game_history[].participants[].summoner.{game_name,tagline}',
      'data.game_history[].teams[].{key,banned_champions_names[]}',
      'data.game_history[].teams[].game_stat.{is_win,baron_kill,dragon_kill,tower_kill,champion_kill,gold_earned,inhibitor_kill,rift_herald_kill}',
    ],
  });
  const parsed = parseClassNotation(raw);
  const history = parsed?.game_history ?? parsed?.data?.game_history ?? [];
  return Array.isArray(history) ? history : [];
}

async function getMatchDetailMCP(
  gameId: string, createdAt: string, region: string,
): Promise<MatchEntry | null> {
  const raw = await mcpCall('lol_get_summoner_game_detail', {
    game_id: gameId, created_at: createdAt, region, lang: 'en_US',
    desired_output_fields: [
      'data.game_detail.{id,created_at,game_length_second,game_type,game_map}',
      'data.game_detail.average_tier_info.{tier,division}',
      'data.game_detail.participants[].{champion_name,champion_id,items[],items_names[],position,team_key,spells[]}',
      'data.game_detail.participants[].stats.{kill,death,assist,minion_kill,neutral_minion_kill,gold_earned,total_damage_dealt_to_champions,total_damage_taken,ward_place,vision_wards_bought_in_game,champion_level,op_score,op_score_rank,result}',
      'data.game_detail.participants[].summoner.{game_name,tagline}',
      'data.game_detail.teams[].{key,banned_champions_names[]}',
      'data.game_detail.teams[].game_stat.{is_win,baron_kill,dragon_kill,tower_kill,champion_kill,gold_earned,inhibitor_kill,rift_herald_kill}',
    ],
  });
  const parsed = parseClassNotation(raw);
  return parsed?.game_detail ?? parsed?.data?.game_detail ?? null;
}
