// Shared domain types — used by the client, the server, and the build scripts.

export type Stage = 'group' | 'r32' | 'r16' | 'qf' | 'sf' | 'third' | 'final';
export type MatchStatus = 'scheduled' | 'live' | 'finished';

/** One side of a fixture: a known team, or an unresolved knockout placeholder. */
export interface TeamSlot {
  /** Canonical team name when known, else null (unresolved knockout slot). */
  team: string | null;
  /** Raw openfootball placeholder: '2A' | 'W74' | 'L101' | '3A/B/C/D/F' | null. */
  placeholder: string | null;
  /** Display label: the team name, or a pretty placeholder ("Runner-up A"). */
  label: string;
}

export interface Fixture {
  /** Canonical 1..104 match number (group matches 1-72, knockout 73-104). */
  num: number;
  stage: Stage;
  /** 'A'..'L' for group stage, else null. */
  group: string | null;
  /** Human round label from the source ("Matchday 1", "Round of 32", …). */
  round: string;
  /** 1 | 2 | 3 — the team's nth group game, else null. */
  groupRound: number | null;
  /** Kickoff as an ISO 8601 UTC timestamp. */
  kickoff: string;
  venue: string;
  home: TeamSlot;
  away: TeamSlot;
  status: MatchStatus;
  homeScore: number | null;
  awayScore: number | null;
  /** Live match minute when status === 'live'. */
  minute: number | null;
}

export interface FixturesFile {
  season: string;
  generatedAt: string;
  groups: string[];
  venues: string[];
  fixtures: Fixture[];
}

export interface StandingRow {
  team: string;
  played: number;
  won: number;
  drawn: number;
  lost: number;
  gf: number;
  ga: number;
  gd: number;
  points: number;
  /** 1-based rank within the group after sorting. */
  rank: number;
}

/** What the server pushes to clients: fixtures with any live overlay + tables. */
export interface Snapshot {
  season: string;
  /** When the live layer last refreshed (ISO), or null if seed-only. */
  updatedAt: string | null;
  /** Which live source produced the current scores. */
  source: 'seed' | 'espn' | 'football-data' | 'sofascore';
  fixtures: Fixture[];
  /** Group letter → ranked standings. */
  tables: Record<string, StandingRow[]>;
}

// ---- model / predictions ----

/** Per-team tournament odds from the Monte Carlo simulation. */
export interface TeamOdds {
  team: string;
  winGroup: number;
  qualify: number;
  reachR16: number;
  reachQF: number;
  reachSF: number;
  reachFinal: number;
  champion: number;
}

export interface MatchProbs {
  home: number;
  draw: number;
  away: number;
}

/** A single per-match prediction (group + resolved knockout games). */
export interface MatchPrediction {
  num: number;
  home: string;
  away: string;
  probs: MatchProbs;
  lambdaHome: number;
  lambdaAway: number;
  topScore: { home: number; away: number; p: number };
}

/** A point on the championship-odds-over-time chart. */
export interface OddsHistoryPoint {
  t: string;
  label: string;
  top: { team: string; champion: number }[];
}

export interface ModelSnapshot {
  generatedAt: string;
  /** Date the underlying ratings reflect (advances as results re-rate teams). */
  asOf: string;
  iterations: number;
  matches: MatchPrediction[];
  odds: TeamOdds[];
  history: OddsHistoryPoint[];
}

// ---- match previews / team profiles ----

export interface H2HMeeting { date: string; home: string; away: string; homeScore: number; awayScore: number }
export interface H2HSummary {
  games: number;
  /** From THIS fixture's home/away perspective. */
  homeWins: number;
  awayWins: number;
  draws: number;
  homeGoals: number;
  awayGoals: number;
  last: H2HMeeting[];
}

export interface KeyPlayer { name: string; goals: number; pens: number }
export interface LineupPlayer { name: string; position: string | null; starter: boolean; number: number | null }

export interface MatchPreview {
  num: number;
  fixture: Fixture;
  venue: string | null;
  prediction: MatchPrediction | null;
  /** Recent results per side, most recent first ('W'|'D'|'L'). */
  form: { home: string[]; away: string[] };
  h2h: H2HSummary | null;
  lineups: { home: LineupPlayer[]; away: LineupPlayer[] } | null;
  keyPlayers: { home: KeyPlayer[]; away: KeyPlayer[] };
  /** Honest note on which data layers are populated yet. */
  dataNote: string;
  updatedAt: number | null;
}

export interface TeamProfile {
  team: string;
  elo: number;
  group: string | null;
  standing: StandingRow | null;
  fixtures: Fixture[];
  keyPlayers: KeyPlayer[];
  championOdds: number | null;
  qualifyOdds: number | null;
}

// ---- data-story viz (StatsBomb open data, a fixed historical match) ----

export interface VizShot {
  team: string;
  player: string;
  minute: number;
  /** StatsBomb pitch coords: x 0–120 (attacking toward 120), y 0–80. */
  x: number;
  y: number;
  xg: number;
  outcome: string;
  goal: boolean;
}

export interface XgPoint { minute: number; cum: number; }

export interface PassNode { player: string; x: number; y: number; passes: number; }
export interface PassEdge { a: string; b: string; count: number; }
export interface PassNetwork { players: PassNode[]; edges: PassEdge[]; }

export interface TeamTotals { xg: number; shots: number; goals: number; passes: number; passAccuracy: number; }

export interface StoryViz {
  matchId: number;
  competition: string;
  stage: string;
  date: string;
  home: string;
  away: string;
  homeScore: number;
  awayScore: number;
  /** Final result line, e.g. "Argentina won 4–2 on penalties". */
  result: string;
  pitch: { length: number; width: number };
  shots: VizShot[];
  goals: { team: string; player: string; minute: number; xg: number }[];
  xgRace: { home: XgPoint[]; away: XgPoint[] };
  passNetwork: { home: PassNetwork; away: PassNetwork };
  totals: { home: TeamTotals; away: TeamTotals };
}

// ---- WebSocket protocol ----
// The snapshot is small (~104 fixtures), so the server simply re-pushes the full
// snapshot on every change — no diffing, no client-side merge races. Predictions
// are heavier to compute, so they ride a separate message sent only when the set
// of finished results changes.

/** Server → client. */
export type ServerMessage =
  | { t: 'snapshot'; snapshot: Snapshot }
  | { t: 'predictions'; model: ModelSnapshot };
