import {
  lambdasFromElo,
  scoreMatrix,
  outcomeProbs,
  topScorelines,
  type ModelParams,
  type OutcomeProbs,
  type Scoreline,
} from './poisson';

/** The ratings.json payload (also the runtime model state after live re-rating). */
export interface RatingsModel {
  asOf: string;
  params: ModelParams;
  ratings: Record<string, number>;
}

export const DEFAULT_ELO = 1500;

export function ratingOf(model: RatingsModel, team: string): number {
  return model.ratings[team] ?? DEFAULT_ELO;
}

export interface MatchPrediction {
  home: string;
  away: string;
  eloHome: number;
  eloAway: number;
  lambdaHome: number;
  lambdaAway: number;
  probs: OutcomeProbs;
  /** Most likely scorelines, descending. */
  scorelines: Scoreline[];
}

/**
 * Win/draw/loss + likely scores for one match. `homeAdv` is the Elo bump for the
 * home slot (0 neutral; use hostAdvantage() for host nations at home).
 */
export function predictMatch(
  home: string,
  away: string,
  model: RatingsModel,
  homeAdv = 0,
): MatchPrediction {
  const eloHome = ratingOf(model, home);
  const eloAway = ratingOf(model, away);
  const { lambdaHome, lambdaAway } = lambdasFromElo(eloHome, eloAway, model.params, homeAdv);
  const m = scoreMatrix(lambdaHome, lambdaAway, model.params.rho);
  return {
    home,
    away,
    eloHome,
    eloAway,
    lambdaHome,
    lambdaAway,
    probs: outcomeProbs(m),
    scorelines: topScorelines(m, 5),
  };
}

/** Probability the home side advances in a knockout (regulation result, draws
 *  resolved by an Elo-weighted shootout). */
export function knockoutAdvanceProb(p: OutcomeProbs, eloHome: number, eloAway: number): number {
  // Penalty edge: small, rating-tilted; mostly a coin flip.
  const shootoutHome = 1 / (1 + 10 ** ((eloAway - eloHome) / 800));
  return p.home + p.draw * shootoutHome;
}
