// WZN Store — Supabase + localStorage fallback
const WZN_STORE_KEY = "wzn_s1";

const WZN_Store = (() => {
  // ── localStorage helpers (fallback + cache) ──────────
  const _load = () => {
    try { return JSON.parse(localStorage.getItem(WZN_STORE_KEY) || "{}"); }
    catch { return {}; }
  };
  const _save = (data) => {
    try { localStorage.setItem(WZN_STORE_KEY, JSON.stringify(data)); }
    catch (e) { console.warn("WZN_Store: localStorage feil", e); }
  };

  const _hasDB = () => typeof window.db !== "undefined";

  // ── Auth-headers for API-kall ─────────────────────────
  // Henter brukerens Supabase JWT og returnerer headers klar til bruk i fetch.
  // Erstatter window.__WZN_SECRET__ — ingen hemmeligheter i klientkoden.
  const getAuthHeaders = async () => {
    const base = { "Content-Type": "application/json" };
    if (!_hasDB()) return base;
    try {
      const { data: { session } } = await window.db.auth.getSession();
      if (session?.access_token) base["Authorization"] = `Bearer ${session.access_token}`;
    } catch {}
    return base;
  };

  // ── Submissions ───────────────────────────────────────
  const getSubmissions = async () => {
    if (_hasDB()) {
      const { data, error } = await window.db
        .from("submissions")
        .select("*")
        .eq("status", "pending")
        .order("submitted_at", { ascending: false });
      if (!error) return data;
    }
    return _load().submissions || [];
  };

  const addSubmission = async (sub) => {
    // localStorage alltid for øyeblikkelig UI-respons
    const data = _load();
    data.submissions = [sub, ...(data.submissions || [])];
    _save(data);

    if (_hasDB()) {
      const { error } = await window.db.from("submissions").insert({
        id: sub.id,
        submitted_at: sub.submittedAt,
        submitted_by: sub.submittedBy,
        submitted_by_tag: sub.submittedByTag,
        team_id: sub.teamId,
        tournament_id: sub.tournament_id || null,
        captain: sub.captain,
        tournament: sub.tournament,
        type: sub.type,
        game: sub.game,
        placement: sub.placement,
        players: sub.players,
        screenshot_label: sub.screenshotLabel,
        screenshots: sub.screenshots || null,
        screenshot_paths: sub.screenshot_paths || null,
        flagged: sub.flagged || false,
        flag_reason: sub.flagReason || null,
        status: "pending",
      });
      if (error) console.warn("Supabase addSubmission feil:", error.message);
    }
  };

  // ── Scoring-formler ───────────────────────────────────────────────────────
  // EWC-format (LIGA): points = kills × placement_multiplier
  //   1. plass: ×1.6 | 2–5: ×1.4 | 6–10: ×1.2 | 11–16: ×1.0
  // KILLRACE: 1 point per kill, ingen placement-bonus
  // CMG (2v2): most-kills vinner runden, damage som tiebreaker
  const calcLigaPoints = (kills, placement) => {
    const k = Math.max(0, parseInt(kills) || 0);
    const p = Math.max(1, parseInt(placement) || 1);
    let multiplier = 1.0;
    if (p === 1)       multiplier = 1.6;
    else if (p <= 5)   multiplier = 1.4;
    else if (p <= 10)  multiplier = 1.2;
    // 11+ → ×1.0
    return Math.round(k * multiplier * 10) / 10; // én desimal
  };

  const calcKillracePoints = (kills) => Math.max(0, parseInt(kills) || 0);

  // CMG (2v2 bracket): runden vinnes av laget med flest kills, damage brytes uavgjort.
  // Returnerer "win" | "loss" | "draw". Brukes IKKE til LP — bracket-format.
  const calcCmgResult = (myKills, myDamage, oppKills, oppDamage) => {
    const mk = Math.max(0, parseInt(myKills)  || 0);
    const ok = Math.max(0, parseInt(oppKills) || 0);
    if (mk > ok) return "win";
    if (mk < ok) return "loss";
    const md = Math.max(0, parseInt(myDamage)  || 0);
    const od = Math.max(0, parseInt(oppDamage) || 0);
    if (md > od) return "win";
    if (md < od) return "loss";
    return "draw";
  };

  // Hent turnerings-type for å velge riktig formel
  const getTournamentType = async (tournamentId) => {
    if (!tournamentId || !_hasDB()) return "bracket";
    const { data } = await window.db.from("tournaments")
      .select("type").eq("id", tournamentId).single();
    return data?.type || "bracket";
  };

  const approveSubmission = async (id, teamId, points) => {
    // localStorage
    const data = _load();
    const sub = (data.submissions || []).find(s => s.id === id);
    data.submissions = (data.submissions || []).filter(s => s.id !== id);
    data.approved = [...(data.approved || []), {
      id, teamId, points,
      approvedAt: new Date().toISOString(),
      ...(sub || {}), status: "verified",
    }];
    const bonuses = data.teamBonuses || {};
    bonuses[teamId] = (bonuses[teamId] || 0) + points;
    data.teamBonuses = bonuses;
    _save(data);

    if (_hasDB()) {
      // Auto-beregn poeng basert på turnerings-type hvis ikke overstyrt
      let finalPoints = points;
      if (sub?.tournament_id && (points === undefined || points === null)) {
        const tType = await getTournamentType(sub.tournament_id);
        if (tType === "liga" || tType === "bracket") {
          finalPoints = calcLigaPoints(sub.kills, sub.placement);
        } else if (tType === "killrace") {
          finalPoints = calcKillracePoints(sub.kills);
        } else if (tType === "cmg" || tType === "2v2" || tType === "switcharoo") {
          // CMG er bracket-format — ingen LP per runde. Posisjonsavansement trackes i bracket_matches.
          finalPoints = 0;
        } else {
          finalPoints = sub.points || 0;
        }
      } else {
        finalPoints = points ?? sub?.points ?? 0;
      }

      // Oppdater points i localStorage òg
      const bonuses2 = (_load().teamBonuses || {});
      bonuses2[teamId] = (bonuses2[teamId] || 0) + (finalPoints - (points || 0));
      const d2 = _load();
      d2.teamBonuses = bonuses2;
      _save(d2);

      // dispute_window_until = 10 minutter etter godkjenning
      const disputeWindow = new Date(Date.now() + 10 * 60 * 1000).toISOString();
      await window.db.from("submissions").update({
        status: "verified",
        approved_at: new Date().toISOString(),
        dispute_window_until: disputeWindow,
        points: finalPoints,
      }).eq("id", id);

      const prevBonus = await getTeamBonus(teamId);
      await window.db.from("team_bonuses").upsert({
        team_id: teamId,
        bonus: prevBonus + finalPoints,
        updated_at: new Date().toISOString(),
      }, { onConflict: "team_id" });

      // Notify captain via Discord DM
      if (sub?.submitted_by) {
        const tType = await getTournamentType(sub?.tournament_id);
        const pointsMsg = tType === "liga"
          ? `+${finalPoints} LP (${sub.kills} kills × multiplier plass ${sub.placement})`
          : `+${finalPoints} poeng`;
        await sendKapteinNotification(
          sub.submitted_by,
          `✅ **Score godkjent!**\nDin innsending for **${sub.tournament || "turneringen"}** er verifisert. ${pointsMsg} er lagt til laget ditt. 🎯`
        );
      }

      // Live score-kanal: hent oppdatert standings og post
      if (sub?.tournament_id) {
        try {
          const { data: allSubs } = await window.db
            .from("submissions")
            .select("team_id, submitted_by_tag, kills, points")
            .eq("tournament_id", sub.tournament_id)
            .in("status", ["verified", "approved"]);

          const { data: trnData } = await window.db
            .from("tournaments").select("id, name").eq("id", sub.tournament_id).single();

          // Aggreger standings
          const map = {};
          (allSubs || []).forEach(s => {
            const k = s.team_id || s.submitted_by_tag;
            if (!map[k]) map[k] = { tag: s.submitted_by_tag || "—", name: s.submitted_by_tag || "—", kills: 0, points: 0, games: 0 };
            map[k].kills  += s.kills  || 0;
            map[k].points += s.points || 0;
            map[k].games  += 1;
          });
          const standings = Object.values(map).sort((a,b) => b.points - a.points);

          // Hent evt. lagret meldingID fra localStorage
          const lsKey = `live_score_msg_${sub.tournament_id}`;
          const savedMsgId = localStorage.getItem(lsKey) || null;

          const resp = await fetch("/api/live-score-update", {
            method: "POST",
            headers: await getAuthHeaders(),
            body: JSON.stringify({
              tournament: trnData || { name: sub.tournament || "Turnering" },
              standings,
              messageId: savedMsgId,
            }),
          });
          const result = await resp.json();
          if (result.messageId) localStorage.setItem(lsKey, result.messageId);
        } catch (e) {
          console.warn("live-score-update feil:", e.message);
        }
      }
    }
  };

  // ── Dispute-system ────────────────────────────────────
  // Kaptein kan sende protest innen 10 min etter score-godkjenning.
  // Dispute lagres i `disputes`-tabellen. Admin ser dem i admin-dashboard.
  //
  // SQL:
  //   CREATE TABLE disputes (
  //     id uuid DEFAULT gen_random_uuid() PRIMARY KEY,
  //     created_at timestamp DEFAULT now(),
  //     submission_id uuid REFERENCES submissions(id),
  //     tournament_id uuid,
  //     team_id text,
  //     captain_discord_id text,
  //     reason text NOT NULL,
  //     evidence_url text,
  //     status text DEFAULT 'open',  -- open | resolved | rejected
  //     resolved_by text,
  //     resolution_note text
  //   );
  const submitDispute = async ({ submissionId, tournamentId, teamId, captainDiscordId, reason, evidenceUrl }) => {
    if (!_hasDB()) return { error: "Ingen DB-tilkobling" };
    if (!reason || reason.trim().length < 10) return { error: "Begrunn protesten (minst 10 tegn)" };

    // Sjekk om dispute-vinduet fortsatt er åpent
    const { data: sub } = await window.db
      .from("submissions")
      .select("dispute_window_until, status")
      .eq("id", submissionId)
      .single();

    if (!sub) return { error: "Innsending ikke funnet" };
    if (sub.status !== "verified") return { error: "Kan kun protestere på godkjente innsendinger" };

    if (sub.dispute_window_until && new Date() > new Date(sub.dispute_window_until)) {
      return { error: "Protest-vinduet er stengt (10 min etter godkjenning)" };
    }

    // Sjekk om dispute allerede finnes
    const { data: existing } = await window.db
      .from("disputes")
      .select("id")
      .eq("submission_id", submissionId)
      .eq("status", "open")
      .limit(1);

    if (existing && existing.length > 0) {
      return { error: "Du har allerede sendt inn en protest på denne innsendingen" };
    }

    const { data, error } = await window.db.from("disputes").insert({
      submission_id: submissionId,
      tournament_id: tournamentId,
      team_id: teamId,
      captain_discord_id: captainDiscordId,
      reason: reason.trim(),
      evidence_url: evidenceUrl || null,
    }).select().single();

    if (error) return { error: error.message };
    return { data };
  };

  const getDisputes = async (status = null) => {
    if (!_hasDB()) return [];
    let q = window.db.from("disputes").select("*").order("created_at", { ascending: false });
    if (status) q = q.eq("status", status);
    const { data } = await q;
    return data || [];
  };

  const resolveDispute = async (disputeId, resolution, note = "") => {
    if (!_hasDB()) return;
    await window.db.from("disputes").update({
      status: resolution, // "resolved" eller "rejected"
      resolution_note: note,
    }).eq("id", disputeId);
  };

  const rejectSubmission = async (id, reason) => {
    const data = _load();
    const sub = (data.submissions || []).find(s => s.id === id);
    data.submissions = (data.submissions || []).filter(s => s.id !== id);
    if (sub) {
      data.rejected = [...(data.rejected || []), {
        ...sub, status: "rejected",
        rejectedAt: new Date().toISOString(),
      }];
    }
    _save(data);

    if (_hasDB()) {
      await window.db.from("submissions").update({
        status: "rejected",
        rejected_at: new Date().toISOString(),
        ...(reason ? { flag_reason: reason } : {}),
      }).eq("id", id);

      // Notify captain via Discord DM
      if (sub?.submitted_by) {
        const reasonText = reason ? `\nÅrsak: ${reason}` : "";
        await sendKapteinNotification(
          sub.submitted_by,
          `❌ **Score avvist**\nDin innsending for **${sub.tournament || "turneringen"}** ble ikke godkjent.${reasonText}\nKontakt en admin hvis du mener dette er feil.`
        );
      }
    }
  };

  // ── Team bonuses ──────────────────────────────────────
  const getTeamBonus = async (teamId) => {
    if (_hasDB()) {
      const { data, error } = await window.db
        .from("team_bonuses")
        .select("bonus")
        .eq("team_id", teamId)
        .single();
      if (!error && data) return data.bonus;
    }
    return (_load().teamBonuses || {})[teamId] || 0;
  };

  // Synkron versjon for React useMemo (leser fra localStorage-cache)
  const getTeamBonusSync = (teamId) =>
    (_load().teamBonuses || {})[teamId] || 0;

  const getTopPlayers = async (limit = 10) => {
    if (!window.db) return [];
    try {
      const { data, error } = await window.db
        .from("submissions")
        .select("team_name, kills, placement, discord_name, created_at")
        .eq("status", "approved")
        .order("created_at", { ascending: false })
        .limit(limit * 10);
      if (error || !data) return [];
      // Aggreger per team_name
      const map = {};
      data.forEach(s => {
        const key = s.team_name || "Ukjent";
        if (!map[key]) map[key] = { team: key, kills: 0, games: 0, wins: 0 };
        map[key].kills += (s.kills || 0);
        map[key].games += 1;
        if (s.placement === 1) map[key].wins += 1;
      });
      return Object.values(map)
        .sort((a, b) => b.kills - a.kills)
        .slice(0, limit)
        .map((p, i) => ({ ...p, rank: i + 1, kd: p.games > 0 ? (p.kills / p.games).toFixed(1) : "—" }));
    } catch { return []; }
  };

  // ── History ───────────────────────────────────────────
  const getMyHistory = async (teamId) => {
    if (_hasDB()) {
      const { data, error } = await window.db
        .from("submissions")
        .select("*")
        .eq("team_id", teamId)
        .order("submitted_at", { ascending: false });
      if (!error) return data;
    }
    const data = _load();
    const pending  = (data.submissions || []).filter(s => s.teamId === teamId).map(s => ({...s, status:"pending"}));
    const approved = (data.approved   || []).filter(a => a.teamId === teamId);
    const rejected = (data.rejected   || []).filter(r => r.teamId === teamId);
    return [...pending, ...approved, ...rejected].sort((a, b) => {
      const ta = new Date(b.submittedAt || b.approvedAt || 0).getTime();
      const tb = new Date(a.submittedAt || a.approvedAt || 0).getTime();
      return ta - tb;
    });
  };

  // ── Team registrations ────────────────────────────────
  const addTeamRegistration = async (team) => {
    const data = _load();
    data.pendingTeams = [team, ...(data.pendingTeams || [])];
    _save(data);

    if (_hasDB()) {
      const { error } = await window.db.from("team_registrations").insert({
        id: team.id,
        registered_at: team.registeredAt,
        name: team.name,
        tag: team.tag,
        division: team.division,
        captain: team.captain,
        captain_discord_id: team.captain_discord_id || null,
        discord_id: team.discord_id || null,
        roster: team.roster,
        tournament_id: team.tournament_id || null,
        status: "pending",
      });
      if (error) console.warn("Supabase addTeamRegistration feil:", error.message);
    }
  };

  const clear = () => localStorage.removeItem(WZN_STORE_KEY);

  // ── Player Registrations (solo signup) ────────────────
  const addPlayerSignup = async (player) => {
    if (!_hasDB()) return { error: "Ingen DB-tilkobling" };
    const { data, error } = await window.db.from("player_registrations").insert([{
      discord_id:    player.discord_id    || player.discordId    || null,
      discord_name:  player.discord_name  || player.discordName  || null,
      display_name:  player.display_name  || player.displayName  || "Spiller",
      activision_id: player.activision_id || player.activisionId || null,
      tournament_id: player.tournament_id || player.tournamentId || null,
      role:          player.role          || null,
      level:         player.level         || null,
      comment:       player.comment       || player.notes        || null,
      status:        "looking",
    }]).select().single();

    // Tildel SPILLERE-rollen i Discord ved solo-påmelding
    const discordId = player.discord_id || player.discordId || null;
    if (!error && discordId) {
      getAuthHeaders().then(headers =>
        fetch("/api/assign-role", {
          method: "POST",
          headers,
          body: JSON.stringify({ discordId, role: "SPILLERE" }),
        }).catch(() => {})
      );
    }

    return { data, error };
  };

  const getPlayerSignups = async (tournamentId) => {
    if (!_hasDB()) return [];
    let q = window.db.from("player_registrations").select("*").order("registered_at");
    if (tournamentId) q = q.eq("tournament_id", tournamentId);
    const { data, error } = await q;
    if (error) return [];
    return data || [];
  };

  const getFreeAgents = async (tournamentId) => {
    if (!_hasDB()) return [];
    const { data } = await window.db.from("player_registrations")
      .select("*").eq("tournament_id", tournamentId).eq("status", "looking")
      .order("registered_at");
    return data || [];
  };

  const assignPlayerToTeam = async (playerId, teamId) => {
    if (!_hasDB()) return { error: "Ingen DB-tilkobling" };
    return window.db.from("player_registrations")
      .update({ status: "in_team", team_id: teamId })
      .eq("id", playerId);
  };

  // ── Roster-locks ──────────────────────────────────────────────────────────
  // Låser alle godkjente lag-rosters for en turnering ved turnerings-start.
  // Etter locking kan ikke kaptein endre roster til den aktuelle turneringen.
  //
  // SQL:
  //   ALTER TABLE team_registrations ADD COLUMN IF NOT EXISTS roster_locked_at timestamp;
  //   ALTER TABLE team_registrations ADD COLUMN IF NOT EXISTS roster_locked_tournament_id uuid;
  const lockTournamentRosters = async (tournamentId) => {
    if (!_hasDB()) return { error: "Ingen DB-tilkobling" };
    const now = new Date().toISOString();
    const { data, error } = await window.db
      .from("team_registrations")
      .update({
        roster_locked_at: now,
        roster_locked_tournament_id: tournamentId,
      })
      .eq("tournament_id", tournamentId)
      .eq("status", "approved")
      .select("id");
    if (error) return { error: error.message };
    return { locked: data?.length || 0 };
  };

  const isRosterLocked = async (teamRegId, tournamentId) => {
    if (!_hasDB()) return false;
    const { data } = await window.db
      .from("team_registrations")
      .select("roster_locked_at, roster_locked_tournament_id")
      .eq("id", teamRegId)
      .single();
    return !!(data?.roster_locked_at && data?.roster_locked_tournament_id === tournamentId);
  };

  const lockPlayerForSwitcharoo = async (playerId) => {
    if (!_hasDB()) return { error: "Ingen DB-tilkobling" };
    return window.db.from("player_registrations")
      .update({ status: "locked_switcharoo" })
      .eq("id", playerId);
  };

  // ── Scorecards / Storage ──────────────────────────────
  // Last opp screenshot til Supabase Storage (bucket "scorecards").
  // Returnerer { url, error }. URL kan lagres i submissions.
  const uploadScorecard = async (file, opts = {}) => {
    if (!_hasDB()) return { url: null, error: "Ingen DB-tilkobling" };
    if (!file) return { url: null, error: "Mangler fil" };

    const submissionId = opts.submissionId || `sub-${Date.now()}`;
    const kind = opts.kind || "scoreboard"; // 'scoreboard' | 'placement'
    const ext = (file.name || "").split(".").pop()?.toLowerCase() || "png";
    const path = `${submissionId}/${kind}-${Date.now()}.${ext}`;

    const { data, error } = await window.db.storage
      .from("scorecards")
      .upload(path, file, {
        cacheControl: "31536000",
        upsert: false,
        contentType: file.type || "image/png",
      });

    if (error) {
      console.warn("uploadScorecard feil:", error.message);
      return { url: null, error: error.message };
    }
    // Hent offentlig URL
    const { data: pub } = window.db.storage.from("scorecards").getPublicUrl(data.path);
    return { url: pub.publicUrl, error: null, path: data.path };
  };

  // Slett scorecard (admin etter ferdig sesong)
  const deleteScorecard = async (path) => {
    if (!_hasDB() || !path) return { error: "Mangler path eller DB" };
    return window.db.storage.from("scorecards").remove([path]);
  };

  // ── Turneringer ───────────────────────────────────────
  const getTournaments = async () => {
    if (_hasDB()) {
      const { data, error } = await window.db
        .from("tournaments")
        .select("*")
        .order("start_date", { ascending: true });
      if (!error) return data || [];
    }
    return (_load().tournaments || []);
  };

  const createTournament = async (form) => {
    if (_hasDB()) {
      const { data, error } = await window.db
        .from("tournaments")
        .insert([form])
        .select()
        .single();
      if (!error) {
        const d = _load();
        d.tournaments = [...(d.tournaments || []), data];
        _save(d);
      }
      return { data, error };
    }
    // localStorage fallback
    const d = _load();
    const rec = { ...form, id: crypto.randomUUID(), created_at: new Date().toISOString() };
    d.tournaments = [...(d.tournaments || []), rec];
    _save(d);
    return { data: rec, error: null };
  };

  const updateTournamentStatus = async (id, status, opts = {}) => {
    const d = _load();
    d.tournaments = (d.tournaments || []).map(t => t.id === id ? { ...t, status } : t);
    _save(d);
    if (_hasDB()) {
      await window.db.from("tournaments").update({ status }).eq("id", id);
    }

    // Discord-annonsering ved statusendring
    const tournament = (d.tournaments || []).find(t => t.id === id) || opts.tournament || { id };

    if (status === "live") {
      // Lås alle rosters for denne turneringen
      lockTournamentRosters(id).then(r => {
        if (r?.locked > 0) {
          console.log(`Roster-lock: ${r.locked} lag låst for turnering ${id}`);
        }
      }).catch(e => console.warn("Roster-lock feil:", e.message));
    }

    if (status === "open") {
      getAuthHeaders().then(headers => {
        fetch("/api/announce-tournament", {
          method: "POST", headers,
          body: JSON.stringify({ tournament }),
        }).catch(e => console.warn("announce-tournament feil:", e.message));

        fetch("/api/post-instagram", {
          method: "POST", headers,
          body: JSON.stringify({ type: "announce", tournament }),
        }).catch(e => console.warn("instagram-announce feil:", e.message));
      });
    }

    if (status === "completed") {
      const standings = opts.standings || [];
      getAuthHeaders().then(headers => {
        fetch("/api/announce-result", {
          method: "POST", headers,
          body: JSON.stringify({ tournament, standings }),
        }).catch(e => console.warn("announce-result feil:", e.message));

        fetch("/api/post-instagram", {
          method: "POST", headers,
          body: JSON.stringify({ type: "result", tournament, standings }),
        }).catch(e => console.warn("instagram-result feil:", e.message));
      });
    }
  };

  // ── Lag-godkjenning ───────────────────────────────────
  const getApprovedTeams = async () => {
    if (_hasDB()) {
      const { data } = await window.db
        .from("team_registrations")
        .select("*")
        .eq("status", "approved")
        .order("created_at", { ascending: false });
      return data || [];
    }
    return [];
  };

  const getPendingRegistrations = async () => {
    if (_hasDB()) {
      const { data, error } = await window.db
        .from("team_registrations")
        .select("*")
        .eq("status", "pending")
        .order("registered_at", { ascending: true });
      if (!error) return data || [];
    }
    return (_load().pendingTeams || []);
  };

  const approveTeamRegistration = async (regId, tournamentId) => {
    if (_hasDB()) {
      await window.db.from("team_registrations")
        .update({ status: "approved" })
        .eq("id", regId);
      const { data: reg } = await window.db
        .from("team_registrations").select("*").eq("id", regId).single();
      const { error } = await window.db.from("tournament_teams").insert([{
        tournament_id: tournamentId,
        registration_id: regId,
        team_name: reg.name,
        team_tag: reg.tag,
        captain: reg.captain,
        roster: reg.roster,
      }]);
      if (error) console.warn("approveTeamRegistration feil:", error.message);

      // Tildel SPILLERE-rolle til alle i laget (kaptein + roster).
      // KAPTEIN + LIGA-DELTAKER reserveres til liga-sesongen — alle får
      // bare SPILLERE for vanlige turneringer.
      const captainDiscordId = reg.captain_discord_id || null;
      const assignRole = async (discordId, role) => fetch("/api/assign-role", {
        method: "POST",
        headers: await getAuthHeaders(),
        body: JSON.stringify({ discordId, role }),
      }).catch(e => console.warn(`assign ${role} feil:`, e.message));

      if (captainDiscordId) {
        await assignRole(captainDiscordId, "SPILLERE");
      }
      const roster = reg.roster || [];
      for (const player of roster) {
        if (player.discordId) {
          await assignRole(player.discordId, "SPILLERE");
        }
      }

      return { error };
    }
    // localStorage fallback
    const d = _load();
    d.pendingTeams = (d.pendingTeams || []).filter(t => t.id !== regId);
    _save(d);
    return { error: null };
  };

  const rejectTeamRegistration = async (regId) => {
    if (_hasDB()) {
      return window.db.from("team_registrations")
        .update({ status: "rejected" })
        .eq("id", regId);
    }
    const d = _load();
    d.pendingTeams = (d.pendingTeams || []).filter(t => t.id !== regId);
    _save(d);
  };

  // ── Bracket ───────────────────────────────────────────
  const getTournamentTeams = async (tournamentId) => {
    if (_hasDB()) {
      const { data, error } = await window.db
        .from("tournament_teams")
        .select("*")
        .eq("tournament_id", tournamentId)
        .order("seed");
      if (!error) return data || [];
    }
    return [];
  };

  const generateBracket = async (tournamentId) => {
    if (!_hasDB()) return { error: "Ingen DB-tilkobling" };
    const { data: teams } = await window.db
      .from("tournament_teams")
      .select("*")
      .eq("tournament_id", tournamentId)
      .order("seed");
    if (!teams || teams.length < 2) return { error: "Trenger minst 2 lag" };

    // Sett seed hvis ikke satt
    const seeded = teams.map((t, i) => ({ ...t, seed: t.seed ?? i + 1 }));
    await Promise.all(seeded.map(t =>
      window.db.from("tournament_teams").update({ seed: t.seed }).eq("id", t.id)
    ));

    // Opprett runde-1 winners-bracket kamper
    const matches = [];
    const count = Math.floor(seeded.length / 2);
    for (let i = 0; i < count; i++) {
      matches.push({
        tournament_id: tournamentId,
        round: 1,
        match_number: i + 1,
        side: "winners",
        team1_id: seeded[i * 2].id,
        team2_id: seeded[i * 2 + 1]?.id || null,
        status: "pending",
      });
    }
    // Bye hvis odde antall lag
    if (seeded.length % 2 !== 0) {
      matches.push({
        tournament_id: tournamentId,
        round: 1,
        match_number: count + 1,
        side: "winners",
        team1_id: seeded[seeded.length - 1].id,
        team2_id: null,
        status: "bye",
      });
    }
    const { error } = await window.db.from("bracket_matches").insert(matches);
    return { error, matchCount: matches.length };
  };

  const getBracketMatches = async (tournamentId) => {
    if (_hasDB()) {
      const { data, error } = await window.db
        .from("bracket_matches")
        .select("*, team1:team1_id(*), team2:team2_id(*), winner:winner_id(*)")
        .eq("tournament_id", tournamentId)
        .order("round")
        .order("match_number");
      if (!error) return data || [];
    }
    return [];
  };

  const recordMatchResult = async (matchId, winnerId, loserId, scores) => {
    if (!_hasDB()) return { error: "Ingen DB-tilkobling" };
    await window.db.from("bracket_matches").update({
      winner_id: winnerId,
      team1_score: scores?.team1 || null,
      team2_score: scores?.team2 || null,
      status: "completed",
      played_at: new Date().toISOString(),
    }).eq("id", matchId);
    const { error } = await window.db
      .from("tournament_teams")
      .update({ status: "eliminated_lb" })
      .eq("id", loserId);
    return { error };
  };

  // ── Turneringsoppdatering ─────────────────────────────
  const updateTournament = async (id, fields) => {
    const d = _load();
    d.tournaments = (d.tournaments || []).map(t => t.id === id ? { ...t, ...fields } : t);
    _save(d);
    if (_hasDB()) {
      return window.db.from("tournaments").update(fields).eq("id", id);
    }
  };

  const deleteTournament = async (id) => {
    const d = _load();
    d.tournaments = (d.tournaments || []).filter(t => t.id !== id);
    _save(d);
    if (_hasDB()) {
      return window.db.from("tournaments").delete().eq("id", id);
    }
  };

  // ── Innsendinger per turnering (admin) ────────────────
  const getSubmissionsByTournament = async (tournamentId) => {
    if (!_hasDB()) return [];
    const { data, error } = await window.db
      .from("submissions")
      .select("*")
      .eq("tournament_id", tournamentId)
      .order("submitted_at", { ascending: false });
    if (error) return [];
    return data || [];
  };

  // ── User roles (user_roles-tabell) ───────────────────
  const getUserRoles = async () => {
    if (!_hasDB()) return [];
    const { data, error } = await window.db
      .from("user_roles")
      .select("*")
      .order("created_at", { ascending: true });
    if (error) { console.warn("getUserRoles feil:", error.message); return []; }
    return data || [];
  };

  // Oppdaterer rolle i Supabase + tildeler/fjerner Discord-rolle hvis relevant
  const updateUserRole = async (userId, newRole, discordId) => {
    if (!_hasDB()) return { error: "Ingen DB-tilkobling" };
    const { error } = await window.db
      .from("user_roles")
      .update({ role: newRole, updated_at: new Date().toISOString() })
      .eq("user_id", userId);
    if (error) return { error };

    // Tildel Discord-rolle ved relevante roller
    const DISCORD_ROLE_MAP = {
      player:     "SPILLERE",
      captain:    "KAPTEIN",
      mod:        null,
      admin:      null,
      superadmin: null,
    };
    const discordRole = DISCORD_ROLE_MAP[newRole];
    if (discordId && discordRole) {
      try {
        await fetch("/api/assign-role", {
          method: "POST",
          headers: await getAuthHeaders(),
          body: JSON.stringify({ discordId, role: discordRole }),
        });
      } catch (e) { console.warn("updateUserRole Discord-kall feil:", e.message); }
    }
    return { error: null };
  };

  // Upsert bruker i user_roles (brukes ved login/signup)
  const upsertUserRole = async ({ user_id, discord_id, discord_name, email, display_name }) => {
    if (!_hasDB()) return;
    await window.db.from("user_roles").upsert({
      user_id,
      discord_id:   discord_id   || null,
      discord_name: discord_name || null,
      email:        email        || null,
      display_name: display_name || null,
      updated_at:   new Date().toISOString(),
    }, { onConflict: "user_id", ignoreDuplicates: false });
  };

  // ── Discord DM-notifikasjon til kaptein ───────────────
  const sendKapteinNotification = async (discordId, message) => {
    try {
      await fetch("/api/notify", {
        method: "POST",
        headers: await getAuthHeaders(),
        body: JSON.stringify({ discordId, message }),
      });
    } catch (e) {
      console.warn("sendKapteinNotification feil:", e.message);
    }
  };

  // ── Supabase Realtime ─────────────────────────────────
  // Returnerer en cleanup-funksjon. Kall den i useEffect cleanup.
  // Eksempel:
  //   useEffect(() => WZN_Store.subscribeTeamBonuses(cb), []);
  const subscribeTeamBonuses = (onChange) => {
    if (!_hasDB()) return () => {};
    const channel = window.db
      .channel("team_bonuses_changes")
      .on("postgres_changes", { event: "*", schema: "public", table: "team_bonuses" }, onChange)
      .subscribe();
    return () => window.db.removeChannel(channel);
  };

  const subscribeSubmissions = (onChange) => {
    if (!_hasDB()) return () => {};
    const channel = window.db
      .channel("submissions_changes")
      .on("postgres_changes", { event: "*", schema: "public", table: "submissions" }, onChange)
      .subscribe();
    return () => window.db.removeChannel(channel);
  };

  // Hent godkjente innsendinger for én turnering (leaderboard)
  const getVerifiedSubmissions = async (tournamentId) => {
    if (!_hasDB()) return [];
    const { data, error } = await window.db
      .from("submissions")
      .select("*")
      .eq("tournament_id", tournamentId)
      .in("status", ["verified", "approved"])
      .order("submitted_at", { ascending: true });
    if (error) return [];
    return data || [];
  };

  // Realtime-abonnement på bracket_matches for én turnering
  const subscribeBracketMatches = (tournamentId, onChange) => {
    if (!_hasDB()) return () => {};
    const channel = window.db
      .channel(`bracket_${tournamentId}`)
      .on("postgres_changes", {
        event: "*", schema: "public", table: "bracket_matches",
        filter: `tournament_id=eq.${tournamentId}`,
      }, onChange)
      .subscribe();
    return () => window.db.removeChannel(channel);
  };

  // Realtime-abonnement på submissions for én turnering
  const subscribeTournamentSubmissions = (tournamentId, onChange) => {
    if (!_hasDB()) return () => {};
    const channel = window.db
      .channel(`submissions_${tournamentId}`)
      .on("postgres_changes", {
        event: "*", schema: "public", table: "submissions",
        filter: `tournament_id=eq.${tournamentId}`,
      }, onChange)
      .subscribe();
    return () => window.db.removeChannel(channel);
  };

  // ── Web Push ──────────────────────────────────────────────────────────────
  // Registrerer brukeren for Web Push-varsler.
  // Kall denne etter at brukeren har gitt tillatelse (Notification.requestPermission).
  //
  // VAPID public key hentes fra /api/push?action=vapid-key (eller hardkodes her).
  // Sett VAPID_PUBLIC_KEY i Vercel. Generer nøkler med: npx web-push generate-vapid-keys
  const VAPID_PUBLIC_KEY = window.__VAPID_PUBLIC_KEY__ || null;

  const subscribePush = async () => {
    if (!("serviceWorker" in navigator) || !("PushManager" in window)) {
      return { error: "Web Push støttes ikke i denne nettleseren" };
    }
    if (!VAPID_PUBLIC_KEY) {
      return { error: "VAPID-nøkkel ikke konfigurert" };
    }
    try {
      const permission = await Notification.requestPermission();
      if (permission !== "granted") return { error: "Varsel-tillatelse avslått" };

      const reg = await navigator.serviceWorker.ready;
      const sub = await reg.pushManager.subscribe({
        userVisibleOnly: true,
        applicationServerKey: urlB64ToUint8Array(VAPID_PUBLIC_KEY),
      });

      await fetch("/api/push", {
        method: "POST",
        headers: await getAuthHeaders(),
        body: JSON.stringify({
          action: "subscribe",
          subscription: sub.toJSON(),
          userAgent: navigator.userAgent,
        }),
      });
      return { ok: true, subscription: sub };
    } catch (e) {
      return { error: e.message };
    }
  };

  const unsubscribePush = async () => {
    if (!("serviceWorker" in navigator)) return;
    try {
      const reg = await navigator.serviceWorker.ready;
      const sub = await reg.pushManager.getSubscription();
      if (!sub) return { ok: true };
      await fetch("/api/push", {
        method: "POST",
        headers: await getAuthHeaders(),
        body: JSON.stringify({ action: "unsubscribe", endpoint: sub.endpoint }),
      });
      await sub.unsubscribe();
      return { ok: true };
    } catch (e) {
      return { error: e.message };
    }
  };

  const isPushSubscribed = async () => {
    if (!("serviceWorker" in navigator) || !("PushManager" in window)) return false;
    try {
      const reg = await navigator.serviceWorker.ready;
      const sub = await reg.pushManager.getSubscription();
      return !!sub;
    } catch { return false; }
  };

  // Hjelpefunksjon: konverter base64-VAPID-nøkkel til Uint8Array
  function urlB64ToUint8Array(base64String) {
    const padding = "=".repeat((4 - (base64String.length % 4)) % 4);
    const base64 = (base64String + padding).replace(/-/g, "+").replace(/_/g, "/");
    const raw = atob(base64);
    return new Uint8Array([...raw].map(c => c.charCodeAt(0)));
  }

  return {
    calcLigaPoints, calcKillracePoints, calcCmgResult,
    getSubmissions, addSubmission, approveSubmission, rejectSubmission,
    submitDispute, getDisputes, resolveDispute,
    lockTournamentRosters, isRosterLocked,
    getTeamBonus, getTeamBonusSync, getMyHistory, addTeamRegistration, clear,
    // Turneringer
    getTournaments, createTournament, updateTournamentStatus, updateTournament, deleteTournament,
    // Lag-godkjenning
    getApprovedTeams, getPendingRegistrations, approveTeamRegistration, rejectTeamRegistration,
    // Bracket
    getTournamentTeams, generateBracket, getBracketMatches, recordMatchResult,
    // Storage / scorecards
    uploadScorecard, deleteScorecard,
    // Solo player signup
    addPlayerSignup, getPlayerSignups, getFreeAgents, assignPlayerToTeam, lockPlayerForSwitcharoo,
    // Stats
    getTopPlayers,
    // Admin
    getSubmissionsByTournament,
    // Bruker-roller
    getUserRoles, updateUserRole, upsertUserRole,
    // Notifikasjon
    sendKapteinNotification,
    // Web Push
    subscribePush, unsubscribePush, isPushSubscribed,
    // Realtime subscriptions
    subscribeTeamBonuses, subscribeSubmissions,
    subscribeBracketMatches, subscribeTournamentSubmissions,
    // Leaderboard
    getVerifiedSubmissions,
  };
})();

window.WZN_Store = WZN_Store;
