/**
 * Dean Oliver's Four Factors + Pace Model for NBA Forecasts
 *
 * Intelligence sources:
 *   - "Mathletics" (Wayne Winston) — Four Factors, eFG%, lineup superiority
 *   - "The Logic of Sports Betting" (Ed Miller) — Pace dynamics, Q4 scoring skew
 *   - "Sports Analytics" (Benjamin Alamar) — PER, per-possession efficiency
 *   - "Sharp Sports Betting" (Stanford Wong) — Poisson distribution for props
 *
 * Data source: ESPN public API (team-level season stats, fetched live with 1h cache)
 *
 * Four Factors (Dean Oliver):
 *   1. eFG% = (FGM + 0.5 * 3PM) / FGA
 *   2. TO% = TOV / (FGA + 0.44 * FTA + TOV)
 *   3. OREB% = OREB / (OREB + Opp_DREB)  — estimated from league avg
 *   4. FTR = FTM / FGA (Free Throw Rate)
 *
 * Pace: Possessions ≈ FGA - OREB + TOV + 0.44 * FTA
 */

import { espnFetch } from './espn-roster';

// ---------------------------------------------------------------------------
// Types
// ---------------------------------------------------------------------------

export interface NBATeamStats {
  teamId: string;
  teamName: string;
  teamShort: string;
  gamesPlayed: number;
  // Offensive raw stats (per game)
  ppg: number;          // Points per game
  fgm: number;          // FG made per game
  fga: number;          // FG attempted per game
  fgPct: number;        // FG%
  tpm: number;          // 3PT made per game
  tpa: number;          // 3PT attempted per game
  tpPct: number;        // 3PT%
  ftm: number;          // FT made per game
  fta: number;          // FT attempted per game
  ftPct: number;        // FT%
  oreb: number;         // OREB per game
  dreb: number;         // DREB per game
  reb: number;          // Total REB per game
  ast: number;          // AST per game
  tov: number;          // TOV per game
  stl: number;          // STL per game
  blk: number;          // BLK per game
  pf: number;           // PF per game
}

export interface FourFactors {
  efg: number;          // Effective FG% = (FGM + 0.5*3PM) / FGA
  toPct: number;        // Turnover% = TOV / (FGA + 0.44*FTA + TOV)
  orebPct: number;      // Offensive Reb% (estimated)
  ftr: number;          // Free Throw Rate = FTM / FGA
}

export interface TeamProfile {
  stats: NBATeamStats;
  offense: FourFactors;
  pace: number;          // Estimated possessions per game
  offRtg: number;        // Offensive rating (points per 100 possessions)
  // Prop-relevant derived metrics
  scoringEfficiency: number;  // Points per FGA (includes FTs)
  threePointRate: number;     // 3PA / FGA — reliance on three-pointers
  astTovRatio: number;        // AST/TOV
  rebRate: number;            // Total REB per game
}

export interface FourFactorsMatchup {
  home: TeamProfile;
  away: TeamProfile;
  // Matchup-level computations
  expectedPace: number;       // Average of both teams' pace
  expectedTotal: number;      // Projected game total from pace + efficiency
  efgEdge: number;            // Home eFG% - Away eFG%
  efgEdgeWins: number;        // eFG edge × 350 (≈3.5 wins per 0.01)
  paceImpact: string;         // "Fast" / "Slow" / "Average" / "Mismatch"
  propAdjustments: PropAdjustment[];
}

export interface PropAdjustment {
  factor: string;
  direction: 'boost' | 'suppress';
  magnitude: string;     // "slight" / "moderate" / "strong"
  explanation: string;
  affectedProps: string[];
}

// ---------------------------------------------------------------------------
// ESPN Data Fetcher + Cache
// ---------------------------------------------------------------------------

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

interface CachedTeamData {
  teams: Map<string, TeamProfile>;
  leagueAvg: {
    pace: number;
    ppg: number;
    efg: number;
    oreb: number;
    dreb: number;
  };
  fetchedAt: number;
}

let cache: CachedTeamData | null = null;
const CACHE_TTL = 60 * 60 * 1000; // 1 hour

function parseStatValue(stats: any[], abbrev: string, perGame: boolean = true): number {
  // ESPN returns stats in an array; we need to find by abbreviation
  // Some stats appear multiple times (total + per-game). We want per-game when specified.
  const matches = stats.filter((s: any) => s.abbreviation === abbrev);
  if (matches.length === 0) return 0;

  if (perGame && matches.length > 1) {
    // Per-game version usually has "Per Game" in displayName or is a decimal
    const pgMatch = matches.find((m: any) =>
      (m.displayName || '').includes('Per Game') ||
      (m.displayName || '').includes('Average') ||
      (typeof m.value === 'number' && m.value < 200)
    );
    return pgMatch ? Number(pgMatch.value) || 0 : Number(matches[0].value) || 0;
  }

  return Number(matches[0].value) || 0;
}

function parseTeamStats(teamId: string, teamName: string, teamShort: string, data: any): NBATeamStats | null {
  const categories = data?.results?.stats?.categories;
  if (!categories) return null;

  // Flatten all stats
  const allStats: any[] = [];
  for (const cat of categories) {
    allStats.push(...(cat.stats || []));
  }

  const gp = parseStatValue(allStats, 'GP', false);
  if (gp === 0) return null;

  return {
    teamId,
    teamName,
    teamShort,
    gamesPlayed: gp,
    ppg: parseStatValue(allStats, 'PTS'),
    fgm: parseStatValue(allStats, 'FGM'),
    fga: parseStatValue(allStats, 'FGA'),
    fgPct: parseStatValue(allStats, 'FG%'),
    tpm: parseStatValue(allStats, '3PM'),
    tpa: parseStatValue(allStats, '3PA'),
    tpPct: parseStatValue(allStats, '3P%'),
    ftm: parseStatValue(allStats, 'FTM'),
    fta: parseStatValue(allStats, 'FTA'),
    ftPct: parseStatValue(allStats, 'FT%'),
    oreb: parseStatValue(allStats, 'OR'),
    dreb: parseStatValue(allStats, 'DR'),
    reb: parseStatValue(allStats, 'REB'),
    ast: parseStatValue(allStats, 'AST'),
    tov: parseStatValue(allStats, 'TO'),
    stl: parseStatValue(allStats, 'STL'),
    blk: parseStatValue(allStats, 'BLK'),
    pf: parseStatValue(allStats, 'PF'),
  };
}

function computeFourFactors(s: NBATeamStats): FourFactors {
  const efg = s.fga > 0 ? (s.fgm + 0.5 * s.tpm) / s.fga : 0;
  const possessionEstimate = s.fga + 0.44 * s.fta + s.tov;
  const toPct = possessionEstimate > 0 ? s.tov / possessionEstimate : 0;
  // OREB% estimated: team OREB / (team OREB + league avg DREB)
  // Will be refined with actual league data in buildProfile
  const orebPct = (s.oreb + 33) > 0 ? s.oreb / (s.oreb + 33) : 0;
  const ftr = s.fga > 0 ? s.ftm / s.fga : 0;

  return { efg, toPct, orebPct, ftr };
}

function computePace(s: NBATeamStats): number {
  // Possessions ≈ FGA - OREB + TOV + 0.44 * FTA
  return s.fga - s.oreb + s.tov + 0.44 * s.fta;
}

function buildProfile(stats: NBATeamStats, leagueAvgDreb: number): TeamProfile {
  const offense = computeFourFactors(stats);
  // Refine OREB% with actual league DREB
  offense.orebPct = (stats.oreb + leagueAvgDreb) > 0
    ? stats.oreb / (stats.oreb + leagueAvgDreb) : 0;

  const pace = computePace(stats);
  const offRtg = pace > 0 ? (stats.ppg / pace) * 100 : 0;

  return {
    stats,
    offense,
    pace,
    offRtg,
    scoringEfficiency: stats.fga > 0 ? stats.ppg / stats.fga : 0,
    threePointRate: stats.fga > 0 ? stats.tpa / stats.fga : 0,
    astTovRatio: stats.tov > 0 ? stats.ast / stats.tov : 0,
    rebRate: stats.reb,
  };
}

// ---------------------------------------------------------------------------
// Fetch all 30 NBA teams
// ---------------------------------------------------------------------------

export async function fetchAllNBATeams(): Promise<CachedTeamData> {
  if (cache && Date.now() - cache.fetchedAt < CACHE_TTL) {
    return cache;
  }

  console.log('[four-factors] Fetching NBA team stats from ESPN...');

  // Get team list
  const teamsData = await espnFetch(`${ESPN_BASE}/teams?limit=50`);
  const rawTeams = teamsData?.sports?.[0]?.leagues?.[0]?.teams ?? [];

  const teams = new Map<string, TeamProfile>();
  let totalPace = 0;
  let totalPpg = 0;
  let totalEfg = 0;
  let totalOreb = 0;
  let totalDreb = 0;
  let teamCount = 0;

  // Fetch stats for each team (with rate limiting)
  for (let i = 0; i < rawTeams.length; i++) {
    const t = rawTeams[i].team;
    const id = String(t.id);
    const short = t.abbreviation || '';
    const name = t.displayName || t.name || '';

    if (i > 0) await new Promise(r => setTimeout(r, 80));

    try {
      const statsData = await espnFetch(`${ESPN_BASE}/teams/${id}/statistics`);
      const parsed = parseTeamStats(id, name, short, statsData);
      if (parsed) {
        totalOreb += parsed.oreb;
        totalDreb += parsed.dreb;
        teamCount++;
      }
    } catch (err: any) {
      console.warn(`[four-factors] Failed ${short}: ${err.message}`);
    }
  }

  // Now compute league average DREB for OREB% calculation
  const leagueAvgDreb = teamCount > 0 ? totalDreb / teamCount : 33;

  // Second pass: build profiles with league context
  for (let i = 0; i < rawTeams.length; i++) {
    const t = rawTeams[i].team;
    const id = String(t.id);
    const short = t.abbreviation || '';
    const name = t.displayName || t.name || '';

    if (i > 0) await new Promise(r => setTimeout(r, 80));

    try {
      const statsData = await espnFetch(`${ESPN_BASE}/teams/${id}/statistics`);
      const parsed = parseTeamStats(id, name, short, statsData);
      if (parsed) {
        const profile = buildProfile(parsed, leagueAvgDreb);
        teams.set(short, profile);
        totalPace += profile.pace;
        totalPpg += parsed.ppg;
        totalEfg += profile.offense.efg;
      }
    } catch (err: any) {
      // Already warned in first pass
    }
  }

  const leagueAvg = {
    pace: teamCount > 0 ? totalPace / teamCount : 98,
    ppg: teamCount > 0 ? totalPpg / teamCount : 112,
    efg: teamCount > 0 ? totalEfg / teamCount : 0.540,
    oreb: teamCount > 0 ? totalOreb / teamCount : 10.5,
    dreb: leagueAvgDreb,
  };

  console.log(`[four-factors] Loaded ${teams.size} NBA teams | Avg pace: ${leagueAvg.pace.toFixed(1)} | Avg PPG: ${leagueAvg.ppg.toFixed(1)} | Avg eFG: ${(leagueAvg.efg * 100).toFixed(1)}%`);

  cache = { teams, leagueAvg, fetchedAt: Date.now() };
  return cache;
}

// Optimization: single-pass fetch for just 2 teams
async function fetchTwoTeams(homeShort: string, awayShort: string): Promise<{ home: TeamProfile | null; away: TeamProfile | null; leagueAvgDreb: number }> {
  // If cache is warm, use it
  if (cache && Date.now() - cache.fetchedAt < CACHE_TTL) {
    return {
      home: cache.teams.get(homeShort) || null,
      away: cache.teams.get(awayShort) || null,
      leagueAvgDreb: cache.leagueAvg.dreb,
    };
  }

  // Fetch just the two teams we need + estimate league DREB
  const teamsData = await espnFetch(`${ESPN_BASE}/teams?limit=50`);
  const rawTeams = teamsData?.sports?.[0]?.leagues?.[0]?.teams ?? [];
  const leagueAvgDreb = 33; // Reasonable NBA default

  const targets = rawTeams.filter((t: any) =>
    [homeShort, awayShort].includes(t.team.abbreviation)
  );

  let home: TeamProfile | null = null;
  let away: TeamProfile | null = null;

  for (const entry of targets) {
    const t = entry.team;
    const short = t.abbreviation || '';
    try {
      const statsData = await espnFetch(`${ESPN_BASE}/teams/${t.id}/statistics`);
      const parsed = parseTeamStats(String(t.id), t.displayName || '', short, statsData);
      if (parsed) {
        const profile = buildProfile(parsed, leagueAvgDreb);
        if (short === homeShort) home = profile;
        if (short === awayShort) away = profile;
      }
    } catch (err: any) {
      console.warn(`[four-factors] Failed ${short}: ${err.message}`);
    }
  }

  return { home, away, leagueAvgDreb };
}

// ---------------------------------------------------------------------------
// Matchup Analysis
// ---------------------------------------------------------------------------

export async function getFourFactorsMatchup(
  homeShort: string,
  awayShort: string,
): Promise<FourFactorsMatchup | null> {
  const ht = homeShort.toUpperCase();
  const at = awayShort.toUpperCase();

  const { home, away } = await fetchTwoTeams(ht, at);
  if (!home || !away) return null;

  // Expected pace = average of both teams (pace correlates strongly in matchups)
  const expectedPace = (home.pace + away.pace) / 2;

  // Expected total: each team's offensive rating scaled by expected pace
  // Total ≈ (home_offRtg + away_offRtg) * expectedPace / 100
  const expectedTotal = (home.offRtg + away.offRtg) * expectedPace / 100;

  // eFG edge
  const efgEdge = home.offense.efg - away.offense.efg;
  // Per Mathletics: 0.01 eFG differential ≈ 3.5 wins → approximate game impact
  const efgEdgeWins = efgEdge * 350;

  // Pace classification
  const avgPace = 98; // Approximate league average
  let paceImpact = 'Average';
  const paceDiff = Math.abs(home.pace - away.pace);
  if (expectedPace > avgPace + 4) paceImpact = 'Fast';
  else if (expectedPace < avgPace - 4) paceImpact = 'Slow';
  if (paceDiff > 6) paceImpact = 'Mismatch — ' + (home.pace > away.pace ? `${home.stats.teamShort} pushes pace` : `${away.stats.teamShort} pushes pace`);

  // Generate prop adjustments based on analytics
  const propAdjustments = generatePropAdjustments(home, away, expectedPace, avgPace);

  return {
    home, away,
    expectedPace, expectedTotal,
    efgEdge, efgEdgeWins,
    paceImpact,
    propAdjustments,
  };
}

function generatePropAdjustments(
  home: TeamProfile,
  away: TeamProfile,
  expectedPace: number,
  leagueAvgPace: number,
): PropAdjustment[] {
  const adjustments: PropAdjustment[] = [];

  // 1. Pace impact on all props
  const paceRatio = expectedPace / leagueAvgPace;
  if (paceRatio > 1.04) {
    adjustments.push({
      factor: 'Fast pace matchup',
      direction: 'boost',
      magnitude: paceRatio > 1.08 ? 'strong' : 'moderate',
      explanation: `Expected ${expectedPace.toFixed(0)} possessions (${((paceRatio - 1) * 100).toFixed(0)}% above league avg). More possessions = more stat opportunities.`,
      affectedProps: ['points', 'rebounds', 'assists', 'threes'],
    });
  } else if (paceRatio < 0.96) {
    adjustments.push({
      factor: 'Slow pace matchup',
      direction: 'suppress',
      magnitude: paceRatio < 0.92 ? 'strong' : 'moderate',
      explanation: `Expected ${expectedPace.toFixed(0)} possessions (${((1 - paceRatio) * 100).toFixed(0)}% below league avg). Fewer possessions = fewer stat opportunities.`,
      affectedProps: ['points', 'rebounds', 'assists', 'threes'],
    });
  }

  // 2. Turnover-prone teams create extra possessions for opponents
  const highToTeam = home.offense.toPct > away.offense.toPct ? home : away;
  const lowToTeam = home.offense.toPct > away.offense.toPct ? away : home;
  if (highToTeam.offense.toPct > 0.14) {
    adjustments.push({
      factor: `${highToTeam.stats.teamShort} turnover-prone (${(highToTeam.offense.toPct * 100).toFixed(1)}% TO rate)`,
      direction: 'boost',
      magnitude: highToTeam.offense.toPct > 0.16 ? 'moderate' : 'slight',
      explanation: `${lowToTeam.stats.teamShort} gets extra possessions from turnovers → boost their scoring props. Also boost ${lowToTeam.stats.teamShort} steals props.`,
      affectedProps: ['points (opponent)', 'steals (opponent)'],
    });
  }

  // 3. Offensive rebounding creates second-chance points
  const orebTeam = home.offense.orebPct > away.offense.orebPct ? home : away;
  if (orebTeam.stats.oreb > 11) {
    adjustments.push({
      factor: `${orebTeam.stats.teamShort} offensive rebounding (${orebTeam.stats.oreb.toFixed(1)} OREB/g)`,
      direction: 'boost',
      magnitude: orebTeam.stats.oreb > 13 ? 'moderate' : 'slight',
      explanation: `More offensive rebounds = more shot attempts and putback points. Boost big man rebound and points props.`,
      affectedProps: ['rebounds', 'points (bigs)'],
    });
  }

  // 4. Free throw rate — high FTR teams slow pace but get to the line
  const ftrTeam = home.offense.ftr > away.offense.ftr ? home : away;
  if (ftrTeam.offense.ftr > 0.22) {
    adjustments.push({
      factor: `${ftrTeam.stats.teamShort} gets to the line (FTR ${(ftrTeam.offense.ftr * 100).toFixed(1)}%)`,
      direction: 'boost',
      magnitude: ftrTeam.offense.ftr > 0.26 ? 'moderate' : 'slight',
      explanation: `High free throw rate inflates points but slows game pace. Boost points props for ${ftrTeam.stats.teamShort} players who draw fouls. May also boost opponent foul props.`,
      affectedProps: ['points', 'fouls (opponent)'],
    });
  }

  // 5. Three-point reliance affects scoring distribution
  for (const team of [home, away]) {
    if (team.threePointRate > 0.45) {
      adjustments.push({
        factor: `${team.stats.teamShort} three-heavy offense (${(team.threePointRate * 100).toFixed(0)}% of FGA)`,
        direction: 'boost',
        magnitude: team.threePointRate > 0.50 ? 'moderate' : 'slight',
        explanation: `High 3PA rate means more variance in scoring. Boost threes props for shooters, but points distribution is streakier.`,
        affectedProps: ['threes'],
      });
    }
  }

  // 6. Assist rate indicates ball movement
  for (const team of [home, away]) {
    if (team.astTovRatio > 2.3) {
      adjustments.push({
        factor: `${team.stats.teamShort} elite ball movement (${team.astTovRatio.toFixed(1)} AST/TO)`,
        direction: 'boost',
        magnitude: team.astTovRatio > 2.6 ? 'moderate' : 'slight',
        explanation: `High assist rate means more players contribute to scoring. Boost assists props for primary playmakers.`,
        affectedProps: ['assists'],
      });
    }
  }

  return adjustments;
}

// ---------------------------------------------------------------------------
// Prompt Formatter
// ---------------------------------------------------------------------------

function f(v: number, d: number = 1): string {
  return isNaN(v) ? 'N/A' : v.toFixed(d);
}

function pct(v: number): string {
  return isNaN(v) ? 'N/A' : (v * 100).toFixed(1) + '%';
}

export function formatFourFactorsForPrompt(data: FourFactorsMatchup): string {
  const h = data.home;
  const a = data.away;

  let s = '\n--- FOUR FACTORS + PACE MODEL (NBA — Dean Oliver / Ed Miller) ---\n';

  // Four Factors comparison
  s += '\nFOUR FACTORS (offensive efficiency metrics — low correlation, each reveals a different edge):\n';
  s += `Metric               | Home (${h.stats.teamShort})      | Away (${a.stats.teamShort})      | Edge\n`;
  s += '-'.repeat(75) + '\n';

  // eFG%
  const efgEdge = h.offense.efg - a.offense.efg;
  s += `eFG%                 | ${pct(h.offense.efg).padEnd(14)} | ${pct(a.offense.efg).padEnd(14)} | ${efgEdge > 0 ? 'Home' : 'Away'} +${(Math.abs(efgEdge) * 100).toFixed(1)}pp\n`;

  // TO%
  const toEdge = a.offense.toPct - h.offense.toPct; // Lower is better
  s += `Turnover%            | ${pct(h.offense.toPct).padEnd(14)} | ${pct(a.offense.toPct).padEnd(14)} | ${toEdge > 0 ? 'Home' : 'Away'} takes better care\n`;

  // OREB%
  const orebEdge = h.offense.orebPct - a.offense.orebPct;
  s += `OREB%                | ${pct(h.offense.orebPct).padEnd(14)} | ${pct(a.offense.orebPct).padEnd(14)} | ${orebEdge > 0 ? 'Home' : 'Away'} +${(Math.abs(orebEdge) * 100).toFixed(1)}pp\n`;

  // FTR
  const ftrEdge = h.offense.ftr - a.offense.ftr;
  s += `Free Throw Rate      | ${pct(h.offense.ftr).padEnd(14)} | ${pct(a.offense.ftr).padEnd(14)} | ${ftrEdge > 0 ? 'Home' : 'Away'} gets to line more\n`;

  // eFG impact
  s += `\neFG IMPACT: ${Math.abs(efgEdge * 100).toFixed(1)}pp eFG% differential ≈ ${f(Math.abs(data.efgEdgeWins), 1)} win-shares impact (per Dean Oliver: 0.01 eFG diff ≈ 3.5 wins).\n`;
  s += `eFG% explains 71% of NBA win variation — this is the MOST IMPORTANT factor.\n`;

  // Pace & Scoring
  s += `\nPACE & SCORING MODEL:\n`;
  s += `  ${h.stats.teamShort} pace: ${f(h.pace, 1)} poss/game | Off rating: ${f(h.offRtg, 1)} pts/100poss\n`;
  s += `  ${a.stats.teamShort} pace: ${f(a.pace, 1)} poss/game | Off rating: ${f(a.offRtg, 1)} pts/100poss\n`;
  s += `  Expected game pace: ${f(data.expectedPace, 1)} possessions (${data.paceImpact})\n`;
  s += `  Pace-projected total: ${f(data.expectedTotal, 1)} points\n`;
  s += `  USE THIS to anchor your over/under projection. Market total significantly above/below ${f(data.expectedTotal, 0)} = potential edge.\n`;

  // Team profiles
  s += `\nTEAM OFFENSIVE PROFILES:\n`;
  for (const team of [h, a]) {
    const t = team.stats;
    s += `  ${t.teamShort}: ${f(t.ppg)} PPG | FG ${pct(t.fgPct / 100)} | 3P ${pct(t.tpPct / 100)} (${f(t.tpa)} att/g) | FT ${pct(t.ftPct / 100)} | ${f(t.ast)} AST | ${f(t.tov)} TOV | ${f(t.oreb)} OREB\n`;
  }

  // Prop adjustments
  if (data.propAdjustments.length > 0) {
    s += `\nPLAYER PROP ADJUSTMENTS (from Four Factors + Pace analysis):\n`;
    for (const adj of data.propAdjustments) {
      const arrow = adj.direction === 'boost' ? '↑' : '↓';
      s += `  ${arrow} ${adj.magnitude.toUpperCase()}: ${adj.factor}\n`;
      s += `    ${adj.explanation}\n`;
    }

    s += `\nAPPLY THESE ADJUSTMENTS when evaluating player prop lines:\n`;
    s += `- Fast pace → lean OVER on scoring/assist props for both teams\n`;
    s += `- Slow pace → lean UNDER, especially for bench players who lose minutes\n`;
    s += `- High TO% team → their players' props are less reliable; opponent steals props get boost\n`;
    s += `- High OREB% → boost rebound props for bigs (centers, PF)\n`;
    s += `- High FTR → boost points for players who draw fouls, even if shooting % is low\n`;
  }

  // Ed Miller insights
  s += `\nED MILLER PACE DYNAMICS (from "The Logic of Sports Betting"):\n`;
  s += `- In blowouts, Q4 scoring drops as leading team slows pace. If this game projects lopsided, more points come in first half.\n`;
  s += `- Per-minute player rates stay stable, but total stat output is GATED BY PACE. Adjust all props by pace ratio.\n`;
  s += `- If expected pace is ${f(data.expectedPace / 98 * 100 - 100)}% vs average, adjust prop expectations proportionally.\n`;

  s += '--- END FOUR FACTORS ---\n';
  return s;
}
