/**
 * ESPN Public API Client — fetches authoritative roster data.
 * Used by the roster-reconciler worker to validate SGO player data.
 */

const ESPN_BASE = 'https://site.api.espn.com/apis/site/v2/sports';

// Map our internal league keys to ESPN's sport/league path segments
export const ESPN_LEAGUES: Record<string, { sport: string; league: string; label: string }> = {
    nba:        { sport: 'basketball', league: 'nba',   label: 'NBA' },
    ncaab:      { sport: 'basketball', league: 'mens-college-basketball', label: 'NCAAB' },
    nfl:        { sport: 'football',   league: 'nfl',   label: 'NFL' },
    mlb:        { sport: 'baseball',   league: 'mlb',   label: 'MLB' },
    nhl:        { sport: 'hockey',     league: 'nhl',   label: 'NHL' },
    wnba:       { sport: 'basketball', league: 'wnba',  label: 'WNBA' },
    epl:        { sport: 'soccer',     league: 'eng.1', label: 'EPL' },
    la_liga:    { sport: 'soccer',     league: 'esp.1', label: 'La Liga' },
    bundesliga: { sport: 'soccer',     league: 'ger.1', label: 'Bundesliga' },
    serie_a:    { sport: 'soccer',     league: 'ita.1', label: 'Serie A' },
    ligue_1:    { sport: 'soccer',     league: 'fra.1', label: 'Ligue 1' },
};

export interface ESPNTeam {
    id: string;
    name: string;        // displayName e.g. "Atlanta Hawks"
    short: string;       // abbreviation e.g. "ATL"
}

export interface ESPNPlayer {
    espnId: string;
    name: string;        // displayName or fullName
    teamId: string;      // ESPN team ID
    teamName: string;
    teamShort: string;
    position: string;
}

export interface ESPNLeagueRoster {
    league: string;
    teams: ESPNTeam[];
    players: ESPNPlayer[];
}

/**
 * Fetch a URL with a 10-second timeout. Parses JSON and throws on non-ok responses.
 */
export async function espnFetch(url: string): Promise<any> {
    const response = await fetch(url, {
        signal: AbortSignal.timeout(10_000),
    });

    if (!response.ok) {
        throw new Error(`ESPN API error: ${response.status} ${response.statusText} for ${url}`);
    }

    return response.json();
}

/**
 * Fetch all teams for a given ESPN sport/league.
 * GET {ESPN_BASE}/{sport}/{league}/teams?limit=2000
 * Parses from sports[0].leagues[0].teams[].team
 */
export async function fetchESPNTeams(sport: string, league: string): Promise<ESPNTeam[]> {
    const url = `${ESPN_BASE}/${sport}/${league}/teams?limit=2000`;
    const data = await espnFetch(url);

    const rawTeams = data?.sports?.[0]?.leagues?.[0]?.teams ?? [];

    return rawTeams.map((entry: any) => {
        const t = entry.team;
        return {
            id: String(t.id),
            name: t.displayName ?? t.name ?? '',
            short: t.abbreviation ?? '',
        };
    });
}

/**
 * Fetch the full roster for a single team.
 * GET {ESPN_BASE}/{sport}/{league}/teams/{teamId}/roster
 *
 * Handles two ESPN response shapes:
 *   - Grouped: athletes[].items[] (NFL, MLB have position groups)
 *   - Flat:    athletes[] directly (each element is a player object)
 */
export async function fetchESPNRoster(
    sport: string,
    league: string,
    teamId: string,
): Promise<ESPNPlayer[]> {
    const url = `${ESPN_BASE}/${sport}/${league}/teams/${teamId}/roster`;
    const data = await espnFetch(url);

    const teamName: string = data?.team?.displayName ?? data?.team?.name ?? '';
    const teamShort: string = data?.team?.abbreviation ?? '';

    const athletes: any[] = data?.athletes ?? [];
    const players: ESPNPlayer[] = [];

    for (const entry of athletes) {
        // Grouped shape: entry has .items[] array (position groups like "offense", "defense")
        if (Array.isArray(entry.items)) {
            for (const athlete of entry.items) {
                players.push({
                    espnId: String(athlete.id),
                    name: athlete.displayName ?? athlete.fullName ?? '',
                    teamId,
                    teamName,
                    teamShort,
                    position: athlete.position?.abbreviation ?? '',
                });
            }
        } else {
            // Flat shape: entry is the athlete object itself
            players.push({
                espnId: String(entry.id),
                name: entry.displayName ?? entry.fullName ?? '',
                teamId,
                teamName,
                teamShort,
                position: entry.position?.abbreviation ?? '',
            });
        }
    }

    return players;
}

/**
 * Orchestrate a full roster pull for one league.
 * Fetches all teams, then each team's roster with a 100ms delay between requests.
 * On individual team roster errors, logs a warning and continues.
 */
export async function fetchLeagueRoster(leagueKey: string): Promise<ESPNLeagueRoster> {
    const config = ESPN_LEAGUES[leagueKey];
    if (!config) {
        throw new Error(`Unknown league key: ${leagueKey}. Valid keys: ${Object.keys(ESPN_LEAGUES).join(', ')}`);
    }

    const { sport, league, label } = config;

    console.log(`[ESPN] Fetching teams for ${label} (${leagueKey})...`);
    const teams = await fetchESPNTeams(sport, league);
    console.log(`[ESPN] Found ${teams.length} ${label} teams`);

    const allPlayers: ESPNPlayer[] = [];

    for (let i = 0; i < teams.length; i++) {
        const team = teams[i];

        // 100ms delay between team requests to be respectful
        if (i > 0) {
            await new Promise((resolve) => setTimeout(resolve, 100));
        }

        try {
            const roster = await fetchESPNRoster(sport, league, team.id);
            allPlayers.push(...roster);
            console.log(`[ESPN] ${label} ${team.short} (${team.name}): ${roster.length} players`);
        } catch (err: any) {
            console.log(`[ESPN] WARNING: Failed to fetch roster for ${team.short} (${team.name}, id=${team.id}): ${err.message}`);
        }
    }

    console.log(`[ESPN] ${label} complete: ${teams.length} teams, ${allPlayers.length} players total`);

    return {
        league: leagueKey,
        teams,
        players: allPlayers,
    };
}

// ---------------------------------------------------------------------------
// Injury types & fetcher
// ---------------------------------------------------------------------------

export interface ESPNInjury {
    espnId: string;            // injury ID from ESPN
    playerName: string;
    athleteId: string;         // ESPN athlete ID (may be empty)
    teamName: string;
    teamShort: string;
    status: string;            // Out, Day-To-Day, Suspension, Injured Reserve, etc.
    date: string;              // ISO date of report
    shortComment: string;
    longComment: string;
    injuryType: string;        // extracted from comment or type field
}

export interface ESPNLeagueInjuries {
    league: string;
    injuries: ESPNInjury[];
    teamCount: number;
}

/**
 * Fetch all injuries for a league from ESPN.
 * GET {ESPN_BASE}/{sport}/{league}/injuries
 */
export async function fetchLeagueInjuries(leagueKey: string): Promise<ESPNLeagueInjuries> {
    const config = ESPN_LEAGUES[leagueKey];
    if (!config) {
        throw new Error(`Unknown league key for injuries: ${leagueKey}`);
    }

    const { sport, league, label } = config;
    const url = `${ESPN_BASE}/${sport}/${league}/injuries`;

    console.log(`[ESPN-INJ] Fetching injuries for ${label}...`);

    const data = await espnFetch(url);
    const teamEntries: any[] = data?.injuries ?? [];
    const injuries: ESPNInjury[] = [];

    for (const teamEntry of teamEntries) {
        const teamName: string = teamEntry.displayName ?? teamEntry.name ?? '';
        // Extract team abbreviation from team data if available
        const teamShort: string = teamEntry.abbreviation ?? teamEntry.team?.abbreviation ?? '';

        for (const inj of (teamEntry.injuries || [])) {
            const athlete = inj.athlete || {};
            injuries.push({
                espnId: String(inj.id || ''),
                playerName: athlete.displayName || athlete.fullName ||
                    `${athlete.firstName || ''} ${athlete.lastName || ''}`.trim(),
                athleteId: String(athlete.id || ''),
                teamName,
                teamShort,
                status: inj.status || 'Unknown',
                date: inj.date || '',
                shortComment: inj.shortComment || '',
                longComment: inj.longComment || '',
                injuryType: typeof inj.type === 'object'
                    ? (inj.type?.description || inj.type?.name || '')
                    : (inj.type || ''),
            });
        }
    }

    console.log(`[ESPN-INJ] ${label}: ${injuries.length} injuries across ${teamEntries.length} teams`);

    return {
        league: leagueKey,
        injuries,
        teamCount: teamEntries.length,
    };
}
