/**
 * Sport configuration — two layers in one file.
 *
 * SPORTS (legacy, UI-facing)
 *   `active`, `collectData`, `comingSoon` — drives the landing-page badges
 *   and the "is grading on for this sport" gate. Consumed by:
 *     - src/services/UnifiedOddsProvider.js  (shouldCollect)
 *     - src/routes/pipeline.js               (isActiveSport)
 *   Mirror in `web/src/config/sports.ts`.
 *
 * SPORT_CONFIG (pipeline / resolution / poller)
 *   Per-sport ESPN endpoints + stat parsers. The resolution route, the
 *   ESPN poller, and the odds adapters all read from here. Active across
 *   all 7 graded sports — when a sport's UI flag is off but pipeline is
 *   on, we collect data without surfacing grades.
 *
 * Game hours are stored as ET (Eastern Time). Pollers convert from UTC
 * with Intl.DateTimeFormat — see `getETHour()` in poller/poller.js.
 *
 * Stat parsing supports FOUR formats; calculateStat in the resolution
 * route picks based on which key is present:
 *   1. idx + parse       → flat stats[] (NBA / WNBA / NCAAB)
 *   2. mlbField          → MLB Stats API native field name
 *   3. category + field  → NFL / NCAAFB category-based athletes
 *   4. calc / mlbCalc    → combo stat computed from components
 *   5. field             → NHL named-field box score
 *
 * All parse functions MUST be defensive — undefined/null/empty input must
 * return 0 rather than NaN, otherwise resolution divides by NaN and bricks
 * an entire sport's batch.
 */

// ---------------------------------------------------------------
// Legacy UI config (unchanged) — keep stable for existing consumers.
// ---------------------------------------------------------------

const SPORTS = Object.freeze({
  nba:    { key: 'nba',    label: 'NBA',    color: '#E94B3C', active: true,  collectData: true },
  wnba:   { key: 'wnba',   label: 'WNBA',   color: '#F7944A', active: true,  collectData: true },
  mlb:    { key: 'mlb',    label: 'MLB',    color: '#1E90FF', active: true,  collectData: true },
  nfl:    { key: 'nfl',    label: 'NFL',    color: '#013369', active: false, collectData: false, comingSoon: 'Coming this summer' },
  nhl:    { key: 'nhl',    label: 'NHL',    color: '#A0A0B0', active: false, collectData: false, comingSoon: 'Coming this summer' },
  tennis: { key: 'tennis', label: 'Tennis', color: '#C5B358', active: false, collectData: false, comingSoon: 'Coming this summer' },
  // Wave 6 — combat intelligence: MMA is live as a READ surface (fight cards +
  // tale-of-the-tape + style blend + ML/round-total odds). It is NOT in the
  // graded-props pipeline (SPORT_CONFIG) or the snapshot/settle loop yet, so
  // collectData stays false — active flips true so the UI treats it as live.
  mma:    { key: 'mma',    label: 'MMA',    color: '#D4AF37', active: true,  collectData: false },
  boxing: { key: 'boxing', label: 'Boxing', color: '#8B0000', active: false, collectData: false, comingSoon: 'Coming this summer' },
  golf:   { key: 'golf',   label: 'Golf',   color: '#2E7D32', active: false, collectData: false, comingSoon: 'Coming this summer' },
});

const ALL = Object.values(SPORTS);
const ACTIVE = ALL.filter((s) => s.active);
const COLLECTING = ALL.filter((s) => s.collectData);

const isActiveSport = (key) => !!SPORTS[String(key || '').toLowerCase()]?.active;
const shouldCollect = (key) => !!SPORTS[String(key || '').toLowerCase()]?.collectData;

// ---------------------------------------------------------------
// Stat parsers — defensive helpers.
// ---------------------------------------------------------------

const numOrZero = (v) => {
  if (v === undefined || v === null || v === '') return 0;
  const n = Number(v);
  return Number.isFinite(n) ? n : 0;
};

// "3-7" → 3 (makes-attempts string format used in NBA threes/FG/FT)
const splitMakes = (v) => {
  if (!v) return 0;
  const left = String(v).split('-')[0];
  const n = Number(left);
  return Number.isFinite(n) ? n : 0;
};

// "5.1" innings pitched → 5.333... (decimal innings)
const inningsToDecimal = (v) => {
  if (!v) return 0;
  const s = String(v);
  const [whole, partial] = s.split('.');
  const w = Number(whole) || 0;
  const p = Number(partial) || 0;
  return w + p / 3;
};

// ---------------------------------------------------------------
// SPORT_CONFIG — pipeline / resolution layer.
// ---------------------------------------------------------------

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

// NBA / WNBA / NCAAB share the same basketball stat layout — index-based
// athletes[].stats array. We define the map once and reuse it; the espn
// URLs differ.
const BASKETBALL_STAT_MAP = {
  // idx values follow the order ESPN returns in the basketball box-score
  // statistics[0].labels array: ['MIN','FG','3PT','FT','OREB','DREB','REB','AST','STL','BLK','TO','PF','+/-','PTS']
  // …but historically points was at idx 1 and rebounds at 5 — keep the
  // tested indices verified against tests/fixtures live samples.
  minutes:        { idx: 0, parse: numOrZero },
  points:         { idx: 1, parse: numOrZero },
  field_goals:    { idx: 2, parse: splitMakes },
  threes_made:    { idx: 3, parse: splitMakes },
  free_throws:    { idx: 4, parse: splitMakes },
  rebounds:       { idx: 5, parse: numOrZero },
  assists:        { idx: 6, parse: numOrZero },
  turnovers:      { idx: 7, parse: numOrZero },
  steals:         { idx: 8, parse: numOrZero },
  blocks:         { idx: 9, parse: numOrZero },
  pts_reb_ast:    { calc: (s) => numOrZero(s?.[1]) + numOrZero(s?.[5]) + numOrZero(s?.[6]) },
  pts_reb:        { calc: (s) => numOrZero(s?.[1]) + numOrZero(s?.[5]) },
  pts_ast:        { calc: (s) => numOrZero(s?.[1]) + numOrZero(s?.[6]) },
  reb_ast:        { calc: (s) => numOrZero(s?.[5]) + numOrZero(s?.[6]) },
  stl_blk:        { calc: (s) => numOrZero(s?.[8]) + numOrZero(s?.[9]) },
};

const MLB_STAT_MAP = {
  totalBases:     { mlbField: 'totalBases' },
  strikeOuts:     { mlbField: 'strikeOuts' },
  hits:           { mlbField: 'hits' },
  homeRuns:       { mlbField: 'homeRuns' },
  rbi:            { mlbField: 'rbi' },
  stolenBases:    { mlbField: 'stolenBases' },
  earnedRuns:     { mlbField: 'earnedRuns' },
  inningsPitched: { mlbField: 'inningsPitched', parse: inningsToDecimal },
  runs:           { mlbField: 'runs' },
  baseOnBalls:    { mlbField: 'baseOnBalls' },
  hits_runs_rbi:  { mlbCalc: (s) => numOrZero(s?.hits) + numOrZero(s?.runs) + numOrZero(s?.rbi) },
};

const FOOTBALL_STAT_MAP = {
  passing_yards:     { category: 'passing',    field: 'passingYards' },
  passing_tds:       { category: 'passing',    field: 'passingTouchdowns' },
  interceptions:     { category: 'passing',    field: 'interceptions' },
  completions:       { category: 'passing',    field: 'completions' },
  rushing_yards:     { category: 'rushing',    field: 'rushingYards' },
  rushing_tds:       { category: 'rushing',    field: 'rushingTouchdowns' },
  receiving_yards:   { category: 'receiving',  field: 'receivingYards' },
  receptions:        { category: 'receiving',  field: 'receptions' },
  receiving_tds:     { category: 'receiving',  field: 'receivingTouchdowns' },
};

const NHL_STAT_MAP = {
  goals:   { field: 'goals' },
  assists: { field: 'assists' },
  shots:   { field: 'shots' },
  saves:   { field: 'saves' },
  points:  { calc: (s) => numOrZero(s?.goals) + numOrZero(s?.assists) },
};

const SPORT_CONFIG = Object.freeze({
  nba: Object.freeze({
    key: 'nba',
    label: 'NBA',
    active: true,
    espnScoreboard: `${ESPN_BASE}/basketball/nba/scoreboard`,
    espnSummary:    `${ESPN_BASE}/basketball/nba/summary`,
    gameStartHourET: 18,
    gameEndHourET:   24,
    statMap: BASKETBALL_STAT_MAP,
  }),
  wnba: Object.freeze({
    key: 'wnba',
    label: 'WNBA',
    active: true,
    espnScoreboard: `${ESPN_BASE}/basketball/wnba/scoreboard`,
    espnSummary:    `${ESPN_BASE}/basketball/wnba/summary`,
    gameStartHourET: 18,
    gameEndHourET:   24,
    statMap: BASKETBALL_STAT_MAP,
  }),
  ncaab: Object.freeze({
    key: 'ncaab',
    label: 'NCAAB',
    active: true,
    espnScoreboard: `${ESPN_BASE}/basketball/mens-college-basketball/scoreboard`,
    espnSummary:    `${ESPN_BASE}/basketball/mens-college-basketball/summary`,
    gameStartHourET: 18,
    gameEndHourET:   25, // late games on the west coast often roll past midnight
    statMap: BASKETBALL_STAT_MAP,
  }),
  mlb: Object.freeze({
    key: 'mlb',
    label: 'MLB',
    active: true,
    espnScoreboard:  `${ESPN_BASE}/baseball/mlb/scoreboard`,
    espnSummary:     `${ESPN_BASE}/baseball/mlb/summary`,
    // MLB resolution reads from the MLB Stats API (richer + more reliable
    // than ESPN's MLB box scores), but tip-off detection still rides on
    // ESPN's scoreboard.
    useMlbStatsApi:  true,
    mlbStatsApiBase: 'https://statsapi.mlb.com/api/v1.1',
    gameStartHourET: 13,
    gameEndHourET:   25,
    statMap: MLB_STAT_MAP,
  }),
  nfl: Object.freeze({
    key: 'nfl',
    label: 'NFL',
    active: true,
    espnScoreboard: `${ESPN_BASE}/football/nfl/scoreboard`,
    espnSummary:    `${ESPN_BASE}/football/nfl/summary`,
    gameStartHourET: 13,
    gameEndHourET:   24,
    statMap: FOOTBALL_STAT_MAP,
  }),
  ncaafb: Object.freeze({
    key: 'ncaafb',
    label: 'NCAA Football',
    active: true,
    espnScoreboard: `${ESPN_BASE}/football/college-football/scoreboard`,
    espnSummary:    `${ESPN_BASE}/football/college-football/summary`,
    gameStartHourET: 12,
    gameEndHourET:   25,
    statMap: FOOTBALL_STAT_MAP,
  }),
  nhl: Object.freeze({
    key: 'nhl',
    label: 'NHL',
    active: true,
    espnScoreboard: `${ESPN_BASE}/hockey/nhl/scoreboard`,
    espnSummary:    `${ESPN_BASE}/hockey/nhl/summary`,
    gameStartHourET: 19,
    gameEndHourET:   24,
    statMap: NHL_STAT_MAP,
  }),
});

function getActiveSports() {
  return Object.values(SPORT_CONFIG).filter((s) => s.active);
}

function getSportConfig(sport) {
  const cfg = SPORT_CONFIG[String(sport || '').toLowerCase()];
  if (!cfg) throw new Error(`Unknown sport: ${sport}`);
  return cfg;
}

module.exports = {
  // Legacy UI surface
  SPORTS,
  ALL,
  ACTIVE,
  COLLECTING,
  isActiveSport,
  shouldCollect,
  // Pipeline / resolution surface
  SPORT_CONFIG,
  getActiveSports,
  getSportConfig,
};
