// ═══════════════════════════════════════════════════════════════
// Elo-Modul — Zweiter Quant-Anker neben Poisson
// Klassisches Elo mit MOV-Adjustment (FiveThirtyEight-Stil light)
// State persistiert in localStorage. Bootstrap aus ESPN-Historie.
// ═══════════════════════════════════════════════════════════════

const elo = {
  KEY: 'fai_elo_v1',
  DEFAULT: 1500,
  K: 20,           // Lernrate
  HOME_ADV: 65,    // Heimvorteil in Elo-Punkten (~7.4% gewinnchance)
  bootstrapped: false,

  load() {
    try { return JSON.parse(localStorage.getItem(this.KEY) || '{}'); }
    catch { return {}; }
  },
  save(state) {
    try { localStorage.setItem(this.KEY, JSON.stringify(state)); } catch {}
  },

  get(teamId) {
    if (!teamId) return this.DEFAULT;
    const state = this.load();
    return state[teamId] != null ? state[teamId] : this.DEFAULT;
  },
  set(teamId, rating) {
    const state = this.load();
    state[teamId] = Math.round(rating);
    this.save(state);
  },

  // Erwartungswert für Heim aus rating-Differenz inkl. Heimvorteil
  expected(homeR, awayR) {
    return 1 / (1 + Math.pow(10, (awayR - (homeR + this.HOME_ADV)) / 400));
  },

  // Update nach Spielende. result: 1 = Heimsieg, 0.5 = Unent., 0 = Auswärts
  update(homeId, awayId, homeGoals, awayGoals) {
    if (!homeId || !awayId) return;
    const homeR = this.get(homeId);
    const awayR = this.get(awayId);
    const exp = this.expected(homeR, awayR);
    const result = homeGoals > awayGoals ? 1 : homeGoals < awayGoals ? 0 : 0.5;
    // MOV-Multiplier (Margin of Victory): klare Siege zählen mehr
    const diff = Math.abs(homeGoals - awayGoals);
    const mov = Math.log(diff + 1) * (2.2 / ((homeR - awayR) * (result === 1 ? 1 : -1) * 0.001 + 2.2));
    const change = this.K * mov * (result - exp);
    this.set(homeId, homeR + change);
    this.set(awayId, awayR - change);
    return { homeNew: homeR + change, awayNew: awayR - change, expected: exp, change };
  },

  // Vorhersage als Wahrscheinlichkeiten {home, draw, away}
  // Draw-Anteil aus empirischer Soccer-Verteilung (~ 27% bei 50/50 Ratings)
  predict(homeId, awayId) {
    const homeR = this.get(homeId);
    const awayR = this.get(awayId);
    const exp = this.expected(homeR, awayR);
    // Draw-Modell: nähert sich 0.27 bei exp=0.5, Richtung 0.05 bei exp Extremen
    const draw = 0.27 - 0.5 * Math.pow(exp - 0.5, 2);
    const remaining = 1 - draw;
    // exp ist effektiv P(home win | non-draw) → skalieren
    const home = exp * remaining;
    const away = (1 - exp) * remaining;
    return {
      home: +home.toFixed(3),
      draw: +draw.toFixed(3),
      away: +away.toFixed(3),
      ratings: { home: Math.round(homeR), away: Math.round(awayR) },
    };
  },

  // Bootstrap: aktuelle Saisons-Tabelle als groben Initial-Estimate
  // Fallback, falls keine Match-Historie verfügbar
  seedFromMatch(match, prediction) {
    if (!match.homeTeamId || !match.awayTeamId) return;
    const state = this.load();
    if (state[match.homeTeamId] && state[match.awayTeamId]) return;
    // Aus Quoten implizite Probas → Elo-Differenz
    if (match.odds?.home && match.odds?.away) {
      const ph = 1 / match.odds.home;
      const pa = 1 / match.odds.away;
      const total = ph + pa + (match.odds.draw ? 1/match.odds.draw : 0);
      const expHome = ph / total;
      // Reverse-Elo: rDiff = 400 * log10((1-exp)/exp) - HOME_ADV
      const rDiff = -400 * Math.log10((1 - expHome) / Math.max(0.01, expHome)) - this.HOME_ADV;
      const homeR = state[match.homeTeamId] ?? (this.DEFAULT + rDiff/2);
      const awayR = state[match.awayTeamId] ?? (this.DEFAULT - rDiff/2);
      this.set(match.homeTeamId, homeR);
      this.set(match.awayTeamId, awayR);
    }
  },

  // Jeden bekannten Match ausm liveScores-Cache initialisieren
  seedFromMatches(matches) {
    if (!matches) return;
    matches.forEach(m => this.seedFromMatch(m));
  },

  reset() { localStorage.removeItem(this.KEY); },
  count() { return Object.keys(this.load()).length; },
};

Object.assign(window, { elo });
