/* global React */
const { useState, useMemo, useEffect, useRef } = React;

// ============ SEED DATA ============
// Spielklassen
const SEED_CLASSES = [
  { id: "c_u7", name: "U7" },
  { id: "c_u8", name: "U8" },
  { id: "c_u9", name: "U9" },
  { id: "c_u10", name: "U10" },
];

// Trainer mit Login + Admin-Recht + Klassen-Zuordnung
const SEED_TRAINERS = [
  { id: "tr_markus",  name: "Markus Fink",    email: "markus@admiradornbirn.at",  password: "admira", admin: true,  classIds: ["c_u8"] },
  { id: "tr_stefan",  name: "Stefan Greber",  email: "stefan@admiradornbirn.at",  password: "admira", admin: false, classIds: ["c_u10"] },
  { id: "tr_daniela", name: "Daniela Mathis", email: "daniela@admiradornbirn.at", password: "admira", admin: true,  classIds: ["c_u9"] },
  { id: "tr_thomas",  name: "Thomas Berger",  email: "thomas@admiradornbirn.at",  password: "admira", admin: false, classIds: ["c_u7", "c_u8"] },
];

// Spieler-Pools je Klasse
const SEED_CLASS_PLAYERS = {
  c_u7: [
    "Emil K.", "Tobias B.", "Jan W.", "Luca M.", "Nico P.", "Raphael O.",
    "Simon R.", "Fabian S.", "Marco G.", "Dominik T.", "Elias V.", "Julian L."
  ],
  c_u8: [
    "Lukas H.", "Tobias K.", "Jonas M.", "Felix W.", "Noah B.", "Elias R.",
    "Maximilian P.", "Leon S.", "Paul G.", "David T.", "Samuel V.", "Jakob L.",
    "Linus E.", "Niklas O.", "Moritz A.", "Anton F."
  ],
  c_u9: [
    "Mia F.", "Lena K.", "Sarah W.", "Anna M.", "Julia P.", "Hanna S.",
    "Lina B.", "Emma R.", "Sophie T.", "Laura V.", "Clara G.", "Marie L.",
    "Lea H.", "Pia E.", "Nina A.", "Greta F."
  ],
  c_u10: [
    "Mattis K.", "Jannik B.", "Henri W.", "Theo M.", "Levi P.", "Finn O.",
    "Emil R.", "Oskar L.", "Mats E.", "Carl V.", "Ben D.", "Tim H.",
    "Luis G.", "Aaron S.", "Hannes A.", "Vincent T.", "Ferdi N.", "Liam C."
  ],
};

// Standard-Lagerartikel je Klasse
const DEFAULT_INVENTORY = [
  { id: "i_baelle",   name: "Bälle",   count: 12 },
  { id: "i_tore",     name: "Tore",    count: 4 },
  { id: "i_trikots",  name: "Trikots", count: 20 },
  { id: "i_huetchen", name: "Hütchen", count: 30 },
  { id: "i_leibchen", name: "Leibchen", count: 16 },
];

// Jahresplan: Vorschläge für Einheiten
const EINHEIT_SUGGESTIONS = [
  "Ballschule TE01","Ballschule TE02","Ballschule TE03","Ballschule TE04","Ballschule TE05",
  "Ballschule TE06","Ballschule TE07","Ballschule TE08","Ballschule TE09","Ballschule TE10",
  "An- und Mitnahme","Zuspiel","Torschuss","Tricks und Finten",
  "Turniervorbereitung","Turnier","Spiel","Halle", "frei wählbar"
];

// Saison läuft August (8) → Juli (7). Reihenfolge der Monatsspalten.
const SEASON_MONTHS = [
  { m: 7,  label: "Aug" }, { m: 8,  label: "Sep" }, { m: 9,  label: "Okt" },
  { m: 10, label: "Nov" }, { m: 11, label: "Dez" }, { m: 0,  label: "Jän" },
  { m: 1,  label: "Feb" }, { m: 2,  label: "Mär" }, { m: 3,  label: "Apr" },
  { m: 4,  label: "Mai" }, { m: 5,  label: "Jun" }, { m: 6,  label: "Jul" },
];
const WEEKDAYS_DE = ["Sonntag","Montag","Dienstag","Mittwoch","Donnerstag","Freitag","Samstag"];
const WEEKDAYS_SHORT = ["So","Mo","Di","Mi","Do","Fr","Sa"];

// Returns weekday name for an ISO date string (yyyy-mm-dd)
function weekdayOf(iso, short) {
  if (!iso) return "";
  const d = new Date(iso + "T12:00:00");
  if (isNaN(d)) return "";
  return (short ? WEEKDAYS_SHORT : WEEKDAYS_DE)[d.getDay()];
}
function formatDateDE(iso) {
  if (!iso) return "";
  const d = new Date(iso + "T12:00:00");
  if (isNaN(d)) return iso;
  return ("0" + d.getDate()).slice(-2) + "." + ("0" + (d.getMonth() + 1)).slice(-2) + "." + d.getFullYear();
}

const TEAM_LETTERS = ["1","2","3","4","5","6","7","8","9","10","11","12"];
const COLOR_PALETTE = [
  "#E63946","#F4A261","#F1C453","#E9C46A","#2A9D8F","#06A77D",
  "#5BAEE0","#1E5BA8","#7B61FF","#A663CC","#E76F51","#D62828",
  "#264653","#52B788","#FB8500","#FF006E","#3A86FF","#8338EC"
];

// ============ MODES ============
// "mix" (Nr.1): each player switches teammates every round
// "fixed" (Nr.2): each player locked into one team
// "tournament" (Nr.3): teams play each other (no individual players)

// Compute the Mix-mode layout from a "driver" parameter.
// driver "fields"  → fields fixed, teamSize (per half) is derived.
// driver "teamSize" → teamSize fixed, fields are derived.
// 1 Mannschaft = one half-team. Anzahl Mannschaften = fields × 2 = playerCount ÷ teamSize.
function computeMixLayout({ playerCount, fields, teamSize, mixDriver }) {
  const pc = Math.max(1, playerCount);
  let f, ts;
  if (mixDriver === "teamSize") {
    // each field needs teamSize×2 players; clamp so teamSize ≤ half the squad
    ts = Math.max(1, Math.min(teamSize, Math.floor(pc / 2)));
    f = Math.max(1, Math.floor(pc / (ts * 2)));
  } else {
    // driver "fields" (default): each field has playerCount/fields players → half that per side
    f = Math.max(1, Math.min(fields, Math.floor(pc / 2)));
    ts = Math.max(1, Math.floor(pc / (f * 2)));
  }
  const teamCount = f * 2;          // number of half-teams (Mannschaften)
  const totalBase = teamCount * ts;
  const extras = Math.max(0, pc - totalBase); // distributed as +1 across teams
  return { fields: f, teamSize: ts, teamCount, mannschaften: teamCount, extras };
}

// MODE 1 — MIX
// Each round, players are split into half-teams of size `teamSize` (a match = 2 halves).
// Each player should play with as many different teammates as possible across rounds.
// Greedy algorithm: for each round, place players one-by-one into the team that minimizes
// total prior-co-play. If playerCount doesn't divide evenly, some teams get +1 player.
function generateMixPlan({ playerCount, fields, rounds, players, teamSize, mixDriver }) {
  const layout = computeMixLayout({ playerCount, fields, teamSize, mixDriver });
  return generateMixPlanCore({
    playerCount,
    rounds,
    players,
    teamCount: layout.teamCount,
    baseSize: layout.teamSize,
    extras: layout.extras,
  });
}

function generateMixPlanCore({ playerCount, rounds, players, teamCount, baseSize, extras }) {
  // co[i][j] = number of times player i has been in same team as j so far.
  const co = Array.from({ length: playerCount }, () => new Array(playerCount).fill(0));
  // Pre-compute team capacities (some teams have +1 when extras > 0)
  const capacityFor = (round, t) => {
    // Rotate which teams get the extra slot per round, so the +1 is fair.
    const offset = round % teamCount;
    const isPlus = ((t - offset + teamCount) % teamCount) < extras;
    return baseSize + (isPlus ? 1 : 0);
  };

  // Build per-round assignments: assignments[round][playerIdx] = teamNumber (1..teamCount)
  const assignments = Array.from({ length: rounds }, () => new Array(playerCount).fill(0));

  for (let r = 0; r < rounds; r++) {
    // Order players for this round: shuffle but seed by index+round so regenerating is deterministic per round.
    // Sort by (random + cumulative co-play sum) so well-mixed players go later.
    const order = Array.from({ length: playerCount }, (_, i) => i);
    // Fisher-Yates shuffle with seeded randomness for variety
    let seed = (r + 1) * 9301 + 49297;
    const rand = () => {
      seed = (seed * 9301 + 49297) % 233280;
      return seed / 233280;
    };
    for (let i = order.length - 1; i > 0; i--) {
      const j = Math.floor(rand() * (i + 1));
      [order[i], order[j]] = [order[j], order[i]];
    }

    const teams = Array.from({ length: teamCount }, () => []);
    const caps = Array.from({ length: teamCount }, (_, t) => capacityFor(r, t));

    for (const pi of order) {
      // Score each available team: lower co-play with current members = better.
      // Tie-break: prefer team with most remaining capacity (= fewer members).
      let bestT = -1;
      let bestScore = Infinity;
      for (let t = 0; t < teamCount; t++) {
        if (teams[t].length >= caps[t]) continue;
        let cost = 0;
        for (const member of teams[t]) cost += co[pi][member];
        // small penalty for fuller teams to balance
        const fillPenalty = teams[t].length * 0.001;
        const score = cost + fillPenalty;
        if (score < bestScore) {
          bestScore = score;
          bestT = t;
        }
      }
      if (bestT < 0) {
        // overflow safety: take any team with capacity
        bestT = caps.findIndex((c, t) => teams[t].length < c);
        if (bestT < 0) bestT = 0;
      }
      teams[bestT].push(pi);
      assignments[r][pi] = bestT + 1;
    }

    // Update co-play counts
    for (let t = 0; t < teamCount; t++) {
      const m = teams[t];
      for (let i = 0; i < m.length; i++) {
        for (let j = i + 1; j < m.length; j++) {
          co[m[i]][m[j]]++;
          co[m[j]][m[i]]++;
        }
      }
    }
  }

  // Build output rows. The "field" the player plays on each round =
  // ceil(team / 2) (since match k uses teams 2k-1 and 2k).
  const out = [];
  for (let i = 0; i < playerCount; i++) {
    const row = {
      idx: i + 1,
      name: players[i]?.name || "",
      group: 1,
      rounds: [],
      teamPerRound: [],
    };
    for (let r = 0; r < rounds; r++) {
      const team = assignments[r][i];
      const field = Math.ceil(team / 2);
      row.rounds.push(String(field));
      row.teamPerRound.push(team);
    }
    out.push(row);
  }
  return out;
}

// MODE 2 — FIXED
// Each player stays in same team. Teams rotate across fields each round.
// Real round-robin schedule: every round each Mannschaft meets a DIFFERENT
// opponent. Two teams sharing a field play against each other. With an odd
// number of teams, one team gets a rotating Freilos (bye) each round.
// teamCount (playerCount ÷ Teamgröße) decides how many teams there are.
function generateFixedPlan({ playerCount, fields, rounds, players, teams, teamCount }) {
  const tc = Math.max(1, teamCount || teams.length);

  // 1) Assign every player to a fixed team index. Honor a manual assignment
  //    from the Mannschaften tab when it fits inside the derived team count;
  //    otherwise distribute players round-robin across the teams.
  const playerTeam = new Array(playerCount).fill(0);
  for (let i = 0; i < playerCount; i++) {
    const player = players[i];
    let idx = -1;
    if (player) idx = teams.findIndex(t => t.playerIds.includes(player.id));
    if (idx < 0 || idx >= tc) idx = i % tc;
    playerTeam[i] = idx;
  }

  // 2) Circle-method round-robin over the team indices (with a bye slot if odd).
  const slots = [];
  for (let i = 0; i < tc; i++) slots.push(i);
  if (slots.length % 2 === 1) slots.push(-1); // -1 = Freilos
  const n = slots.length;
  const rrLen = Math.max(1, n - 1);

  // teamField[r] = { teamIdx: fieldNo | null(bye) }
  const teamField = [];
  const maxMatches = Math.max(1, fields);
  const playCount = new Array(tc).fill(0); // how often each team has played so far
  for (let r = 0; r < rounds; r++) {
    const k = r % rrLen;
    // keep slot 0 fixed, rotate the rest right by k
    const first = slots[0];
    const rest = slots.slice(1);
    const rot = rest.slice(rest.length - k).concat(rest.slice(0, rest.length - k));
    const arr = [first, ...rot];
    // All disjoint pairings for this round-robin round (skip the bye pair).
    const pairings = [];
    for (let i = 0; i < n / 2; i++) {
      const a = arr[i];
      const b = arr[n - 1 - i];
      if (a === -1 || b === -1) continue; // the real team in a bye pair rests
      pairings.push([a, b]);
    }
    // Everyone rests by default; then field as many matches as there are
    // Spielfelder. When there are more pairings than fields, prefer the pairings
    // whose teams have played the LEAST so far — that shares rest fairly across
    // rounds instead of always benching the same teams.
    const map = {};
    for (let t = 0; t < tc; t++) map[t] = null;
    const ranked = pairings
      .map((p, idx) => ({ p, idx, load: playCount[p[0]] + playCount[p[1]] }))
      .sort((a, b) => a.load - b.load || a.idx - b.idx);
    const fielded = Math.min(maxMatches, pairings.length);
    for (let m = 0; m < fielded; m++) {
      const [a, b] = ranked[m].p;
      const fieldNo = m + 1;
      map[a] = fieldNo;
      map[b] = fieldNo;
      playCount[a] += 1;
      playCount[b] += 1;
    }
    teamField.push(map);
  }

  // 3) Per-player rows: field per round (empty string = Freilos/Pause).
  const out = [];
  for (let i = 0; i < playerCount; i++) {
    const player = players[i];
    const teamIdx = playerTeam[i];
    const fallbackLetter = (typeof TEAM_LETTERS !== "undefined" && TEAM_LETTERS[teamIdx]) || String(teamIdx + 1);
    const fallbackColor = (typeof COLOR_PALETTE !== "undefined" && COLOR_PALETTE[teamIdx % COLOR_PALETTE.length]) || undefined;
    const row = {
      idx: i + 1,
      name: player?.name || "",
      teamIdx,
      teamLetter: teams[teamIdx]?.letter || fallbackLetter,
      teamColor: teams[teamIdx]?.color || fallbackColor,
      group: 1,
      rounds: [],
    };
    for (let r = 0; r < rounds; r++) {
      const f = teamField[r][teamIdx];
      row.rounds.push(f == null ? "" : String(f));
    }
    out.push(row);
  }
  return out;
}

// MODE 3 — TOURNAMENT
// teams list, divided into N groups. Round-robin within each group.
// Output: list of rounds, each round is list of matches { groupIdx, teamA, teamB, field }
function generateTournamentPlan({ groups, rounds, fields }) {
  // groups = [{ name, teams: [{id, name, letter, color}] }, ...]
  const out = [];
  groups.forEach((grp, gIdx) => {
    const ts = grp.teams.slice();
    // Circle method round robin
    if (ts.length % 2 === 1) ts.push({ id: "bye_" + gIdx, name: "Spielfrei", letter: "—", bye: true });
    const n = ts.length;
    const rrRounds = n - 1;
    for (let r = 0; r < rrRounds; r++) {
      const matches = [];
      for (let i = 0; i < n / 2; i++) {
        const a = ts[i];
        const b = ts[n - 1 - i];
        if (!a.bye && !b.bye) {
          matches.push({
            groupIdx: gIdx,
            groupName: grp.name,
            teamA: a,
            teamB: b,
          });
        }
      }
      // rotate (keep first fixed)
      const last = ts.pop();
      ts.splice(1, 0, last);
      if (!out[r]) out[r] = { round: r + 1, matches: [] };
      out[r].matches.push(...matches);
    }
  });
  // Limit/extend to requested rounds
  const final = [];
  for (let r = 0; r < rounds; r++) {
    if (out[r]) {
      final.push(out[r]);
    } else {
      final.push({ round: r + 1, matches: [] });
    }
  }
  // Assign field numbers per round
  final.forEach(rd => {
    rd.matches.forEach((m, i) => {
      m.field = String((i % Math.max(1, fields)) + 1);
    });
  });
  return final;
}

// Build matchups for player-based modes (1, 2): for each round, each field's players.
function buildRoundMatches(plan, rounds) {
  const matches = [];
  for (let r = 0; r < rounds; r++) {
    const round = { round: r + 1, fields: [], bye: [] };
    const byField = new Map();
    plan.forEach(row => {
      const f = row.rounds[r];
      if (f === "" || f == null) { round.bye.push(row); return; } // Freilos/Pause
      if (!byField.has(f)) byField.set(f, []);
      byField.get(f).push(row);
    });
    Array.from(byField.entries())
      .sort((a, b) => Number(a[0]) - Number(b[0]))
      .forEach(([field, ps]) => {
        // Mix mode: split by actual team (odd team number = home/a, even = away/b).
        if (ps[0] && ps[0].teamPerRound) {
          const home = ps.filter(p => p.teamPerRound[r] % 2 === 1);
          const away = ps.filter(p => p.teamPerRound[r] % 2 === 0);
          round.fields.push({ field, home, away });
        } else {
          // Fixed mode: a field hosts a real match between two fixed Mannschaften.
          // Group the players by their team, then pair the two teams.
          const groups = [];
          ps.forEach(p => {
            let g = groups.find(t => t.teamIdx === p.teamIdx);
            if (!g) { g = { teamIdx: p.teamIdx, players: [] }; groups.push(g); }
            g.players.push(p);
          });
          groups.sort((a, b) => a.teamIdx - b.teamIdx);
          round.fields.push({
            field,
            home: groups[0] ? groups[0].players : [],
            away: groups.length > 1 ? groups.slice(1).flatMap(g => g.players) : [],
          });
        }
      });
    matches.push(round);
  }
  // Group resting players by their team so each Freilos shows as its own team.
  matches.forEach(round => {
    const groups = [];
    round.bye.forEach(p => {
      let g = groups.find(t => t.teamIdx === p.teamIdx);
      if (!g) { g = { teamIdx: p.teamIdx, teamLetter: p.teamLetter, teamColor: p.teamColor, players: [] }; groups.push(g); }
      g.players.push(p);
    });
    groups.sort((a, b) => a.teamIdx - b.teamIdx);
    round.byeTeams = groups;
  });
  return matches;
}

// Co-play matrix: counts how often each pair was in the SAME team (not just same field).
// Mix → uses teamPerRound; Fixed → uses the player's fixed team; fallback → field.
function buildCoPlay(plan, rounds) {
  const n = plan.length;
  const m = Array.from({ length: n }, () => new Array(n).fill(0));
  const teamKey = (row, r) => {
    if (row.teamPerRound) return "t" + row.teamPerRound[r];     // mix: team varies per round
    if (row.teamLetter != null && row.teamLetter !== "") return "L" + row.teamLetter; // fixed
    return "f" + row.rounds[r];                                 // fallback
  };
  for (let r = 0; r < rounds; r++) {
    for (let i = 0; i < n; i++) {
      for (let j = i + 1; j < n; j++) {
        if (teamKey(plan[i], r) === teamKey(plan[j], r)) {
          m[i][j]++;
          m[j][i]++;
        }
      }
    }
  }
  return m;
}

// ============ STORAGE (MySQL-Backend via API) ============
// Der Zustand wird von app.jsx beim Start per GET /api/state geladen
// und bei Änderungen per PUT /api/state gespeichert.
// Diese Funktionen sind No-Ops – die API-Logik liegt in app.jsx.
function loadState() { return null; }
function saveState(_s) { /* wird von app.jsx via fetch('/api/state') erledigt */ }

// ============ ICONS ============
const Icon = {
  Home: () => <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/></svg>,
  Users: () => <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg>,
  Shield: () => <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg>,
  Settings: () => <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 1 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.6 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 1 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 1 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 1 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>,
  Grid: () => <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/></svg>,
  History: () => <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M3 3v5h5"/><path d="M3.05 13A9 9 0 1 0 6 5.3L3 8"/><path d="M12 7v5l4 2"/></svg>,
  Plus: () => <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>,
  Trash: () => <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6l-2 14a2 2 0 0 1-2 2H9a2 2 0 0 1-2-2L5 6"/><path d="M10 11v6"/><path d="M14 11v6"/></svg>,
  Logout: () => <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" y1="12" x2="9" y2="12"/></svg>,
  Sparkles: () => <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M12 3l1.5 4.5L18 9l-4.5 1.5L12 15l-1.5-4.5L6 9l4.5-1.5z"/><path d="M19 15l.8 2.2L22 18l-2.2.8L19 21l-.8-2.2L16 18l2.2-.8z"/></svg>,
  Check: () => <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round"><polyline points="20 6 9 17 4 12"/></svg>,
  X: () => <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>,
  Trophy: () => <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M6 9H4a2 2 0 0 1-2-2V5h4"/><path d="M18 9h2a2 2 0 0 0 2-2V5h-4"/><path d="M6 5v6a6 6 0 0 0 12 0V5"/><path d="M9 21h6"/><path d="M12 17v4"/></svg>,
  Shuffle: () => <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><polyline points="16 3 21 3 21 8"/><line x1="4" y1="20" x2="21" y2="3"/><polyline points="21 16 21 21 16 21"/><line x1="15" y1="15" x2="21" y2="21"/><line x1="4" y1="4" x2="9" y2="9"/></svg>,
  Lock: () => <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><rect x="3" y="11" width="18" height="11" rx="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg>,
  Box: () => <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/><polyline points="3.27 6.96 12 12.01 20.73 6.96"/><line x1="12" y1="22.08" x2="12" y2="12"/></svg>,
  Cog: () => <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M12 15a3 3 0 1 0 0-6 3 3 0 0 0 0 6z"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 1 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.6 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 1 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 1 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 1 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>,
  Mail: () => <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><rect x="2" y="4" width="20" height="16" rx="2"/><path d="m22 7-10 5L2 7"/></svg>,
  Calendar: () => <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><rect x="3" y="4" width="18" height="18" rx="2"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/></svg>,
  Chart: () => <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><line x1="18" y1="20" x2="18" y2="10"/><line x1="12" y1="20" x2="12" y2="4"/><line x1="6" y1="20" x2="6" y2="14"/></svg>,
  Printer: () => <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><polyline points="6 9 6 2 18 2 18 9"/><path d="M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2"/><rect x="6" y="14" width="12" height="8"/></svg>,
};

Object.assign(window, { SEED_CLASSES, SEED_TRAINERS, SEED_CLASS_PLAYERS, DEFAULT_INVENTORY, EINHEIT_SUGGESTIONS, SEASON_MONTHS, WEEKDAYS_DE, WEEKDAYS_SHORT, weekdayOf, formatDateDE, TEAM_LETTERS, COLOR_PALETTE, computeMixLayout, generateMixPlan, generateFixedPlan, generateTournamentPlan, buildRoundMatches, buildCoPlay, loadState, saveState, Icon });
