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

// MLB Stats API — free, no key required.
// leagueId 103 = American League, 104 = National League
const MLB_STANDINGS_URL =
  "https://statsapi.mlb.com/api/v1/standings?leagueId=103,104&standingsTypes=regularSeason&hydrate=team(division,league)";

interface MlbSplitRecord {
  type: string;     // "home", "away", "lastTen", etc.
  wins: number;
  losses: number;
}

interface MlbTeamRecord {
  team: {
    id: number;
    name: string;
    league?: { name?: string };
    division?: { name?: string };
  };
  wins: number;
  losses: number;
  gamesPlayed: number;
  winningPercentage: string;     // e.g. ".617"
  divisionRank?: string;         // e.g. "1"–"5" — rank within the 5-team division (all teams)
  wildCardRank?: string;         // e.g. "1" — only set for WC-ranked teams
  leagueRank?: string;           // rank within the 15-team AL or NL
  gamesBack: string;             // "-" for leader, "2.0" otherwise
  wildCardGamesBack?: string;
  streak?: {
    streakCode: string;  // full streak string e.g. "W3" or "L5" — NOT just the letter
    streakNumber: number; // numeric part; redundant with streakCode, not used
  };
  records?: { splitRecords?: MlbSplitRecord[] };
}

interface MlbDivisionRecord {
  teamRecords: MlbTeamRecord[];
}

interface MlbStandingsResponse {
  records: MlbDivisionRecord[];
}

function sortByRecord(a: MlbTeamRecord, b: MlbTeamRecord): number {
  return b.wins !== a.wins ? b.wins - a.wins : a.losses - b.losses;
}

export class MlbStandingsAdapter implements StandingsSyncAdapter {
  constructor(private season: number = new Date().getFullYear()) {}

  async fetchStandings(): Promise<FetchedStandingsRecord[]> {
    const url = `${MLB_STANDINGS_URL}&season=${this.season}`;
    const response = await fetch(url);
    if (!response.ok) {
      throw new Error(`MLB standings API returned ${response.status}: ${response.statusText}`);
    }

    const json = (await response.json()) as MlbStandingsResponse;

    if (!json.records || !Array.isArray(json.records)) {
      throw new Error("Unexpected MLB standings API response shape");
    }

    // Collect all team records from all division groups
    const allTeamRecords: MlbTeamRecord[] = json.records.flatMap(
      (divRecord) => divRecord.teamRecords ?? []
    );

    if (allTeamRecords.length === 0) {
      throw new Error("MLB standings API returned no team records");
    }

    // Compute leagueRank by sorting all teams by wins desc, losses asc within each league.
    // We'll assign ranks per league (AL rank 1–15, NL rank 1–15) separately since
    // the API's leagueRank field may reflect conference-level rank.
    const alTeams = allTeamRecords.filter(
      (t) => t.team.league?.name?.includes("American")
    );
    const nlTeams = allTeamRecords.filter(
      (t) => t.team.league?.name?.includes("National")
    );

    const alSorted = [...alTeams].toSorted(sortByRecord);
    const nlSorted = [...nlTeams].toSorted(sortByRecord);

    const conferenceRankMap = new Map<number, number>();
    for (const [i, t] of alSorted.entries()) conferenceRankMap.set(t.team.id, i + 1);
    for (const [i, t] of nlSorted.entries()) conferenceRankMap.set(t.team.id, i + 1);

    // Compute overall league rank across all 30 teams for leagueRank field
    const allSorted = [...allTeamRecords].toSorted(sortByRecord);
    const overallRankMap = new Map<number, number>();
    for (const [i, t] of allSorted.entries()) overallRankMap.set(t.team.id, i + 1);

    return allTeamRecords.map((team): FetchedStandingsRecord => {
      // Conference: "American League" → "AL", "National League" → "NL"
      const leagueName = team.team.league?.name ?? "";
      const conference = leagueName.startsWith("American")
        ? "AL"
        : leagueName.startsWith("National")
        ? "NL"
        : leagueName;

      // Division: "AL East", "AL Central", etc.
      const division = team.team.division?.name ?? null;

      // Win percentage
      const winPct = parseFloat(team.winningPercentage) || 0;

      // Games back — "-" means leader; use null. Use isNaN guard so GB=0 (tied for first) is kept.
      const gbParsed = parseFloat(team.gamesBack);
      const gbStr = (team.gamesBack === "-" || isNaN(gbParsed)) ? null : gbParsed;

      // Division rank and conference rank
      const divisionRank = team.divisionRank ? parseInt(team.divisionRank, 10) : null;

      // Conference rank: use the per-league rank computed above
      const conferenceRank = conferenceRankMap.get(team.team.id) ?? null;

      // streakCode is the full string e.g. "W3"; streakNumber is the numeric part embedded within it
      const streak = team.streak?.streakCode;

      // Split records
      const splits = team.records?.splitRecords ?? [];
      const homeSplit  = splits.find((s) => s.type === "home");
      const awaySplit  = splits.find((s) => s.type === "away");
      const lastTenSplit = splits.find((s) => s.type === "lastTen");

      const homeRecord = homeSplit
        ? `${homeSplit.wins}-${homeSplit.losses}`
        : undefined;
      const awayRecord = awaySplit
        ? `${awaySplit.wins}-${awaySplit.losses}`
        : undefined;
      const lastTen = lastTenSplit
        ? `${lastTenSplit.wins}-${lastTenSplit.losses}`
        : undefined;

      return {
        teamName: team.team.name,
        externalTeamId: String(team.team.id),
        wins: team.wins,
        losses: team.losses,
        gamesPlayed: team.gamesPlayed,
        winPct,
        // MLB has no OT losses
        conference,
        division: division ?? undefined,
        divisionRank: divisionRank ?? undefined,
        conferenceRank: conferenceRank ?? undefined,
        leagueRank: overallRankMap.get(team.team.id) ?? 1,
        gamesBack: gbStr ?? undefined,
        streak,
        lastTen,
        homeRecord,
        awayRecord,
      };
    });
  }
}
