import { memberToCanonicalTtBucket } from '../utils/ttInternalStatsBuckets.js';

/**
 * Statistik-Werte für interne Einzel-Turniere (Gruppenphase + K.-o.-Runde).
 *
 * Gruppe: Platzierung als Prozent relativ zur Feldgröße N (alle mit Platzierung > 0),
 *          Formel (N − pos) / (N − 1) × 100 für N ≥ 2 (1. = 100 %, Letzter = 0 %),
 *          bei N = 1: 100 %. Nur Vereinsmitglieder werden gewertet; N zählt alle Platzierten.
 * K.-o.: Wer die K.-o.-Runde erreicht: höchster Gruppen-Prozentwert dieser Klasse + 1,
 *        danach +1 pro gewonnenes K.-o.-Spiel (wie bisher, jetzt auf %-Skala).
 * Alters-/Geschlechtsfilter: pro Spieler aus Vereinsmitglied Geburtsdatum + Geschlecht (nicht aus Turnierklassen-Name).
 */

export function parseWinnerFromMatch(match) {
  if (!match || !match.isFinished) return { winnerId: null, loserId: null };
  if (String(match.result || '').toUpperCase() === 'BYE') {
    const wid = match.player1Id || match.player2Id;
    const lid = wid === match.player1Id ? match.player2Id : match.player1Id;
    return { winnerId: wid || null, loserId: lid || null };
  }
  const results = match.tournamentResults;
  if (Array.isArray(results) && results.length > 0) {
    let w1 = 0;
    let w2 = 0;
    for (const r of results) {
      const p1 = Number(r.pointsPlayer1);
      const p2 = Number(r.pointsPlayer2);
      if (Number.isFinite(p1) && Number.isFinite(p2)) {
        if (p1 > p2) w1 += 1;
        else if (p2 > p1) w2 += 1;
      }
    }
    if (w1 !== w2) {
      return w1 > w2
        ? { winnerId: match.player1Id, loserId: match.player2Id }
        : { winnerId: match.player2Id, loserId: match.player1Id };
    }
  }
  if (typeof match.result === 'string') {
    const tokens = match.result.match(/-?\d+\s*:\s*-?\d+/g);
    if (tokens && tokens.length > 0) {
      const last = tokens[tokens.length - 1];
      const parts = last.split(':');
      const a = Number((parts[0] || '').trim());
      const b = Number((parts[1] || '').trim());
      if (Number.isFinite(a) && Number.isFinite(b) && a !== b) {
        return a > b
          ? { winnerId: match.player1Id, loserId: match.player2Id }
          : { winnerId: match.player2Id, loserId: match.player1Id };
      }
    }
  }
  return { winnerId: null, loserId: null };
}

/**
 * @param {Array<{ position: number, id: number }>} rankings – zu wertende Spieler; id = Turnier-Mitglied-ID
 * @param {number} nInGroup – Anzahl Teilnehmer in der Gruppe mit Platzierung > 0 (inkl. Externe)
 * @returns {Map<number, number>} tournamentMemberId -> Prozent 0–100 (Gleitkomma)
 */
export function groupPercentFromRankings(rankings, nInGroup) {
  const map = new Map();
  const N = Number(nInGroup);
  if (!rankings || rankings.length === 0 || !Number.isFinite(N) || N < 1) return map;

  if (N === 1) {
    for (const r of rankings) {
      const id = r.id;
      if (id == null) continue;
      map.set(Number(id), 100);
    }
    return map;
  }

  for (const r of rankings) {
    const pos = Number(r.position);
    if (!Number.isFinite(pos) || pos < 1) continue;
    const id = r.id;
    if (id == null) continue;
    const raw = ((N - pos) / (N - 1)) * 100;
    const pct = Math.max(0, Math.min(100, raw));
    map.set(Number(id), pct);
  }
  return map;
}

/**
 * @param {object} opts
 * @param {Array} opts.groups – Aus getGroupsWithParticipants (participants: flache Objekte mit id, position, isExternal, …)
 * @param {Array} opts.matches – Aus getTournamentMatches
 * @param {Array<{ id: number, isDoubles?: boolean }>} opts.classes
 * @param {Map<number, number>} opts.tmToMemberId – Turnier-Mitglied-ID -> Vereins-Mitglied-ID
 * @param {Map<number, { firstName: string, lastName: string }>} [opts.tmToName] – optional Namen
 * @param {Map<number, { birthDate?: string|null, gender?: string|null }>} [opts.tmToMemberProfile] – pro TM: Mitglied Geburtsdatum + Geschlecht (für Filter)
 * @param {Set<string>|null} [opts.allowedAgeKeys] – tt|9|female … tt|adult|open; null = alle
 * @param {string} [opts.tournamentDateIso] – Turnierdatum für J/Erwachsenen-Zuordnung
 * @returns {Map<number, { points: number, firstName: string, lastName: string }>} Vereins-Mitglied-ID -> Aggregation
 */
export function computeInternalSinglesStatsForTournament({
  groups,
  matches,
  classes,
  tmToMemberId,
  tmToName,
  tmToMemberProfile,
  allowedAgeKeys = null,
  tournamentDateIso,
}) {
  const doublesClassIds = new Set(
    (classes || []).filter((c) => c.isDoubles).map((c) => Number(c.id))
  );

  const classKeys = new Set();
  for (const g of groups || []) {
    const cid = g.classId != null ? Number(g.classId) : null;
    if (cid != null && doublesClassIds.has(cid)) continue;
    classKeys.add(cid != null ? String(cid) : 'null');
  }
  for (const m of matches || []) {
    if (m.round === 'group') continue;
    const cid = m.classId != null ? Number(m.classId) : null;
    if (cid != null && doublesClassIds.has(cid)) continue;
    classKeys.add(cid != null ? String(cid) : 'null');
  }

  const memberTotals = new Map();

  const ensureMember = (memberId, firstName, lastName) => {
    if (memberId == null || !Number.isFinite(memberId)) return null;
    if (!memberTotals.has(memberId)) {
      memberTotals.set(memberId, {
        points: 0,
        firstName: firstName || '',
        lastName: lastName || '',
      });
    }
    return memberTotals.get(memberId);
  };

  const nameForTm = (tmId) => {
    const n = tmToName?.get(Number(tmId));
    return { firstName: n?.firstName || '', lastName: n?.lastName || '' };
  };

  const profileForTm = (tmId) => tmToMemberProfile?.get(Number(tmId)) ?? null;

  for (const classKey of classKeys) {
    const classIdNum = classKey === 'null' ? null : Number(classKey);
    if (classIdNum != null && doublesClassIds.has(classIdNum)) continue;

    const classGroups = (groups || []).filter((g) => {
      const gc = g.classId != null ? Number(g.classId) : null;
      const key = gc != null ? String(gc) : 'null';
      return key === classKey;
    });

    const groupPointsByTm = new Map();
    let maxGroupPct = 0;

    for (const g of classGroups) {
      const parts = g.participants || [];
      if (parts.length === 0) continue;

      const nInGroup = parts.filter((p) => Number(p.position) > 0).length;
      if (nInGroup < 1) continue;

      const singlesRankings = parts
        .filter((p) => !p.isExternal && p.id && tmToMemberId.has(Number(p.id)))
        .map((p) => ({
          id: Number(p.id),
          position: Number(p.position || 0),
        }))
        .filter((r) => r.position > 0);

      const m = groupPercentFromRankings(singlesRankings, nInGroup);
      for (const [tmId, pts] of m) {
        groupPointsByTm.set(tmId, pts);
        if (pts > maxGroupPct) maxGroupPct = pts;
      }
    }

    const koMatches = (matches || []).filter((m) => {
      if (m.round === 'group') return false;
      const mid = m.classId != null ? Number(m.classId) : null;
      if (classIdNum == null) return mid == null;
      return mid === classIdNum;
    });

    const koWins = new Map();
    const playedKo = new Set();

    for (const m of koMatches) {
      if (!m.isFinished) continue;
      const p1 = m.player1Id;
      const p2 = m.player2Id;
      if (p1) playedKo.add(p1);
      if (p2) playedKo.add(p2);
      const { winnerId } = parseWinnerFromMatch(m);
      if (winnerId) {
        koWins.set(winnerId, (koWins.get(winnerId) || 0) + 1);
      }
    }

    const tmIdsInClass = new Set([...groupPointsByTm.keys(), ...playedKo]);

    for (const tmId of tmIdsInClass) {
      const mid = tmToMemberId.get(Number(tmId));
      if (mid == null) continue;

      const bucket = memberToCanonicalTtBucket(profileForTm(tmId), tournamentDateIso);
      if (allowedAgeKeys != null) {
        if (allowedAgeKeys.size === 0) continue;
        if (bucket == null || !allowedAgeKeys.has(bucket)) continue;
      }

      const gPts = groupPointsByTm.get(tmId) ?? 0;
      const wins = koWins.get(tmId) || 0;
      const inKo = playedKo.has(tmId);
      let total;
      if (inKo) {
        total = maxGroupPct + 1 + wins;
      } else {
        total = gPts;
      }

      const { firstName, lastName } = nameForTm(tmId);
      const row = ensureMember(mid, firstName, lastName);
      if (row) row.points += total;
    }
  }

  return memberTotals;
}

export default {
  groupPercentFromRankings,
  computeInternalSinglesStatsForTournament,
  parseWinnerFromMatch,
};
