import type { FetchedStandingsRecord, StandingsSyncAdapter } from "./types";

const NBA_STANDINGS_URL =
  "https://site.api.espn.com/apis/v2/sports/basketball/nba/standings";

interface EspnStat {
  name: string;
  displayName?: string;
  shortDisplayName?: string;
  description?: string;
  abbreviation?: string;
  type?: string;
  value?: number;
  displayValue?: string;
}

interface EspnTeam {
  id: string;
  displayName: string;
  shortDisplayName?: string;
  abbreviation?: string;
  location?: string;
  name?: string;
}

interface EspnStandingsEntry {
  team: EspnTeam;
  stats: EspnStat[];
}

interface EspnStandingsGroup {
  name?: string;
  standings?: { entries?: EspnStandingsEntry[] };
  children?: EspnStandingsGroup[];
}

interface EspnStandingsResponse {
  children?: EspnStandingsGroup[];
}

function statsMap(stats: EspnStat[]): Map<string, EspnStat> {
  const map = new Map<string, EspnStat>();
  for (const stat of stats) {
    map.set(stat.name, stat);
  }
  return map;
}

/**
 * Flatten the ESPN standings response (conference → division → entries).
 * Returns enriched entries with conference and division names attached.
 */
function flattenEspnStandings(
  response: EspnStandingsResponse
): Array<{ entry: EspnStandingsEntry; conference: string; division: string }> {
  const results: Array<{
    entry: EspnStandingsEntry;
    conference: string;
    division: string;
  }> = [];

  for (const confGroup of response.children ?? []) {
    const conferenceName = confGroup.name ?? "";

    // Some ESPN responses have nested division children
    if (confGroup.children && confGroup.children.length > 0) {
      for (const divGroup of confGroup.children) {
        const divisionName = divGroup.name ?? "";
        for (const entry of divGroup.standings?.entries ?? []) {
          results.push({ entry, conference: conferenceName, division: divisionName });
        }
      }
    } else {
      // Flat conference without division sub-groups
      for (const entry of confGroup.standings?.entries ?? []) {
        results.push({ entry, conference: conferenceName, division: "" });
      }
    }
  }

  return results;
}

export class NbaStandingsAdapter implements StandingsSyncAdapter {
  async fetchStandings(): Promise<FetchedStandingsRecord[]> {
    const response = await fetch(NBA_STANDINGS_URL);
    if (!response.ok) {
      throw new Error(`NBA standings API returned ${response.status}: ${response.statusText}`);
    }

    const json = (await response.json()) as EspnStandingsResponse;
    const flattened = flattenEspnStandings(json);

    if (flattened.length === 0) {
      throw new Error("NBA standings API returned no entries — response shape may have changed");
    }

    // Assign league rank by sorting on wins desc, then losses asc
    const sorted = [...flattened].toSorted((a, b) => {
      const winsA = statsMap(a.entry.stats).get("wins")?.value ?? 0;
      const winsB = statsMap(b.entry.stats).get("wins")?.value ?? 0;
      if (winsB !== winsA) return winsB - winsA;
      const lossA = statsMap(a.entry.stats).get("losses")?.value ?? 0;
      const lossB = statsMap(b.entry.stats).get("losses")?.value ?? 0;
      return lossA - lossB;
    });

    return sorted.map(({ entry, conference, division }, leagueIdx): FetchedStandingsRecord => {
      const sm = statsMap(entry.stats);
      const wins = sm.get("wins")?.value ?? 0;
      const losses = sm.get("losses")?.value ?? 0;
      const winPercent = sm.get("winPercent")?.value ?? sm.get("winPct")?.value ?? 0;
      const gamesBehind = sm.get("gamesBehind")?.value;
      const streak = sm.get("streak")?.displayValue ?? sm.get("streakSummary")?.displayValue;

      // ESPN returns last-10 as "Last Ten Games" with a displayValue like "7-3"
      const lastTen =
        sm.get("Last Ten Games")?.displayValue ??
        sm.get("L10")?.displayValue ??
        undefined;

      // Seed within conference — ESPN key is "playoffSeed", value is a float (1.0, 2.0 ...)
      const playoffSeedStat = sm.get("playoffSeed");
      const conferenceRank =
        playoffSeedStat !== undefined && playoffSeedStat.value !== null && playoffSeedStat.value !== undefined
          ? Math.round(playoffSeedStat.value)
          : playoffSeedStat?.displayValue
          ? parseInt(playoffSeedStat.displayValue, 10) || undefined
          : undefined;

      const gamesPlayed = wins + losses;

      // Home/road records — ESPN returns these as displayValue strings (e.g. "24-17")
      const homeRecord = sm.get("Home")?.displayValue ?? undefined;
      const awayRecord = sm.get("Road")?.displayValue ?? undefined;

      return {
        teamName: entry.team.displayName,
        externalTeamId: entry.team.id,
        wins: Math.round(wins),
        losses: Math.round(losses),
        winPct: winPercent,
        gamesPlayed,
        gamesBack: gamesBehind,
        conference,
        division: division || undefined,
        conferenceRank,
        leagueRank: leagueIdx + 1,
        streak,
        lastTen,
        homeRecord,
        awayRecord,
      };
    });
  }
}
