import type { Game, Team } from './apiResponseTypes'
import type { Player } from './playersService'
import { MODERN_STAT_ERA_START, SITE_URL, avg, obp, ops, slg, woba } 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 PitchingStat {
  player: Player
  team: Team
  game: Game | 'TOT'
  // stats below
  tbf: number
  outs: number
  games: number
  gs: number
  win: number
  loss: number
  hold: number
  save: number
  bsave: number
  ir: number
  ir_sc: number
  ab: number
  run: number
  e_run: number
  hits: number
  double: number
  triple: number
  hr: number
  bb: number
  so: number
  hbp: number
  sac: number
  ibb: number
  gidp: number
  sb: number
  cs: number
  bphr: number
  bpfo: number
  bp1b: number
  bplo: number
  wp: number
  balk: number
  wpa: number
  era: number
  whip: number
  avg: number
  obp: number
  slg: number
  ops: number
  woba: number
  kPer9: number
  bbPer9: number
  kPerBB: number
}

interface PitchingStatRaw {
  player: Player
  team: Team
  game: Game | 'TOT'
  // stats below
  tbf: number
  outs: number
  games: number
  gs: number
  win: number
  loss: number
  hold: number
  save: number
  bsave: number
  ir: number
  ir_sc: number
  ab: number
  run: number
  e_run: number
  hits: number
  double: number
  triple: number
  hr: number
  bb: number
  so: number
  hbp: number
  sac: number
  ibb: number
  gidp: number
  sb: number
  cs: number
  bphr: number
  bpfo: number
  bp1b: number
  bplo: number
  wp: number
  balk: number
  wpa: number
  era: number
  whip: number
  avg: number
  obp: number
  slg: number
  ops: number
  woba: number
  "k/9": number
  "bb/9": number
  "k/bb": number
}

interface LegacyPitchingStat {
  player: Player
  team: Team
  ip: number,
  hit: number,
  run: number,
  erun: number,
  so: number,
  bb: number,
  hbp: number,
  wp: number,
  balk: number,
  hr: number,
  ir: number,
  irs: number,
  gs: number,
  games: number,
  win: number,
  loss: number,
  hold: number,
  sv: number,
  bsv: number
}

export async function fetchPitchingStatsBySeasonAndPlayerId(seasonNumber: number, playerId: number, isRegularSeason: boolean): Promise<PitchingStat | undefined> {
  // different endpoint for pre-modern stats (/plays) era
  if (seasonNumber < MODERN_STAT_ERA_START) {
    return await fetchLegacyPitchingStatsBySeasonAndPlayerId(seasonNumber, playerId, isRegularSeason)
  }

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

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

  if (pitchingStatsResponse.count === 0) return undefined

  if (pitchingStatsResponse.count > 1) {
    throw new Error('pitchingStatsService.fetchPitchingStatsBySeasonAndPlayerId - Expected one stat line for season/player, return contained many')
  }

  return normalizePitchingStat(pitchingStatsResponse.stats[0])
}

export async function fetchPitchingStatsBySeasonAndTeamId(seasonNumber: number, teamId: number, isRegularSeason: boolean): Promise<PitchingStat[]> {
  const response = await fetch(`${SITE_URL}/api/v3/plays/pitching?season=${seasonNumber}&team_id=${teamId}&group_by=playerteam&s_type=${isRegularSeason ? 'regular' : 'post'}`)

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

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

  return pitchingStatsResponse.stats.map(normalizePitchingStat)
}

async function fetchLegacyPitchingStatsBySeasonAndPlayerId(seasonNumber: number, playerId: number, isRegularSeason: boolean): Promise<PitchingStat | undefined> {
  const response = await fetch(`${SITE_URL}/api/v3/pitchingstats/totals?season=${seasonNumber}&player_id=${playerId}&s_type=${isRegularSeason ? 'regular' : 'post'}`)

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

  if (legacyPitchingStatsResponse.count === 0) return undefined

  if (legacyPitchingStatsResponse.count > 1) {
    throw new Error('pitchingStatsService.fetchLegacyPitchingStatsBySeasonAndPlayerId - Expected one stat line for season/player, return contained many')
  }

  return makeModernPitchingStatFromLegacy(legacyPitchingStatsResponse.stats[0])
}

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

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

  if (pitchingStatsResponse.count > 4) {
    throw new Error(`pitchingStatsService.fetchPitchingStatsForLastFourBySeasonAndPlayerId - Expected at most 4 games, return contained ${pitchingStatsResponse.count}`)
  }

  return pitchingStatsResponse.stats.map(normalizePitchingStat)
}

export function aggregatePitchingStats(pitchingStats: PitchingStat[]): PitchingStat {
  let totalStat: PitchingStat = {
    player: pitchingStats[0].player,
    team: pitchingStats[0].team,
    game: 'TOT',
    // stats below
    tbf: 0,
    outs: 0,
    games: 0,
    gs: 0,
    win: 0,
    loss: 0,
    hold: 0,
    save: 0,
    bsave: 0,
    ir: 0,
    ir_sc: 0,
    ab: 0,
    run: 0,
    e_run: 0,
    hits: 0,
    double: 0,
    triple: 0,
    hr: 0,
    bb: 0,
    so: 0,
    hbp: 0,
    sac: 0,
    ibb: 0,
    gidp: 0,
    sb: 0,
    cs: 0,
    bphr: 0,
    bpfo: 0,
    bp1b: 0,
    bplo: 0,
    wp: 0,
    balk: 0,
    wpa: 0,
    era: 0,
    whip: 0,
    avg: 0,
    obp: 0,
    slg: 0,
    ops: 0,
    woba: 0,
    kPer9: 0,
    bbPer9: 0,
    kPerBB: 0
  }

  pitchingStats.forEach(stat => {
    totalStat.tbf += stat.tbf,
      totalStat.outs += stat.outs,
      totalStat.games += stat.games,
      totalStat.gs += stat.gs,
      totalStat.win += stat.win,
      totalStat.loss += stat.loss,
      totalStat.hold += stat.hold,
      totalStat.save += stat.save,
      totalStat.bsave += stat.bsave,
      totalStat.ir += stat.ir,
      totalStat.ir_sc += stat.ir_sc,
      totalStat.ab += stat.ab,
      totalStat.run += stat.run,
      totalStat.e_run += stat.e_run,
      totalStat.hits += stat.hits,
      totalStat.double += stat.double,
      totalStat.triple += stat.triple,
      totalStat.hr += stat.hr,
      totalStat.bb += stat.bb,
      totalStat.so += stat.so,
      totalStat.hbp += stat.hbp,
      totalStat.sac += stat.sac,
      totalStat.ibb += stat.ibb,
      totalStat.gidp += stat.gidp,
      totalStat.sb += stat.sb,
      totalStat.cs += stat.cs,
      totalStat.bphr += stat.bphr,
      totalStat.bpfo += stat.bpfo,
      totalStat.bp1b += stat.bp1b,
      totalStat.bplo += stat.bplo,
      totalStat.wp += stat.wp,
      totalStat.balk += stat.balk,
      totalStat.wpa += stat.wpa
  })

  // a shim since batting stat has "hit" and pitching stat has "hits" and I want to share functions
  // TODO will need to figure out if there are pas for pitching or if there is an easy way to back into it
  const totalStatWithHit = { ...totalStat, hit: totalStat.hits }

  return {
    ...totalStat,
    era: eraFromOuts(totalStat),
    whip: whipFromOuts(totalStat),
    kPer9: kPer9FromOuts(totalStat),
    bbPer9: bbPer9FromOuts(totalStat),
    kPerBB: kPerBB(totalStat),
    avg: avg(totalStatWithHit),
    obp: 0, //obp({pa: totalStat.}),
    slg: slg(totalStatWithHit),
    ops: 0, //ops(totalStat),
    woba: woba(totalStatWithHit)
  }
}

function makeModernPitchingStatFromLegacy(legacyStat: LegacyPitchingStat): PitchingStat {
  return {
    player: legacyStat.player,
    team: legacyStat.team,
    game: 'TOT',
    // stats below
    outs: Math.round(legacyStat.ip * 3),
    games: legacyStat.games,
    gs: legacyStat.gs,
    win: legacyStat.win,
    loss: legacyStat.loss,
    hold: legacyStat.hold,
    save: legacyStat.sv,
    bsave: legacyStat.bsv,
    ir: legacyStat.ir,
    ir_sc: legacyStat.irs,
    run: legacyStat.run,
    e_run: legacyStat.erun,
    hits: legacyStat.hit,
    hr: legacyStat.hr,
    bb: legacyStat.bb,
    so: legacyStat.so,
    hbp: legacyStat.hbp,
    wp: legacyStat.wp,
    balk: legacyStat.balk,

    // calculated stats
    era: era({ ip: legacyStat.ip, e_run: legacyStat.erun }),
    whip: whip({ ip: legacyStat.ip, bb: legacyStat.bb, hits: legacyStat.hit }),
    kPer9: kPer9(legacyStat),
    bbPer9: bbPer9(legacyStat),
    kPerBB: kPerBB(legacyStat),

    // no easy way to get below values from legacy stats afaik
    tbf: 0,
    ab: 0,
    double: 0,
    triple: 0,
    sac: 0,
    ibb: 0,
    gidp: 0,
    sb: 0,
    cs: 0,
    bphr: 0,
    bpfo: 0,
    bp1b: 0,
    bplo: 0,
    wpa: 0,
    avg: 0,
    obp: 0,
    slg: 0,
    ops: 0,
    woba: 0,
  }
}

function normalizePitchingStat(rawStat: PitchingStatRaw): PitchingStat {
  return {
    player: rawStat.player,
    team: rawStat.team,
    game: rawStat.game,
    // stats below
    tbf: rawStat.tbf,
    outs: rawStat.outs,
    games: rawStat.games,
    gs: rawStat.gs,
    win: rawStat.win,
    loss: rawStat.loss,
    hold: rawStat.hold,
    save: rawStat.save,
    bsave: rawStat.bsave,
    ir: rawStat.ir,
    ir_sc: rawStat.ir_sc,
    ab: rawStat.ab,
    run: rawStat.run,
    e_run: rawStat.e_run,
    hits: rawStat.hits,
    double: rawStat.double,
    triple: rawStat.triple,
    hr: rawStat.hr,
    bb: rawStat.bb,
    so: rawStat.so,
    hbp: rawStat.hbp,
    sac: rawStat.sac,
    ibb: rawStat.ibb,
    gidp: rawStat.gidp,
    sb: rawStat.sb,
    cs: rawStat.cs,
    bphr: rawStat.bphr,
    bpfo: rawStat.bpfo,
    bp1b: rawStat.bp1b,
    bplo: rawStat.bplo,
    wp: rawStat.wp,
    balk: rawStat.balk,
    wpa: rawStat.wpa,
    era: rawStat.era,
    whip: rawStat.whip,
    avg: rawStat.avg,
    obp: rawStat.obp,
    slg: rawStat.slg,
    ops: rawStat.ops,
    woba: rawStat.woba,
    kPer9: rawStat["k/9"],
    bbPer9: rawStat["bb/9"],
    kPerBB: rawStat["k/bb"]
  }
}

function era(stat: { e_run: number, ip: number }): number {
  if (stat.ip === 0) return stat.e_run > 0 ? NaN : 0

  return stat.e_run * 9 / stat.ip
}

function eraFromOuts(stat: { e_run: number, outs: number }): number {
  if (stat.outs === 0) return stat.e_run > 0 ? NaN : 0

  return stat.e_run * 27 / stat.outs
}

function whip(stat: { bb: number, hits: number, ip: number }): number {
  if (stat.ip === 0) return stat.bb + stat.hits > 0 ? NaN : 0

  return (stat.bb + stat.hits) / stat.ip
}

function whipFromOuts(stat: { bb: number, hits: number, outs: number }): number {
  if (stat.outs === 0) return stat.bb + stat.hits > 0 ? NaN : 0

  return (stat.bb + stat.hits) * 3 / stat.outs
}

function kPer9(stat: { so: number, ip: number }): number {
  if (stat.ip === 0) return 0

  return stat.so * 9 / stat.ip
}

function kPer9FromOuts(stat: { so: number, outs: number }): number {
  if (stat.outs === 0) return 0

  return stat.so * 27 / stat.outs
}

function bbPer9(stat: { bb: number, ip: number }): number {
  if (stat.ip === 0) return stat.bb > 0 ? NaN : 0

  return stat.bb * 9 / stat.ip
}

function bbPer9FromOuts(stat: { bb: number, outs: number }): number {
  if (stat.outs === 0) return stat.bb > 0 ? NaN : 0

  return stat.bb * 27 / stat.outs
}

function kPerBB(stat: { so: number, bb: number }): number {
  if (stat.bb === 0) return 0

  return stat.so / stat.bb
}

