// ═══════════════════════════════════════════════════════════════
// Phase E — Datenagenten (Match-Enrichment)
// FATIGUE + LINEUP. REFEREE bewusst weggelassen — siehe NOTE unten.
// Diese Module reichern match-Objekte mit zusätzlichen Daten an,
// BEVOR die LLM-Agenten laufen. Sie sind selbst keine LLMs.
// ═══════════════════════════════════════════════════════════════

// ─── FATIGUE ──────────────────────────────────────────────────
// Berechnet Tage seit letztem Spiel + Mittwochs-Europa-Belastung
// Datenquelle: ESPN team-scoreboard (für vergangene Spiele)
const fatigueAgent = {
  cache: {},

  async getTeamRecent(teamId, leagueCode) {
    if (!teamId) return null;
    const ck = `fatigue_${teamId}`;
    if (this.cache[ck]) return this.cache[ck];
    try {
      const espnLeague = espnAPI.LEAGUES[leagueCode] || 'eng.1';
      const r = await fetch(`https://site.api.espn.com/apis/site/v2/sports/soccer/${espnLeague}/teams/${teamId}/schedule`);
      if (!r.ok) throw new Error(`HTTP ${r.status}`);
      const d = await r.json();
      const events = d.events || d.team?.events || [];
      const past = events.filter(e => {
        const dt = new Date(e.date);
        return dt < new Date() && e.status?.type?.completed;
      }).sort((a,b) => new Date(b.date) - new Date(a.date)).slice(0, 5);
      this.cache[ck] = past;
      return past;
    } catch(e) { console.warn(`Fatigue ${teamId}:`, e.message); return null; }
  },

  // Liefert {daysRest, midweekEuro, gameLoad} für ein Team
  computeStats(recentEvents, kickoff) {
    if (!recentEvents || recentEvents.length === 0) return null;
    const last = recentEvents[0];
    const lastDate = new Date(last.date);
    const kickoffDate = new Date(kickoff || Date.now());
    const daysRest = Math.max(0, Math.floor((kickoffDate - lastDate) / (1000*60*60*24)));
    // Midweek-Europa: letztes Spiel war Di/Mi/Do UND in UCL/UEL/ECL
    const lastDay = lastDate.getDay(); // 0=So, 2=Di, 3=Mi, 4=Do
    const lastLeague = (last.league?.abbreviation || last.competitions?.[0]?.competition?.abbreviation || '').toUpperCase();
    const isEuro = ['UCL','UEL','UECL','ECL','CL','EL'].some(c => lastLeague.includes(c));
    const midweekEuro = isEuro && [2,3,4].includes(lastDay);
    // Game-Load: Anzahl Spiele in letzten 14 Tagen
    const cutoff = Date.now() - 14*24*60*60*1000;
    const gameLoad = recentEvents.filter(e => new Date(e.date).getTime() > cutoff).length;
    return { daysRest, midweekEuro, gameLoad, lastDate: lastDate.toISOString() };
  },

  async enrich(match) {
    if (!match || match.fatigue) return; // already enriched
    try {
      const espnId = (match.id || '').replace('espn_', '');
      // Hole Team-IDs aus dem ESPN-Event (falls verfügbar via espnData)
      const homeTeamId = match.espnData?.homeTeamId || match.homeTeamId;
      const awayTeamId = match.espnData?.awayTeamId || match.awayTeamId;
      // Fallback: skip wenn keine IDs (selten — ESPN-Matches haben sie meist)
      if (!homeTeamId && !awayTeamId) {
        match.fatigue = { home: null, away: null, status: 'no_team_ids' };
        return;
      }
      const [homeRecent, awayRecent] = await Promise.all([
        homeTeamId ? this.getTeamRecent(homeTeamId, match.leagueKey) : null,
        awayTeamId ? this.getTeamRecent(awayTeamId, match.leagueKey) : null,
      ]);
      const kickoff = match.espnData?.date || new Date().toISOString();
      match.fatigue = {
        home: this.computeStats(homeRecent, kickoff),
        away: this.computeStats(awayRecent, kickoff),
        status: 'ok',
      };
    } catch(e) {
      console.warn('Fatigue enrich failed:', e.message);
      match.fatigue = { home: null, away: null, status: 'error' };
    }
  },
};

// ─── LINEUP ───────────────────────────────────────────────────
// Versucht ESPN's Match-Detail-Endpoint für Aufstellungen
// Returns: { home: [players], away: [players], confirmed: bool }
const lineupAgent = {
  cache: {},

  async fetchLineup(eventId, leagueCode) {
    if (!eventId) return null;
    const ck = `lineup_${eventId}`;
    if (this.cache[ck]) return this.cache[ck];
    try {
      const espnLeague = espnAPI.LEAGUES[leagueCode] || 'eng.1';
      const r = await fetch(`https://site.api.espn.com/apis/site/v2/sports/soccer/${espnLeague}/summary?event=${eventId}`);
      if (!r.ok) throw new Error(`HTTP ${r.status}`);
      const d = await r.json();
      const rosters = d.rosters || [];
      if (!rosters.length) return null;
      const result = { home: [], away: [], confirmed: false, formation: {} };
      rosters.forEach(roster => {
        const side = roster.homeAway === 'home' ? 'home' : 'away';
        const starters = (roster.roster || []).filter(p => p.starter).map(p => ({
          name: p.athlete?.displayName || p.athlete?.name,
          position: p.position?.abbreviation,
          number: p.jersey,
        }));
        result[side] = starters;
        if (starters.length === 11) result.confirmed = true;
        if (roster.formation) result.formation[side] = roster.formation;
      });
      this.cache[ck] = result;
      return result;
    } catch(e) { console.warn(`Lineup ${eventId}:`, e.message); return null; }
  },

  async enrich(match) {
    if (!match || match.lineup) return;
    try {
      const eventId = (match.id || '').replace('espn_', '');
      if (!eventId || !match.id?.startsWith('espn_')) {
        match.lineup = { home: [], away: [], confirmed: false, status: 'no_espn_id' };
        return;
      }
      const lu = await this.fetchLineup(eventId, match.leagueKey);
      match.lineup = lu || { home: [], away: [], confirmed: false, status: 'unavailable' };
    } catch(e) {
      console.warn('Lineup enrich failed:', e.message);
      match.lineup = { home: [], away: [], confirmed: false, status: 'error' };
    }
  },
};

// ─── REFEREE-Agent (FBref-Scrape via /api/fbref/) ────────────
const refereeAgent = {
  cache: {}, // pro Liga
  async getLeagueRefs(leagueCode) {
    if (!leagueCode) return null;
    if (this.cache[leagueCode]) return this.cache[leagueCode];
    try {
      const r = await fetch(`/api/fbref/referees/${leagueCode}`);
      if (!r.ok) throw new Error(`HTTP ${r.status}`);
      const d = await r.json();
      this.cache[leagueCode] = d.referees || [];
      return this.cache[leagueCode];
    } catch(e) { console.warn(`Referee ${leagueCode}:`, e.message); return null; }
  },
  // Schiri-Name fuzzy match in Liga-Tabelle
  findRef(name, refs) {
    if (!name || !refs?.length) return null;
    const n = name.toLowerCase();
    return refs.find(r => {
      const rn = (r.referee || r.player || '').toLowerCase();
      return rn === n || rn.includes(n.split(' ').pop()) || n.includes(rn.split(' ').pop());
    });
  },
  async enrich(match) {
    if (!match || match.referee) return;
    // Schiri-Name aus ESPN-Event
    const refName = match.espnData?.referee || match.refereeName;
    if (!refName) {
      match.referee = { status: 'no_name' };
      return;
    }
    const refs = await this.getLeagueRefs(match.leagueKey);
    if (!refs?.length) {
      match.referee = { status: 'no_data', name: refName };
      return;
    }
    const r = this.findRef(refName, refs);
    if (!r) {
      match.referee = { status: 'not_found', name: refName };
      return;
    }
    match.referee = {
      status: 'ok',
      name: refName,
      games: parseInt(r.games)||0,
      yellow_cards: parseFloat(r.cards_yellow)||0,
      red_cards: parseFloat(r.cards_red)||0,
      cards_per_match: r.games > 0 ? +(((parseFloat(r.cards_yellow)||0)+(parseFloat(r.cards_red)||0))/parseFloat(r.games)).toFixed(2) : null,
      penalties: parseInt(r.pens_made)||0,
      raw: r,
    };
  },
};

// ─── MOTIVATION ───────────────────────────────────────────────
// Klassifiziert Saison-Kontext: Title-Race, Klassenerhalt, Europa-Jagd, Dead-Rubber
// Quelle: ESPN-Standings. Liefert score 0-100 + Kategorie pro Team.
const motivationAgent = {
  cache: {},

  async getStandings(leagueCode) {
    if (!leagueCode) return null;
    if (this.cache[leagueCode]) return this.cache[leagueCode];
    try {
      const espnLeague = espnAPI.LEAGUES[leagueCode];
      if (!espnLeague) return null;
      const r = await fetch(`https://site.api.espn.com/apis/site/v2/sports/soccer/${espnLeague}/standings`);
      if (!r.ok) throw new Error(`HTTP ${r.status}`);
      const d = await r.json();
      // ESPN-Format: children[0].standings.entries[]
      const entries = d.children?.[0]?.standings?.entries || d.standings?.entries || [];
      const teams = entries.map((e, i) => {
        const stats = Object.fromEntries((e.stats || []).map(s => [s.name || s.type, parseFloat(s.value)]));
        return {
          id: e.team?.id,
          name: e.team?.displayName || e.team?.name,
          rank: stats.rank || i + 1,
          points: stats.points || 0,
          games: stats.gamesPlayed || stats.games || 0,
          wins: stats.wins || 0,
          draws: stats.ties || stats.draws || 0,
          losses: stats.losses || 0,
          goalDiff: stats.pointDifferential || stats.goalDifference || 0,
        };
      }).filter(t => t.id);
      const data = { teams, total: teams.length, fetchedAt: Date.now() };
      this.cache[leagueCode] = data;
      return data;
    } catch(e) { console.warn(`Standings ${leagueCode}:`, e.message); return null; }
  },

  // Score 0-100 + Kategorie für eine Team-Position
  classify(team, league, leagueCode) {
    if (!team || !league) return { score: 50, category: 'unknown', reason: 'Keine Tabellendaten' };
    const total = league.total;
    const rank = team.rank;
    const totalGamesEstimate = leagueCode === 'PL' ? 38 : leagueCode === 'BL' ? 34 : leagueCode === 'SA' || leagueCode === 'LL' ? 38 : leagueCode === 'L1' ? 34 : 36;
    const gamesLeft = Math.max(0, totalGamesEstimate - team.games);
    const seasonProgress = team.games / totalGamesEstimate; // 0-1
    const isLateSeason = seasonProgress > 0.75;

    // Title race: top 3 mit dichtem Punktabstand
    if (rank <= 3) {
      const top1 = league.teams[0];
      const gap = top1.points - team.points;
      if (rank === 1 && gap === 0 && isLateSeason && gamesLeft <= 5) {
        // Champion praktisch sicher (großer Vorsprung) — nur wenn ranks 2-3 weit weg
        const next = league.teams[1];
        const lead = team.points - next.points;
        if (lead >= gamesLeft * 3 + 3) return { score: 35, category: 'champion_secure', reason: `Titel sicher (+${lead} Pkt, ${gamesLeft} Spiele)` };
      }
      if (gap <= gamesLeft * 3 || rank > 1) {
        return { score: 95, category: 'title_race', reason: `Titel-Kampf (Rang ${rank}, ${gap} Pkt zur Spitze, ${gamesLeft} Spiele)` };
      }
    }

    // Relegation battle: bottom 3 (top-Ligen) bzw. bottom 4 (kleinere)
    const relegationZone = ['BL'].includes(leagueCode) ? 3 : ['PL','LL','SA','L1'].includes(leagueCode) ? 3 : 3;
    const safetyMargin = relegationZone + 2;
    if (rank > total - safetyMargin) {
      const safe = league.teams[total - relegationZone - 1];
      const gap = safe ? Math.abs(team.points - safe.points) : 0;
      const inDanger = rank > total - relegationZone;
      return {
        score: inDanger ? 95 : 80,
        category: inDanger ? 'relegation_fight' : 'relegation_threat',
        reason: `${inDanger?'Abstiegszone':'Abstiegsgefährdet'} (Rang ${rank}/${total}, ${gap} Pkt zum sicheren Ufer)`,
      };
    }

    // Europa-Jagd (Plätze 4-7 in Top-5-Ligen)
    if (['PL','BL','LL','SA','L1'].includes(leagueCode) && rank >= 4 && rank <= 7) {
      return { score: 78, category: 'european_chase', reason: `Europa-Plätze in Reichweite (Rang ${rank})` };
    }

    // Dead rubber: Mid-Table ohne Stakes, späte Saison
    if (isLateSeason && rank > 7 && rank < total - safetyMargin) {
      return { score: 45, category: 'dead_rubber', reason: `Mid-Table ohne Stakes (Rang ${rank}, ${gamesLeft} Spiele, kein Abstieg/Europa)` };
    }

    // Default: normale Liga-Motivation
    return { score: 65, category: 'midtable', reason: `Tabellenplatz ${rank}/${total}` };
  },

  async enrich(match) {
    if (!match || match.motivation) return;
    try {
      const standings = await this.getStandings(match.leagueKey);
      if (!standings) {
        match.motivation = { status: 'no_standings' };
        return;
      }
      const findTeam = (id, name) => standings.teams.find(t =>
        (id && t.id === id) ||
        (name && (t.name?.toLowerCase().split(' ')[0] === name.toLowerCase().split(' ')[0]))
      );
      const homeTeam = findTeam(match.homeTeamId, match.home);
      const awayTeam = findTeam(match.awayTeamId, match.away);
      if (!homeTeam || !awayTeam) {
        match.motivation = { status: 'team_not_found' };
        return;
      }
      const homeMot = this.classify(homeTeam, standings, match.leagueKey);
      const awayMot = this.classify(awayTeam, standings, match.leagueKey);
      // Derby-Override: aus existing match.importance ablesen
      const isDerby = (match.importance || '').toUpperCase().includes('DERBY')
                  || (match.importance || '').toUpperCase().includes('CLÁSICO')
                  || (match.importance || '').toUpperCase().includes('TOPPER');
      if (isDerby) {
        homeMot.score = Math.max(homeMot.score, 92);
        awayMot.score = Math.max(awayMot.score, 92);
        homeMot.category = 'derby_' + homeMot.category;
        awayMot.category = 'derby_' + awayMot.category;
        homeMot.reason += ' + DERBY (alle Form-Logik off)';
        awayMot.reason += ' + DERBY (alle Form-Logik off)';
      }
      match.motivation = {
        status: 'ok',
        home: { ...homeMot, rank: homeTeam.rank, points: homeTeam.points, games: homeTeam.games },
        away: { ...awayMot, rank: awayTeam.rank, points: awayTeam.points, games: awayTeam.games },
        gap: homeMot.score - awayMot.score, // positiv = Heim-Vorteil
      };
    } catch(e) {
      console.warn('Motivation enrich failed:', e.message);
      match.motivation = { status: 'error' };
    }
  },
};

// ─── TEAM-STATS (FBref Tiefenstats) ───────────────────────────
const teamStatsAgent = {
  cache: {},  // pro Liga: { teamName: { gp, xg, xga, xg90, xga90, gf, ga } }
  async getLeagueStats(leagueCode) {
    if (!leagueCode) return null;
    if (this.cache[leagueCode]) return this.cache[leagueCode];
    try {
      const r = await fetch(`/api/fbref/team-stats/${leagueCode}`);
      if (!r.ok) throw new Error(`HTTP ${r.status}`);
      const d = await r.json();
      const map = {};
      (d.teams || []).forEach(t => {
        const name = (t.team || t.squad || '').toLowerCase().replace(/\s+/g, ' ').trim();
        if (!name) return;
        map[name] = {
          gp: parseInt(t.games || t.matches_played) || 0,
          xg: parseFloat(t.xg_for || t.xg) || null,
          xga: parseFloat(t.xg_against || t.xga) || null,
          xg90: parseFloat(t.xg_for_per90 || t.xg_per90) || null,
          xga90: parseFloat(t.xg_against_per90 || t.xga_per90) || null,
          goals: parseInt(t.goals_for || t.goals) || null,
          goalsAgainst: parseInt(t.goals_against) || null,
        };
      });
      this.cache[leagueCode] = map;
      return map;
    } catch(e) { console.warn(`Team-Stats ${leagueCode}:`, e.message); return null; }
  },
  // Fuzzy team-Suche
  findTeam(stats, teamName) {
    if (!stats || !teamName) return null;
    const n = teamName.toLowerCase().replace(/\s+/g, ' ').trim();
    if (stats[n]) return stats[n];
    // Erste Wort
    const first = n.split(' ')[0];
    for (const [k, v] of Object.entries(stats)) {
      if (k.includes(first) || first.includes(k.split(' ')[0])) return v;
    }
    return null;
  },
  async enrich(match) {
    if (!match || match.teamStats) return;
    const stats = await this.getLeagueStats(match.leagueKey);
    if (!stats) { match.teamStats = { status: 'no_data' }; return; }
    const home = this.findTeam(stats, match.home);
    const away = this.findTeam(stats, match.away);
    if (!home && !away) { match.teamStats = { status: 'no_match' }; return; }
    match.teamStats = { status: 'ok', home, away };
    // Wenn FBref-xG vorhanden, überschreibe match.xG für Poisson-Update
    if (home?.xg90 && away?.xg90) {
      match.xG = { home: home.xg90, away: away.xg90 };
      match.xGA = { home: home.xga90, away: away.xga90 };
    }
  },
};

// ─── INJURY (Transfermarkt-Scraper) ───────────────────────────
// TM-Slug+ID-Mapping für die größten Vereine (vermeidet Such-Roundtrip)
const TM_TEAMS = {
  // Bundesliga
  'bayern münchen':           ['fc-bayern-munchen', '27'],
  'borussia dortmund':        ['borussia-dortmund', '16'],
  'rb leipzig':               ['rasenballsport-leipzig', '23826'],
  'bayer leverkusen':         ['bayer-04-leverkusen', '15'],
  'eintracht frankfurt':      ['eintracht-frankfurt', '24'],
  'vfb stuttgart':            ['vfb-stuttgart', '79'],
  'borussia mönchengladbach': ['borussia-monchengladbach', '18'],
  'vfl wolfsburg':            ['vfl-wolfsburg', '82'],
  'sc freiburg':              ['sc-freiburg', '60'],
  'tsg hoffenheim':           ['tsg-1899-hoffenheim', '533'],
  '1. fsv mainz 05':          ['1-fsv-mainz-05', '39'],
  'fc augsburg':              ['fc-augsburg', '167'],
  'union berlin':             ['1-fc-union-berlin', '89'],
  'werder bremen':            ['sv-werder-bremen', '86'],
  '1. fc heidenheim 1846':    ['1-fc-heidenheim-1846', '2036'],
  'fc st. pauli':             ['fc-st-pauli', '35'],
  'fc cologne':               ['1-fc-koln', '3'],
  '1. fc köln':               ['1-fc-koln', '3'],
  'holstein kiel':            ['holstein-kiel', '269'],
  // Premier League
  'manchester city':       ['manchester-city', '281'],
  'arsenal':               ['fc-arsenal', '11'],
  'liverpool':             ['fc-liverpool', '31'],
  'chelsea':               ['fc-chelsea', '631'],
  'manchester united':     ['manchester-united', '985'],
  'tottenham hotspur':     ['tottenham-hotspur', '148'],
  'newcastle united':      ['newcastle-united', '762'],
  'aston villa':           ['aston-villa', '405'],
  'west ham united':       ['west-ham-united', '379'],
  'brighton & hove albion':['brighton-amp-hove-albion', '1237'],
  'wolverhampton wanderers':['wolverhampton-wanderers', '543'],
  'crystal palace':        ['crystal-palace', '873'],
  'fulham':                ['fc-fulham', '931'],
  'everton':               ['fc-everton', '29'],
  'brentford':             ['fc-brentford', '1148'],
  'nottingham forest':     ['nottingham-forest', '703'],
  // La Liga
  'real madrid':           ['real-madrid', '418'],
  'fc barcelona':          ['fc-barcelona', '131'],
  'atletico madrid':       ['atletico-madrid', '13'],
  'atlético de madrid':    ['atletico-madrid', '13'],
  'athletic club':         ['athletic-bilbao', '621'],
  'real sociedad':         ['real-sociedad-san-sebastian', '681'],
  'villarreal':            ['fc-villarreal', '1050'],
  'real betis':            ['real-betis-balompie', '150'],
  // Serie A
  'inter milan':           ['inter-mailand', '46'],
  'juventus':              ['juventus-turin', '506'],
  'ac milan':              ['ac-mailand', '5'],
  'napoli':                ['ssc-neapel', '6195'],
  'roma':                  ['as-rom', '12'],
  'lazio':                 ['lazio-rom', '398'],
  'atalanta':              ['atalanta-bergamo', '800'],
  'fiorentina':            ['ac-florenz', '430'],
  // Ligue 1
  'paris saint-germain':   ['fc-paris-saint-germain', '583'],
  'olympique marseille':   ['olympique-marseille', '244'],
  'olympique lyon':        ['olympique-lyon', '1041'],
  'as monaco':             ['as-monaco', '162'],
  'lille osc':             ['osc-lille', '1082'],
};

const injuryAgent = {
  cache: {},
  resolve(teamName) {
    if (!teamName) return null;
    const k = teamName.toLowerCase().trim();
    if (TM_TEAMS[k]) return TM_TEAMS[k];
    // Fuzzy: erstes Wort matchen
    const first = k.split(' ')[0];
    for (const [name, info] of Object.entries(TM_TEAMS)) {
      if (name.startsWith(first) || name.includes(first)) return info;
    }
    return null;
  },
  async fetchInjuries(teamName) {
    const info = this.resolve(teamName);
    if (!info) return null;
    const [slug, id] = info;
    const ck = `inj_${slug}_${id}`;
    if (this.cache[ck]) return this.cache[ck];
    try {
      const r = await fetch(`/api/transfermarkt/injuries/${slug}/${id}`);
      if (!r.ok) throw new Error(`HTTP ${r.status}`);
      const d = await r.json();
      this.cache[ck] = d.injuries || [];
      return this.cache[ck];
    } catch(e) { console.warn(`Injury ${teamName}:`, e.message); return null; }
  },
  async enrich(match) {
    if (!match || match.injuries) return;
    const [home, away] = await Promise.all([
      this.fetchInjuries(match.home),
      this.fetchInjuries(match.away),
    ]);
    match.injuries = {
      home: home || [],
      away: away || [],
      status: (home || away) ? 'ok' : 'no_data',
    };
  },
};

// ─── HEAD-TO-HEAD ─────────────────────────────────────────────
// Holt vergangene Begegnungen aus ESPN /summary?event={id}
const h2hAgent = {
  cache: {},
  async fetchH2H(eventId, leagueCode) {
    const ck = `h2h_${eventId}`;
    if (this.cache[ck]) return this.cache[ck];
    try {
      const espnLeague = espnAPI.LEAGUES[leagueCode] || 'eng.1';
      const r = await fetch(`https://site.api.espn.com/apis/site/v2/sports/soccer/${espnLeague}/summary?event=${eventId}`);
      if (!r.ok) throw new Error(`HTTP ${r.status}`);
      const d = await r.json();
      // ESPN-Format variiert: headToHeadGames[].events ODER headToHead[].events
      const h2hSrc = d.headToHeadGames?.[0]?.events
                  || d.headToHead?.[0]?.events
                  || d.lastFiveGames?.flatMap(t => t.events || []).slice(0, 10)
                  || [];
      const teamIds = new Set();
      d.boxscore?.teams?.forEach(t => teamIds.add(String(t.team?.id)));
      const results = h2hSrc.slice(0, 5).map(ev => {
        const comp = ev.competitions?.[0] || ev;
        const home = comp.competitors?.find(c => c.homeAway === 'home');
        const away = comp.competitors?.find(c => c.homeAway === 'away');
        if (!home || !away) return null;
        return {
          date: (ev.date || ev.gameDate || '').split('T')[0],
          home: home.team?.abbreviation || home.team?.shortDisplayName || home.team?.displayName || '?',
          away: away.team?.abbreviation || away.team?.shortDisplayName || away.team?.displayName || '?',
          score: `${home.score ?? 0}-${away.score ?? 0}`,
          competition: ev.league?.abbreviation || ev.season?.year || '',
        };
      }).filter(Boolean);
      if (results.length === 0) { this.cache[ck] = null; return null; }
      let homeGoals = 0, awayGoals = 0, btts = 0, over = 0;
      results.forEach(r => {
        const [h, a] = r.score.split('-').map(n => parseInt(n, 10) || 0);
        homeGoals += h; awayGoals += a;
        if (h > 0 && a > 0) btts++;
        if (h + a > 2.5) over++;
      });
      const data = { results, homeGoals, awayGoals, bttsCount: btts, overCount: over };
      this.cache[ck] = data;
      return data;
    } catch(e) { console.warn(`H2H ${eventId}:`, e.message); return null; }
  },
  async enrich(match) {
    if (!match || match.h2hData) return;
    if (!match.id?.startsWith('espn_')) {
      match.h2hData = { results: [], status: 'no_espn_id' };
      return;
    }
    const eventId = match.id.replace('espn_', '');
    const data = await this.fetchH2H(eventId, match.leagueKey);
    match.h2hData = data || { results: [], status: 'no_data' };
  },
};

// ─── COMBINED ENRICHER ────────────────────────────────────────
async function enrichMatch(match) {
  if (!match) return;
  // Team-Stats müssen zuerst laufen damit xG für Poisson direkt verfügbar
  await teamStatsAgent.enrich(match);
  await Promise.allSettled([
    fatigueAgent.enrich(match),
    lineupAgent.enrich(match),
    refereeAgent.enrich(match),
    motivationAgent.enrich(match),
    h2hAgent.enrich(match),
    injuryAgent.enrich(match),
  ]);
  return match;
}

Object.assign(window, { fatigueAgent, lineupAgent, refereeAgent, motivationAgent, h2hAgent, injuryAgent, teamStatsAgent, enrichMatch });
