// Identity helpers + pure formatters. No JSX. Loaded early so every
// later module can rely on the globals these export.

const REQUIRED_VOTES = 6;
const ACCEPT_THRESHOLD = 0.9;

const playerByIdent = (id) => Store.get().players.find(p => p.identifier === id);
const isVerifiedArtist = (id) => Store.get().verifiedSet.has(id);

// Eligibility helpers. The catalog page lists rules; these implement
// them. `canVote` is the gate for the review queue; `canSubmit` is
// the gate for the upload form; `canForceAccept` is the admin
// override that bypasses the vote threshold.
const canVote        = (p) => !!p && (p.group !== 'user' || isVerifiedArtist(p.identifier));
const canSubmit      = (p) => !!p && p.playtime >= 100 && !p.banned && !p.blocked;
const hasArtistProfile = (p) => !!p && !!p.artistProfile;
const canForceAccept = (p) => !!p && p.group === 'admin';
const isUserOnly     = (p) => !!p && p.group === 'user' && !isVerifiedArtist(p.identifier);
const isBanned       = (p) => !!p && !!p.banned;
const isBlocked      = (p) => !!p && !!p.blocked;

// Tally a submission's vote breakdown. `meetsAuto` is the auto-accept
// gate — 6+ votes AND 90%+ approval. Used both by the queue UI to
// render the progress bar AND by the server to decide when to flip
// a track to 'accepted'.
function tally(sub) {
  const accepts = sub.votes.filter(v => v.vote === 'accept').length;
  const rejects = sub.votes.filter(v => v.vote === 'reject').length;
  const total = accepts + rejects;
  const ratio = total === 0 ? 0 : accepts / total;
  return { accepts, rejects, total, ratio, meetsAuto: total >= REQUIRED_VOTES && ratio >= ACCEPT_THRESHOLD };
}

// Localised relative time in German — matches the lyrics-transcription
// worker's `timeAgoDe` so the same vocabulary appears on both sides
// of the system. "gerade eben" / "vor 5 min" / "gestern" / etc.
//
// For dates >30 days back, we drop into an absolute date format.
// Same-year entries render as "5. Mai"; cross-year entries include
// the year ("5. Mai 2025") so old audit-log rows aren't ambiguous.
function timeAgo(ts) {
  if (!ts) return '—';
  const s = Math.floor((Date.now() - ts) / 1000);
  if (s < 0)        return 'gerade';
  if (s < 60)       return 'gerade eben';
  if (s < 3600)     return `vor ${Math.floor(s/60)} min`;
  if (s < 86400)    return `vor ${Math.floor(s/3600)} h`;
  if (s < 2*86400)  return 'gestern';
  if (s < 7*86400)  return `vor ${Math.floor(s/86400)} Tagen`;
  if (s < 30*86400) return `vor ${Math.floor(s/(7*86400))} Woche${Math.floor(s/(7*86400)) === 1 ? '' : 'n'}`;
  const then = new Date(ts);
  const now  = new Date();
  const fmt  = then.getFullYear() === now.getFullYear()
    ? { day: 'numeric', month: 'short' }
    : { day: 'numeric', month: 'short', year: 'numeric' };
  return then.toLocaleDateString('de-DE', fmt);
}

// mm:ss for track positions / durations. Tabular-num is applied at
// the callsite via inline style — fmt itself just produces the text.
function fmt(s) {
  s = Math.max(0, Math.floor(s));
  const m = Math.floor(s / 60), ss = s % 60;
  return m + ':' + (ss < 10 ? '0' : '') + ss;
}

// European number formatting — '12.450' with thousands dot. Used for
// streams / likes / play counts in stats and home rails.
function numDE(n) {
  if (n == null || !isFinite(n)) return '0';
  return Number(n).toLocaleString('de-DE');
}

// Deterministic avatar palette from a player identifier. Two-colour
// gradient drawn from a fixed Beachy-Lila-flavoured set. Same input
// always produces the same colours — so a player's avatar stays
// recognisable across screens even when the server doesn't ship a
// `player.avatar` field.
function fallbackAvatar(identifier) {
  const palettes = [
    ['#A972F4', '#FF7E8B'],   // lila ↔ coral
    ['#6EE0D2', '#2BB0C8'],   // aqua ↔ deep
    ['#FFD27A', '#FF7E8B'],   // gold ↔ coral
    ['#E94CD0', '#8A4FE2'],   // magenta ↔ lila
    ['#6EE0D2', '#8FB7FF'],   // aqua ↔ info-blue
    ['#FFC267', '#FF7E8B'],   // warning ↔ coral
    ['#A972F4', '#2BB0C8'],   // lila ↔ deep aqua
    ['#FFD27A', '#FFB48A'],   // gold ↔ peach
    ['#8FB7FF', '#A972F4'],   // info ↔ lila
    ['#5FD8A6', '#8A4FE2'],   // success ↔ lila
  ];
  let h = 0;
  for (let i = 0; i < (identifier || '').length; i++) {
    h = ((h << 5) - h + identifier.charCodeAt(i)) | 0;
  }
  return palettes[Math.abs(h) % palettes.length];
}

Object.assign(window, {
  REQUIRED_VOTES, ACCEPT_THRESHOLD,
  playerByIdent, isVerifiedArtist,
  canVote, canSubmit, hasArtistProfile, canForceAccept, isUserOnly, isBanned, isBlocked,
  tally, timeAgo, fmt, numDE, fallbackAvatar,
});
