﻿// Tournament data + double-elim bracket builder

// ============ TEAM POOLS ============

const POOL_DUOS_CUP = [
  { seed: 1,  name: "FROST · BRIGADE",      tag: "FRST", p1: "ZRO_lars90",     p2: "STRK_eivind66",   lp: 1842 },
  { seed: 2,  name: "NORD · RECON",         tag: "NRD",  p1: "FROST_magnus34", p2: "VKNG_jonas21",    lp: 1798 },
  { seed: 3,  name: "FJORD · VIPERS",       tag: "FJV",  p1: "KOLD_petter27",  p2: "ICE_henrik55",    lp: 1721 },
  { seed: 4,  name: "AURORA · STRIKE",      tag: "AUR",  p1: "RIOT_aksel41",   p2: "BANE_sondre77",   lp: 1689 },
  { seed: 5,  name: "VIKING · REFLEX",      tag: "VKR",  p1: "ODIN_thor62",    p2: "WLF_sigurd88",    lp: 1654 },
  { seed: 6,  name: "POLAR · WOLVES",       tag: "PLR",  p1: "RAGE_iver73",    p2: "ECHO_brage12",    lp: 1612 },
  { seed: 7,  name: "TRONDHEIM · TACTICAL", tag: "TRD",  p1: "BLITZ_kasper29", p2: "ZRO_marius18",    lp: 1583 },
  { seed: 8,  name: "BERGEN · BULLET CO",   tag: "BGN",  p1: "DARK_simen55",   p2: "GHST_filip64",    lp: 1547 },
  { seed: 9,  name: "SALTVANN · GG",        tag: "SLT",  p1: "TRIG_vegard91",  p2: "KNFR_ola23",      lp: 1502 },
  { seed: 10, name: "OSLO · OUTLAWS",       tag: "OSL",  p1: "XPRT_tobias14",  p2: "STM_andreas07",   lp: 1488 },
  { seed: 11, name: "KVITFJELL · KREW",     tag: "KVT",  p1: "BANE_even33",    p2: "RKT_jakob58",     lp: 1465 },
  { seed: 12, name: "STAVANGER · STORM",    tag: "STV",  p1: "ZERO_brage44",   p2: "HVN_petter71",    lp: 1432 },
  { seed: 13, name: "LOFOTEN · LOCKDOWN",   tag: "LFT",  p1: "WLF_eivind08",   p2: "KRSE_lars51",     lp: 1401 },
  { seed: 14, name: "DRAMMEN · DEATH",      tag: "DRM",  p1: "ECHO_marius22",  p2: "STRK_sander17",   lp: 1378 },
  { seed: 15, name: "HARDANGER · HEAT",     tag: "HRD", p1: "RKT_simen63",    p2: "BLITZ_thor19",     lp: 1342 },
  { seed: 16, name: "NARVIK · NINE",        tag: "NRV",  p1: "DARK_iver74",    p2: "TRIG_aksel29",    lp: 1318 },
];

// 13 duos -> bracket of 16 with 3 BYEs (seeds 14, 15, 16 byes)
const POOL_DUOS_BLITZ = [
  { seed: 1,  name: "TRONDHEIM · TACTICAL", tag: "TRD", p1: "BLITZ_kasper29", p2: "ZRO_marius18",   lp: 1583 },
  { seed: 2,  name: "BERGEN · BULLET CO",   tag: "BGN", p1: "DARK_simen55",   p2: "GHST_filip64",   lp: 1547 },
  { seed: 3,  name: "SALTVANN · GG",        tag: "SLT", p1: "TRIG_vegard91",  p2: "KNFR_ola23",     lp: 1502 },
  { seed: 4,  name: "OSLO · OUTLAWS",       tag: "OSL", p1: "XPRT_tobias14",  p2: "STM_andreas07",  lp: 1488 },
  { seed: 5,  name: "KVITFJELL · KREW",     tag: "KVT", p1: "BANE_even33",    p2: "RKT_jakob58",    lp: 1465 },
  { seed: 6,  name: "STAVANGER · STORM",    tag: "STV", p1: "ZERO_brage44",   p2: "HVN_petter71",   lp: 1432 },
  { seed: 7,  name: "LOFOTEN · LOCKDOWN",   tag: "LFT", p1: "WLF_eivind08",   p2: "KRSE_lars51",    lp: 1401 },
  { seed: 8,  name: "DRAMMEN · DEATH",      tag: "DRM", p1: "ECHO_marius22",  p2: "STRK_sander17",  lp: 1378 },
  { seed: 9,  name: "HARDANGER · HEAT",     tag: "HRD", p1: "RKT_simen63",    p2: "BLITZ_thor19",   lp: 1342 },
  { seed: 10, name: "NARVIK · NINE",        tag: "NRV", p1: "DARK_iver74",    p2: "TRIG_aksel29",   lp: 1318 },
  { seed: 11, name: "ÅLESUND · AIM",        tag: "ÅLS", p1: "STM_jakob44",    p2: "KNFR_sondre60",  lp: 1287 },
  { seed: 12, name: "ROMERIKE · RECON",     tag: "ROM", p1: "VKNG_mathias12", p2: "FROST_petter18", lp: 1264 },
  { seed: 13, name: "TELEMARK · TIGERS",    tag: "TLM", p1: "ODIN_henrik09",  p2: "WLF_eivind50",   lp: 1241 },
];

// Killrace: 14 duos competing in parallel kill-race lobbies
// (CMG/Total Frenzy style — kills accumulate across maps, no bracket, leaderboard ranking)
const POOL_KILLRACE = [
  { seed: 1,  name: "FROST · BRIGADE",      tag: "FRST", p1: "ZRO_lars90",     p2: "STRK_eivind66",   lp: 1842 },
  { seed: 2,  name: "NORD · RECON",         tag: "NRD",  p1: "FROST_magnus34", p2: "VKNG_jonas21",    lp: 1798 },
  { seed: 3,  name: "FJORD · VIPERS",       tag: "FJV",  p1: "ICE_henrik55",   p2: "RAGE_iver73",     lp: 1721 },
  { seed: 4,  name: "AURORA · STRIKE",      tag: "AUR",  p1: "RIOT_aksel41",   p2: "BANE_sondre77",   lp: 1689 },
  { seed: 5,  name: "VIKING · REFLEX",      tag: "VKR",  p1: "ODIN_thor62",    p2: "WLF_sigurd88",    lp: 1654 },
  { seed: 6,  name: "POLAR · WOLVES",       tag: "PLR",  p1: "ECHO_brage12",   p2: "BLITZ_kasper29",  lp: 1612 },
  { seed: 7,  name: "TRONDHEIM · TACTICAL", tag: "TRD",  p1: "DARK_simen55",   p2: "GHST_filip64",    lp: 1583 },
  { seed: 8,  name: "BERGEN · BULLET CO",   tag: "BGN",  p1: "TRIG_vegard91",  p2: "KNFR_ola23",      lp: 1547 },
  { seed: 9,  name: "SALTVANN · GG",        tag: "SLT",  p1: "XPRT_tobias14",  p2: "STM_andreas07",   lp: 1502 },
  { seed: 10, name: "OSLO · OUTLAWS",       tag: "OSL",  p1: "BANE_even33",    p2: "RKT_jakob58",     lp: 1488 },
  { seed: 11, name: "KVITFJELL · KREW",     tag: "KVT",  p1: "ZERO_brage44",   p2: "HVN_petter71",    lp: 1465 },
  { seed: 12, name: "STAVANGER · STORM",    tag: "STV",  p1: "WLF_eivind08",   p2: "KRSE_lars51",     lp: 1432 },
  { seed: 13, name: "LOFOTEN · LOCKDOWN",   tag: "LFT",  p1: "ECHO_marius22",  p2: "STRK_sander17",   lp: 1401 },
  { seed: 14, name: "DRAMMEN · DEATH",      tag: "DRM",  p1: "RKT_simen63",    p2: "BLITZ_thor19",    lp: 1378 },
];

// Killrace match schedule: each "map" is a 12-min Verdansk/Urzikstan lobby.
// Kills are cumulative across maps for leaderboard ranking (CMG Total Frenzy style).
const KILLRACE_MAPS = [
  { idx: 1, map: "Verdansk",  status: "completed", time: "19:00" },
  { idx: 2, map: "Urzikstan", status: "completed", time: "19:30" },
  { idx: 3, map: "Verdansk",  status: "live",      time: "20:00" },
  { idx: 4, map: "Urzikstan", status: "pending",   time: "20:30" },
  { idx: 5, map: "Verdansk",  status: "pending",   time: "21:00" },
];

// Pre-seeded results per duo per map (deterministic — favors higher seeds slightly)
// Numbers tuned for realistic Warzone duo kill counts (8-22 per map).
const buildKillraceResults = (tournament) => {
  const rng = mulberry32(hashStr(tournament ? tournament.id : "nordlys-killrace-04"));
  const teams = tournament && tournament.teams ? tournament.teams : POOL_KILLRACE;
  const maps = tournament && tournament.killraceMaps ? tournament.killraceMaps : KILLRACE_MAPS;
  return teams.map(team => {
    const skill = 1.6 - (team.seed - 1) * 0.07;
    const damagePerKill = 380 + Math.floor(rng() * 80);
    const mapResults = maps.map(m => {
      if (m.status === "pending") return { kills: null, damage: null, status: m.status };
      const base = 8 + skill * 7 + rng() * 9;
      const kills = Math.max(2, Math.round(base + (rng() < 0.15 ? 6 : 0)));
      const damage = Math.round(kills * damagePerKill + rng() * 800);
      return { kills, damage, status: m.status };
    });
    return { ...team, maps: mapResults };
  });
};

const hashStr = (s) => {
  let h = 5381;
  for (let i = 0; i < s.length; i++) h = ((h << 5) + h + s.charCodeAt(i)) | 0;
  return h >>> 0;
};
const mulberry32 = (seed) => () => {
  seed = (seed + 0x6D2B79F5) >>> 0;
  let t = seed;
  t = Math.imul(t ^ (t >>> 15), t | 1);
  t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
  return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
};

// ============ TOURNAMENT REGISTRY ============

const TOURNAMENTS = [
  {
    id: "duos-cup-mai",
    name: "WZN DUOS CUP",
    subtitle: "MAI 2026",
    type: "2v2",
    formatLong: "2v2 Duos · Double Elim · BO3 Winners · BO1 Losers · BO5 Grand Final",
    formatShort: "BO3 WB · BO1 LB · BO5 GF",
    wbBestOf: 3, lbBestOf: 1, gfBestOf: 5,
    map: "Verdansk",
    prize: "25 000 NOK",
    prizeSplit: [["1ST", "15 000"], ["2ND", "7 000"], ["3-4TH", "1 500"]],
    entry: "200 NOK / duo",
    status: "live",
    statusLabel: "PÅGÅR · WB QF",
    startsAt: "2026-05-11 18:00",
    teams: POOL_DUOS_CUP,
    progressIndex: 11, // matches completed
    liveCount: 2,
  },
  {
    id: "nordlys-killrace-04",
    name: "NORDLYS KILLRACE",
    subtitle: "#04 · MAI 2026",
    type: "killrace",
    formatLong: "2v2 Killrace · 5 lobbier · 12 min Verdansk + Urzikstan · Kumulative kills",
    formatShort: "KILLRACE · 5 MAPS",
    map: "Verdansk + Urzikstan",
    prize: "8 000 NOK",
    prizeSplit: [["1ST", "5 000"], ["2ND", "2 000"], ["3RD", "1 000"]],
    entry: "150 NOK / duo",
    status: "live",
    statusLabel: "PÅGÅR · MAP 3/5",
    startsAt: "2026-05-12 19:00",
    teams: POOL_KILLRACE,
    killraceMaps: KILLRACE_MAPS,
    progressIndex: 6,
    liveCount: 2,
  },
  {
    id: "duos-blitz",
    name: "WZN DUOS BLITZ",
    subtitle: "13.05.2026",
    type: "2v2",
    formatLong: "2v2 Duos · Double Elim · BO1 Winners · BO1 Losers · BO3 Grand Final · Rapid format",
    formatShort: "BO1 WB · BO1 LB · BO3 GF",
    wbBestOf: 1, lbBestOf: 1, gfBestOf: 3,
    map: "Verdansk",
    prize: "5 000 NOK",
    prizeSplit: [["1ST", "3 000"], ["2ND", "1 200"], ["3-4TH", "400"]],
    entry: "100 NOK / duo",
    status: "live",
    statusLabel: "PÅGÅR · WB R1",
    startsAt: "2026-05-13 20:00",
    teams: POOL_DUOS_BLITZ,
    progressIndex: 4,
    liveCount: 3,
  },
  {
    id: "grunnlov-2026",
    name: "GRUNNLOVS CUP",
    subtitle: "2026 · ÅPEN",
    type: "2v2",
    formatShort: "BO3 WB · BO1 LB · BO5 GF",
    wbBestOf: 3, lbBestOf: 1, gfBestOf: 5,
    map: "Verdansk",
    prize: "15 000 NOK",
    entry: "200 NOK / duo",
    status: "upcoming",
    statusLabel: "PÅMELDING ÅPEN · 24/32",
    startsAt: "2026-05-17 18:00",
    registered: 24, capacity: 32,
  },
  {
    id: "northern-open-5",
    name: "NORTHERN OPEN",
    subtitle: "#05 · KILLRACE",
    type: "killrace",
    formatShort: "KILLRACE · 5 MAPS",
    map: "Verdansk + Urzikstan",
    prize: "10 000 NOK",
    entry: "150 NOK / duo",
    status: "upcoming",
    statusLabel: "PÅMELDING ÅPEN · 12/32",
    startsAt: "2026-05-24 19:00",
    registered: 12, capacity: 32,
  },
  {
    id: "april-cup",
    name: "WZN APRIL CUP",
    subtitle: "2026 · FERDIG",
    type: "2v2",
    formatShort: "BO3 WB · BO1 LB · BO5 GF",
    map: "Verdansk",
    prize: "25 000 NOK",
    status: "completed",
    statusLabel: "FERDIG · 28.04",
    startsAt: "2026-04-28 18:00",
    winner: "NORD · RECON",
  },
];

// ============ BRACKET BUILDER ============

const nextPow2 = (n) => Math.pow(2, Math.ceil(Math.log2(Math.max(2, n))));

// Standard tournament seeding for size = power of 2.
// Returns flat seed order [s1, s_size, s_..., s2, ...] in slots.
const seedingOrder = (size) => {
  let arr = [1, 2];
  while (arr.length < size) {
    const sum = 2 * arr.length + 1;
    const next = [];
    arr.forEach(s => { next.push(s); next.push(sum - s); });
    arr = next;
  }
  return arr;
};

const buildBracket = (tournament) => {
  const size = nextPow2(tournament.teams.length);
  const teams = tournament.teams.slice();
  const byeCount = size - teams.length;
  while (teams.length < size) {
    teams.push({ bye: true, seed: teams.length + 1, name: "BYE", tag: "—" });
  }
  const order = seedingOrder(size);

  let rngState = hashStr(tournament.id);
  const r = () => { rngState = (rngState * 1664525 + 1013904223) >>> 0; return rngState / 4294967296; };

  const pickWinner = (a, b) => {
    if (a.bye && !b.bye) return "B";
    if (b.bye && !a.bye) return "A";
    if (a.bye && b.bye) return "A";
    // slight favor for higher seed (lower seed number)
    const favorA = a.seed < b.seed ? 0.62 : 0.38;
    return r() < favorA ? "A" : "B";
  };

  // CMG 2v2 scoring: WB rounds = BO_n (kill-totals across maps), LB = BO_m, GF = BO_g.
  // For BO1 we report kills per game (e.g. 12-9).
  // For BO3 we report combined kills across decisive maps (e.g. 24-18).
  const wbBO = tournament.wbBestOf ?? 3;
  const lbBO = tournament.lbBestOf ?? 1;
  const gfBO = tournament.gfBestOf ?? 5;

  const genScore = (winner, bracketKind, isBye) => {
    if (isBye) return { scoreA: "—", scoreB: "—", scoreLabel: "BYE" };
    const bo = bracketKind === "gf" ? gfBO : (bracketKind === "lb" ? lbBO : wbBO);
    if (bo === 1) {
      // single map kill total
      const w = 12 + Math.floor(r() * 14);
      const l = 4 + Math.floor(r() * (w - 3));
      const sa = winner === "A" ? w : l;
      const sb = winner === "A" ? l : w;
      return { scoreA: sa, scoreB: sb, scoreLabel: `${sa}-${sb} K` };
    }
    // BO3 or BO5: combined kills across maps decided
    const mapsPlayed = bo === 3 ? (r() < 0.6 ? 2 : 3) : (r() < 0.5 ? 3 : (r() < 0.7 ? 4 : 5));
    const w = 10 + Math.floor(r() * 16) + mapsPlayed * 6;
    const l = Math.max(8, w - 4 - Math.floor(r() * 16));
    const sa = winner === "A" ? w : l;
    const sb = winner === "A" ? l : w;
    return { scoreA: sa, scoreB: sb, scoreLabel: `${sa}-${sb} (${mapsPlayed}M)` };
  };

  // ---- WB ----
  const wbRounds = [];
  const wb1 = [];
  for (let i = 0; i < size / 2; i++) {
    const a = teams[order[2 * i] - 1];
    const b = teams[order[2 * i + 1] - 1];
    const isBye = a.bye || b.bye;
    const winner = pickWinner(a, b);
    wb1.push({
      id: `wb-1-${i}`, bracket: "wb", round: 1, matchNum: i + 1,
      teamA: a, teamB: b, winner, ...genScore(winner, "wb", isBye), isBye,
    });
  }
  wbRounds.push(wb1);

  const wbRoundsCount = Math.log2(size);
  for (let rd = 2; rd <= wbRoundsCount; rd++) {
    const prev = wbRounds[rd - 2];
    const cur = [];
    for (let i = 0; i < prev.length; i += 2) {
      const m1 = prev[i], m2 = prev[i + 1];
      const a = m1.winner === "A" ? m1.teamA : m1.teamB;
      const b = m2.winner === "A" ? m2.teamA : m2.teamB;
      const isBye = a.bye || b.bye;
      const winner = pickWinner(a, b);
      cur.push({
        id: `wb-${rd}-${i / 2}`, bracket: "wb", round: rd, matchNum: i / 2 + 1,
        teamA: a, teamB: b, winner, ...genScore(winner, "wb", isBye), isBye,
      });
    }
    wbRounds.push(cur);
  }

  // ---- LB ----
  const lbRounds = [];
  const wbLosers = (rd) => wbRounds[rd - 1].map(m => ({
    team: m.winner === "A" ? m.teamB : m.teamA,
    fromBye: m.isBye,
  }));

  // LB R1: pair WB R1 losers
  const losersR1 = wbLosers(1);
  const lb1 = [];
  for (let i = 0; i < losersR1.length; i += 2) {
    const a = losersR1[i].team;
    const b = losersR1[i + 1].team;
    const isBye = a.bye || b.bye;
    const winner = pickWinner(a, b);
    lb1.push({
      id: `lb-1-${i / 2}`, bracket: "lb", round: 1, matchNum: i / 2 + 1,
      teamA: a, teamB: b, winner, ...genScore(winner, "lb", isBye), isBye,
    });
  }
  lbRounds.push(lb1);

  const totalLBRounds = 2 * (wbRoundsCount - 1);
  for (let lr = 2; lr <= totalLBRounds; lr++) {
    const prev = lbRounds[lr - 2];
    const cur = [];
    if (lr % 2 === 0) {
      // Drop-in round: LB winners vs WB round (lr/2 + 1) losers
      const wbRound = lr / 2 + 1;
      const drops = wbLosers(wbRound);
      for (let i = 0; i < prev.length; i++) {
        const a = prev[i].winner === "A" ? prev[i].teamA : prev[i].teamB;
        const b = drops[drops.length - 1 - i] ? drops[drops.length - 1 - i].team : drops[i].team;
        const isBye = a.bye || b.bye;
        const winner = pickWinner(a, b);
        cur.push({
          id: `lb-${lr}-${i}`, bracket: "lb", round: lr, matchNum: i + 1,
          teamA: a, teamB: b, winner, ...genScore(winner, "lb", isBye), isBye,
          dropFromWB: wbRound,
        });
      }
    } else {
      // Pair-up round: LB winners paired
      for (let i = 0; i < prev.length; i += 2) {
        const a = prev[i].winner === "A" ? prev[i].teamA : prev[i].teamB;
        const b = prev[i + 1].winner === "A" ? prev[i + 1].teamA : prev[i + 1].teamB;
        const isBye = a.bye || b.bye;
        const winner = pickWinner(a, b);
        cur.push({
          id: `lb-${lr}-${i / 2}`, bracket: "lb", round: lr, matchNum: i / 2 + 1,
          teamA: a, teamB: b, winner, ...genScore(winner, "lb", isBye), isBye,
        });
      }
    }
    lbRounds.push(cur);
  }

  // ---- Grand Final ----
  const wbF = wbRounds[wbRounds.length - 1][0];
  const lbF = lbRounds[lbRounds.length - 1][0];
  const gfA = wbF.winner === "A" ? wbF.teamA : wbF.teamB;
  const gfB = lbF.winner === "A" ? lbF.teamA : lbF.teamB;
  const gfWinner = pickWinner(gfA, gfB);
  const gf = {
    id: "gf", bracket: "gf", round: 1, matchNum: 1,
    teamA: gfA, teamB: gfB, winner: gfWinner,
    ...genScore(gfWinner, "gf", false), isBye: false,
  };

  // ---- Apply progress: completed/live/pending status ----
  // Order: WB R1, LB R1, WB R2, LB R2, LB R3, WB R3, LB R4, LB R5, ..., WB Final, LB last-1, LB Final, GF
  // Simplification: WB R1, LB R1, WB R2, LB R2, LB R3, WB R3, LB R4, LB R5, ...
  // Then GF.
  const playOrder = [];
  for (let i = 0; i < wbRoundsCount; i++) {
    playOrder.push({ bracket: "wb", rd: i + 1 });
    if (i === 0) playOrder.push({ bracket: "lb", rd: 1 });
    else {
      playOrder.push({ bracket: "lb", rd: 2 * i });
      if (2 * i + 1 <= totalLBRounds) playOrder.push({ bracket: "lb", rd: 2 * i + 1 });
    }
  }
  playOrder.push({ bracket: "gf", rd: 1 });

  // Assign sequential indices to non-bye matches
  let seq = 0;
  const setStatus = (m, idx, prog, live) => {
    if (m.isBye) { m.status = "bye"; return; }
    if (idx < prog) m.status = "completed";
    else if (idx < prog + live) m.status = "live";
    else m.status = "pending";
  };
  const prog = tournament.progressIndex || 0;
  const live = tournament.liveCount || 0;

  playOrder.forEach(step => {
    let matches;
    if (step.bracket === "wb") matches = wbRounds[step.rd - 1];
    else if (step.bracket === "lb") matches = lbRounds[step.rd - 1];
    else matches = [gf];
    matches.forEach(m => {
      if (m.isBye) m.status = "bye";
      else { setStatus(m, seq, prog, live); seq++; }
    });
  });

  // Assign mock times based on play order
  const startH = 18;
  let timeStep = 0;
  playOrder.forEach(step => {
    let matches;
    if (step.bracket === "wb") matches = wbRounds[step.rd - 1];
    else if (step.bracket === "lb") matches = lbRounds[step.rd - 1];
    else matches = [gf];
    const h = startH + Math.floor(timeStep / 2);
    const min = (timeStep % 2) * 30;
    matches.forEach(m => {
      if (!m.isBye) m.time = `${String(h).padStart(2,"0")}:${String(min).padStart(2,"0")}`;
    });
    timeStep++;
  });

  return {
    size,
    byeCount,
    wbRounds,
    lbRounds,
    gf,
    totalMatches: seq,
    totalCompleted: Math.min(prog, seq),
    totalLive: Math.min(live, Math.max(0, seq - prog)),
  };
};

window.WZN_TOURNAMENTS = { TOURNAMENTS, buildBracket, nextPow2, buildKillraceResults };
