import type { Game, Team } from './apiResponseTypes'
import type { Player } from './playersService'
import { MODERN_STAT_ERA_START, SITE_URL, isNotUndefined } from './utilities'

// TODO make a stats object that has properties for current regular season, current post season, 
// last 4 games, historical seasons, career totals
// could split into batting/pitching/fielding so only necessary ones are called to save time

export interface FieldingStat {
  game: Game | 'TOT'
  player: Player
  team: Team
  pos: 'P' | 'C' | '1B' | '2B' | '3B' | 'SS' | 'LF' | 'CF' | 'RF' | 'TOT'
  xCheckCount: number
  hit: number
  error: number
  stolenBaseCheckCount: number
  stolenBaseCount: number
  caughtStealingCount: number
  passedBallCount: number
  winProbabilityAdded: number
  weightedFieldingPercent: number
  caughtStealingPercent: number
}

interface FieldingStatRaw {
  game: Game | 'TOT'
  player: Player
  team: Team
  pos: 'P' | 'C' | '1B' | '2B' | '3B' | 'SS' | 'LF' | 'CF' | 'RF' | 'TOT'
  "x-ch": number
  hit: number
  error: number
  "sb-ch": number
  sb: number
  cs: number
  pb: number
  wpa: number
  "wf%": number
  "cs%": number | ''
}

interface LegacyFieldingStat {
  player: Player
  team: Team
  pos: 'P' | 'C' | '1B' | '2B' | '3B' | 'SS' | 'LF' | 'CF' | 'RF' | 'TOT' | 'PH'
  xch: number
  xhit: number
  error: number
  pb: number
  sbc: number
  csc: number
}

export async function fetchFieldingStatsBySeasonAndPlayerId(seasonNumber: number, playerId: number, isRegularSeason: boolean): Promise<FieldingStat[]> {
  // different endpoint for pre-modern stats (/plays) era
  if (seasonNumber < MODERN_STAT_ERA_START) {
    return await fetchLegacyFieldingStatsBySeasonAndPlayerId(seasonNumber, playerId, isRegularSeason)
  }

  const response = await fetch(`${SITE_URL}/api/v3/plays/fielding?season=${seasonNumber}&player_id=${playerId}&group_by=playerposition&s_type=${isRegularSeason ? 'regular' : 'post'}`)

  const fieldingStatsResponse: {
    count: number
    stats: FieldingStatRaw[]
  } = await response.json()

  if (fieldingStatsResponse.count === 0) return []

  return fieldingStatsResponse.stats.map(normalizeFieldingStat)
}

async function fetchLegacyFieldingStatsBySeasonAndPlayerId(seasonNumber: number, playerId: number, isRegularSeason: boolean): Promise<FieldingStat[]> {
  const response = await fetch(`${SITE_URL}/api/v3/fieldingstats/totals?season=${seasonNumber}&player_id=${playerId}&s_type=${isRegularSeason ? 'regular' : 'post'}`)

  const legacyFieldingStatsResponse: {
    count: number
    stats: LegacyFieldingStat[]
  } = await response.json()

  if (legacyFieldingStatsResponse.count === 0) return []

  return legacyFieldingStatsResponse.stats
    .map(makeModernFieldingStatFromLegacy)
    .filter(isNotUndefined)
}

export async function fetchFieldingStatsForLastFourGamesBySeasonAndPlayerId(seasonNumber: number, playerId: number): Promise<FieldingStat[]> {
  const response = await fetch(`${SITE_URL}/api/v3/plays/fielding?season=${seasonNumber}&player_id=${playerId}&group_by=playerpositiongame&limit=4&s_type=regular&sort=newest`)

  const fieldingStatsResponse: {
    count: number
    stats: FieldingStatRaw[]
  } = await response.json()

  if (fieldingStatsResponse.count > 4) {
    throw new Error(`fieldingStatsService.fetchFieldingStatsForLastFourBySeasonAndPlayerId - Expected at most 4 games, return contained ${fieldingStatsResponse.count}`)
  }

  return fieldingStatsResponse.stats.map(normalizeFieldingStat)
}

export function aggregateFieldingStats(fieldingStats: FieldingStat[]): FieldingStat[] {
  const fieldingStatsByPosition = fieldingStats.reduce((statsByPos: { [key: string]: FieldingStat }, stat: FieldingStat) => {
    if (!statsByPos[stat.pos]) {
      statsByPos[stat.pos] = {
        game: 'TOT',
        player: stat.player,
        team: stat.team,
        pos: stat.pos,
        xCheckCount: stat.xCheckCount,
        hit: stat.hit,
        error: stat.error,
        stolenBaseCheckCount: stat.stolenBaseCheckCount,
        stolenBaseCount: stat.stolenBaseCount,
        caughtStealingCount: stat.caughtStealingCount,
        passedBallCount: stat.passedBallCount,
        winProbabilityAdded: stat.winProbabilityAdded,
        weightedFieldingPercent: stat.weightedFieldingPercent,
        caughtStealingPercent: stat.caughtStealingPercent
      }
    } else {
      statsByPos[stat.pos].xCheckCount += stat.xCheckCount
      statsByPos[stat.pos].hit += stat.hit
      statsByPos[stat.pos].error += stat.error
      statsByPos[stat.pos].stolenBaseCheckCount += stat.stolenBaseCheckCount
      statsByPos[stat.pos].stolenBaseCount += stat.stolenBaseCount
      statsByPos[stat.pos].caughtStealingCount += stat.caughtStealingCount
      statsByPos[stat.pos].passedBallCount += stat.passedBallCount
      statsByPos[stat.pos].winProbabilityAdded += stat.winProbabilityAdded
      statsByPos[stat.pos].weightedFieldingPercent = weightedFieldingPercentage({
        xChecks: statsByPos[stat.pos].xCheckCount,
        hits: statsByPos[stat.pos].hit,
        errors: statsByPos[stat.pos].error
      })
      statsByPos[stat.pos].caughtStealingPercent = caughtStealingPercentage({
        stolenBaseAttempts: statsByPos[stat.pos].stolenBaseCheckCount,
        caughtStealingCount: statsByPos[stat.pos].caughtStealingCount
      })
    }

    return statsByPos
  }, {})
  return Object.values(fieldingStatsByPosition)
}

function makeModernFieldingStatFromLegacy(legacyStat: LegacyFieldingStat): FieldingStat | undefined {
  // filter out "PH" fielding stats here which used to occur when position/xchecks were loosely checked/observed
  if (legacyStat.pos === 'PH') return undefined
  return {
    game: 'TOT',
    player: legacyStat.player,
    team: legacyStat.team,
    pos: legacyStat.pos,
    xCheckCount: legacyStat.xch,
    hit: legacyStat.xhit,
    error: legacyStat.error,
    stolenBaseCheckCount: legacyStat.sbc,
    stolenBaseCount: legacyStat.sbc - legacyStat.csc,
    caughtStealingCount: legacyStat.csc,
    passedBallCount: legacyStat.pb,
    winProbabilityAdded: 0,
    weightedFieldingPercent: weightedFieldingPercentage({ xChecks: legacyStat.xch, hits: legacyStat.xhit, errors: legacyStat.error }),
    caughtStealingPercent: caughtStealingPercentage({ stolenBaseAttempts: legacyStat.sbc, caughtStealingCount: legacyStat.csc })
  }
}

function normalizeFieldingStat(rawStat: FieldingStatRaw): FieldingStat {
  return {
    game: rawStat.game,
    player: rawStat.player,
    team: rawStat.team,
    pos: rawStat.pos,
    xCheckCount: rawStat['x-ch'],
    hit: rawStat.hit,
    error: rawStat.error,
    stolenBaseCheckCount: rawStat['sb-ch'],
    stolenBaseCount: rawStat.sb,
    caughtStealingCount: rawStat.cs,
    passedBallCount: rawStat.pb,
    winProbabilityAdded: rawStat.wpa,
    weightedFieldingPercent: rawStat['wf%'],
    caughtStealingPercent: rawStat['cs%'] === '' ? NaN : rawStat['cs%']
  }
}

export function weightedFieldingPercentage(stat: { xChecks: number, hits: number, errors: number }): number {
  if (stat.xChecks === 0) return NaN
  return (stat.xChecks - stat.errors * 0.5 - stat.hits * 0.75) / stat.xChecks
}

export function caughtStealingPercentage(stat: { stolenBaseAttempts: number, caughtStealingCount: number }): number {
  if (stat.stolenBaseAttempts === 0) return NaN
  return stat.caughtStealingCount / stat.stolenBaseAttempts
}
