// mograndom — shared design tokens + tier system + simulated data
// Globals (window): TIERS_VOLTAGE/PANTHEON/STRATA, getTiers, tierFor,
// progressToNextTier, fmtMu, OPPONENTS, CATEGORIES, useCountdown, hashShort

// ─── Tier themes (mirrors uploads/tiers.ts) ──────────────────────────────
const TIERS_VOLTAGE = [
  { id: 'static',      name: 'Static',      short: 'STC', min:    0, max:  500,      color: '#6b7280', glow: '#9ca3af' },
  { id: 'spark',       name: 'Spark',       short: 'SPK', min:  500, max: 1000,      color: '#f59e0b', glow: '#fbbf24' },
  { id: 'surge',       name: 'Surge',       short: 'SRG', min: 1000, max: 1500,      color: '#3b82f6', glow: '#60a5fa' },
  { id: 'pulse',       name: 'Pulse',       short: 'PLS', min: 1500, max: 2000,      color: '#06b6d4', glow: '#22d3ee' },
  { id: 'bolt',        name: 'Bolt',        short: 'BLT', min: 2000, max: 3000,      color: '#a855f7', glow: '#c084fc' },
  { id: 'plasma',      name: 'Plasma',      short: 'PLA', min: 3000, max: 4000,      color: '#ec4899', glow: '#f472b6' },
  { id: 'storm',       name: 'Storm',       short: 'STM', min: 4000, max: 5000,      color: '#ef4444', glow: '#f87171' },
  { id: 'singularity', name: 'Singularity', short: 'SNG', min: 5000, max: Infinity,  color: '#fbbf24', glow: '#fde047' },
];
const TIERS_PANTHEON = [
  { id: 'mortal',    name: 'Mortal',    short: 'MTL', min:    0, max:  500,      color: '#71717a', glow: '#a1a1aa' },
  { id: 'initiate',  name: 'Initiate',  short: 'INI', min:  500, max: 1000,      color: '#84cc16', glow: '#a3e635' },
  { id: 'acolyte',   name: 'Acolyte',   short: 'ACO', min: 1000, max: 1500,      color: '#06b6d4', glow: '#22d3ee' },
  { id: 'champion',  name: 'Champion',  short: 'CHA', min: 1500, max: 2000,      color: '#3b82f6', glow: '#60a5fa' },
  { id: 'paragon',   name: 'Paragon',   short: 'PAR', min: 2000, max: 3000,      color: '#8b5cf6', glow: '#a78bfa' },
  { id: 'mythic',    name: 'Mythic',    short: 'MYT', min: 3000, max: 4000,      color: '#ec4899', glow: '#f472b6' },
  { id: 'legend',    name: 'Legend',    short: 'LGD', min: 4000, max: 5000,      color: '#ef4444', glow: '#f87171' },
  { id: 'apex',      name: 'Apex',      short: 'APX', min: 5000, max: Infinity,  color: '#fbbf24', glow: '#fde047' },
];
const TIERS_STRATA = [
  { id: 'drift',   name: 'Drift',   short: 'DFT', min:    0, max:  500,      color: '#94a3b8', glow: '#cbd5e1' },
  { id: 'vein',    name: 'Vein',    short: 'VEI', min:  500, max: 1000,      color: '#65a30d', glow: '#84cc16' },
  { id: 'ridge',   name: 'Ridge',   short: 'RDG', min: 1000, max: 1500,      color: '#0891b2', glow: '#06b6d4' },
  { id: 'crest',   name: 'Crest',   short: 'CRT', min: 1500, max: 2000,      color: '#2563eb', glow: '#3b82f6' },
  { id: 'summit',  name: 'Summit',  short: 'SMT', min: 2000, max: 3000,      color: '#7c3aed', glow: '#a855f7' },
  { id: 'aurora',  name: 'Aurora',  short: 'AUR', min: 3000, max: 4000,      color: '#db2777', glow: '#ec4899' },
  { id: 'zenith',  name: 'Zenith',  short: 'ZNT', min: 4000, max: 5000,      color: '#dc2626', glow: '#ef4444' },
  { id: 'apex',    name: 'Apex',    short: 'APX', min: 5000, max: Infinity,  color: '#f59e0b', glow: '#fbbf24' },
];

function getTiers(theme) {
  if (theme === 'pantheon') return TIERS_PANTHEON;
  if (theme === 'strata') return TIERS_STRATA;
  return TIERS_VOLTAGE;
}
function tierFor(mu, theme) {
  const list = getTiers(theme);
  return list.find(t => mu >= t.min && mu < t.max) ?? list[list.length - 1];
}
function progressToNextTier(mu, theme) {
  const list = getTiers(theme);
  const current = tierFor(mu, theme);
  const idx = list.indexOf(current);
  const next = idx < list.length - 1 ? list[idx + 1] : null;
  if (!next) return { current, next: null, percent: 1, eloIntoTier: mu - current.min, eloToNext: 0 };
  const span = current.max - current.min;
  const into = mu - current.min;
  return {
    current, next,
    percent: Math.max(0, Math.min(1, into / span)),
    eloIntoTier: Math.floor(into),
    eloToNext: Math.ceil(current.max - mu),
  };
}
const fmtMu = (n) => Math.round(n).toLocaleString();
const hashShort = (seed) => {
  // deterministic hex-looking string for commit hashes
  let h = 0;
  const s = String(seed);
  for (let i = 0; i < s.length; i++) h = (h * 31 + s.charCodeAt(i)) >>> 0;
  let out = '';
  for (let i = 0; i < 8; i++) {
    h = (h * 1664525 + 1013904223) >>> 0;
    out += h.toString(16).padStart(8, '0').slice(0, 4);
  }
  return out.slice(0, 32);
};

// ─── Categories (mirrors 005_seed.sql) ───────────────────────────────────
const CATEGORIES = [
  { id: 'm_18_24', display: 'Men, 18–24',          gender: 'm', age: '18–24' },
  { id: 'm_25_34', display: 'Men, 25–34',          gender: 'm', age: '25–34' },
  { id: 'm_35_44', display: 'Men, 35–44',          gender: 'm', age: '35–44' },
  { id: 'm_45_99', display: 'Men, 45+',            gender: 'm', age: '45+'   },
  { id: 'f_18_24', display: 'Women, 18–24',        gender: 'f', age: '18–24' },
  { id: 'f_25_34', display: 'Women, 25–34',        gender: 'f', age: '25–34' },
  { id: 'f_35_44', display: 'Women, 35–44',        gender: 'f', age: '35–44' },
  { id: 'f_45_99', display: 'Women, 45+',          gender: 'f', age: '45+'   },
  { id: 'x_18_24', display: 'Non-binary, 18–24',   gender: 'x', age: '18–24' },
  { id: 'x_25_34', display: 'Non-binary, 25–34',   gender: 'x', age: '25–34' },
  { id: 'x_35_44', display: 'Non-binary, 35–44',   gender: 'x', age: '35–44' },
  { id: 'x_45_99', display: 'Non-binary, 45+',     gender: 'x', age: '45+'   },
];

// ─── Simulated leaderboard / opponent pool ───────────────────────────────
// Realistic distribution of μ, with display names that don't impersonate.
const OPPONENTS = [
  { id: 'u01', name: 'kestrel',         mu: 5240, phi: 78,  w: 84, l: 14, country: 'JP' },
  { id: 'u02', name: 'haloglass',       mu: 4810, phi: 92,  w: 71, l: 19, country: 'US' },
  { id: 'u03', name: 'vesper.ix',       mu: 4520, phi: 110, w: 58, l: 21, country: 'DE' },
  { id: 'u04', name: 'noor_77',         mu: 4180, phi: 84,  w: 49, l: 19, country: 'AE' },
  { id: 'u05', name: 'cinderlake',      mu: 3940, phi: 99,  w: 41, l: 18, country: 'CA' },
  { id: 'u06', name: 'mireille',        mu: 3760, phi: 121, w: 36, l: 17, country: 'FR' },
  { id: 'u07', name: 'pale.daughter',   mu: 3580, phi: 88,  w: 32, l: 16, country: 'GB' },
  { id: 'u08', name: 'opaline',         mu: 3320, phi: 140, w: 27, l: 15, country: 'IT' },
  { id: 'u09', name: 'thrushwing',      mu: 3110, phi: 105, w: 24, l: 16, country: 'NZ' },
  { id: 'u10', name: 'sable_quartz',    mu: 2980, phi: 118, w: 22, l: 16, country: 'BR' },
  { id: 'u11', name: 'ash.coda',        mu: 2740, phi: 132, w: 19, l: 15, country: 'SE' },
  { id: 'u12', name: 'caladrius',       mu: 2490, phi: 96,  w: 17, l: 14, country: 'PL' },
  { id: 'u13', name: 'magpie.wren',     mu: 2210, phi: 112, w: 15, l: 14, country: 'IE' },
  { id: 'u14', name: 'dust.kit',        mu: 2080, phi: 145, w: 13, l: 13, country: 'KR' },
  { id: 'u15', name: 'umbra__',         mu: 1860, phi: 128, w: 11, l: 12, country: 'AR' },
  { id: 'u16', name: 'glasswing',       mu: 1720, phi: 161, w:  9, l: 11, country: 'NL' },
  { id: 'u17', name: 'mothwood',        mu: 1610, phi: 174, w:  7, l:  9, country: 'ES' },
  { id: 'u18', name: 'iris.minus',      mu: 1480, phi: 188, w:  6, l:  9, country: 'CL' },
  { id: 'u19', name: 'soft.threshold',  mu: 1290, phi: 201, w:  4, l:  8, country: 'PT' },
  { id: 'u20', name: 'low_orbit',       mu: 1110, phi: 222, w:  3, l:  7, country: 'TR' },
  { id: 'u21', name: 'pinned.fern',     mu:  920, phi: 255, w:  2, l:  6, country: 'MX' },
  { id: 'u22', name: 'half-known',      mu:  640, phi: 280, w:  1, l:  5, country: 'GR' },
];

// ─── useInterval / useCountdown ──────────────────────────────────────────
function useInterval(fn, ms) {
  const fnRef = React.useRef(fn);
  fnRef.current = fn;
  React.useEffect(() => {
    if (ms == null) return;
    const id = setInterval(() => fnRef.current(), ms);
    return () => clearInterval(id);
  }, [ms]);
}

// ─── Country flag (text-only, not a real flag) ───────────────────────────
const flagOf = (cc) => {
  const A = 0x1f1e6;
  const a = 'A'.charCodeAt(0);
  if (!cc || cc.length !== 2) return '';
  return String.fromCodePoint(A + cc.charCodeAt(0) - a) + String.fromCodePoint(A + cc.charCodeAt(1) - a);
};

// ─── Generic Tier Chip ───────────────────────────────────────────────────
function TierChip({ tier, size = 'md', muted = false }) {
  const px = size === 'sm' ? 9 : size === 'lg' ? 14 : 11;
  const pad = size === 'sm' ? '2px 6px' : size === 'lg' ? '6px 12px' : '4px 9px';
  return (
    <span style={{
      display: 'inline-flex', alignItems: 'center', gap: 6,
      padding: pad, borderRadius: 4,
      fontFamily: 'JetBrains Mono, monospace', fontSize: px, fontWeight: 600,
      letterSpacing: '0.08em',
      color: muted ? 'rgba(255,255,255,0.78)' : tier.color,
      background: muted ? 'rgba(255,255,255,0.05)' : `color-mix(in oklab, ${tier.color} 12%, transparent)`,
      border: `1px solid ${muted ? 'rgba(255,255,255,0.10)' : `color-mix(in oklab, ${tier.color} 50%, transparent)`}`,
      textTransform: 'uppercase', whiteSpace: 'nowrap',
    }}>
      <span style={{
        width: 5, height: 5, borderRadius: 999, background: tier.color,
        boxShadow: muted ? 'none' : `0 0 8px ${tier.glow}`,
      }} />
      {tier.short} · {tier.name}
    </span>
  );
}

// ─── Simulated cam portrait — abstract animated SVG, never a real face ───
function SimPortrait({ seed = 0, hue = 280, label = 'opponent' }) {
  // A deterministic abstract glyph: drifting blobs + a subtle "sensor" overlay.
  // Explicitly NOT a face. Used only for the simulated opponent panel.
  const t = useTick(60);
  const offset = (t * 0.6 + seed * 17) % 360;
  return (
    <div style={{
      position: 'absolute', inset: 0, overflow: 'hidden',
      background: `radial-gradient(120% 80% at 30% 20%, oklch(0.32 0.08 ${hue}), oklch(0.12 0.03 260) 70%)`,
    }}>
      <svg viewBox="0 0 400 400" preserveAspectRatio="xMidYMid slice" style={{ position: 'absolute', inset: 0, width: '100%', height: '100%' }}>
        <defs>
          <radialGradient id={`g-${seed}`} cx="50%" cy="40%" r="60%">
            <stop offset="0%" stopColor={`oklch(0.7 0.15 ${hue})`} stopOpacity="0.55" />
            <stop offset="60%" stopColor={`oklch(0.4 0.12 ${hue})`} stopOpacity="0.25" />
            <stop offset="100%" stopColor="transparent" />
          </radialGradient>
          <pattern id={`scan-${seed}`} width="4" height="4" patternUnits="userSpaceOnUse">
            <rect width="4" height="2" fill="rgba(255,255,255,0.025)" />
          </pattern>
        </defs>
        <circle cx={200 + Math.cos(offset / 60) * 30} cy={170 + Math.sin(offset / 50) * 20} r="140" fill={`url(#g-${seed})`} />
        <circle cx={120 + Math.cos(offset / 40) * 20} cy={300 + Math.sin(offset / 30) * 14} r="90" fill={`url(#g-${seed})`} opacity="0.7" />
        <rect width="400" height="400" fill={`url(#scan-${seed})`} />
      </svg>
      <div style={{
        position: 'absolute', left: 12, top: 12,
        fontFamily: 'JetBrains Mono, monospace', fontSize: 10, letterSpacing: '0.12em',
        color: 'rgba(255,255,255,0.55)', textTransform: 'uppercase',
      }}>
        sim · {label}
      </div>
      <div style={{
        position: 'absolute', right: 12, top: 12,
        width: 8, height: 8, borderRadius: 999, background: '#ef4444',
        boxShadow: '0 0 12px #ef4444', animation: 'mg-pulse 1.4s ease-in-out infinite',
      }} />
    </div>
  );
}

// shared 60fps tick (returns frame index since mount)
function useTick(fps = 30) {
  const [n, setN] = React.useState(0);
  React.useEffect(() => {
    let raf; let last = performance.now();
    const step = 1000 / fps;
    const loop = (t) => {
      if (t - last >= step) { setN(x => x + 1); last = t; }
      raf = requestAnimationFrame(loop);
    };
    raf = requestAnimationFrame(loop);
    return () => cancelAnimationFrame(raf);
  }, [fps]);
  return n;
}

// ─── Click-driven webcam hook ────────────────────────────────────────────
// Returns { videoRef, state, request }. Caller renders a Request Camera
// button that calls request() — many browsers (and sandboxed previews) only
// surface the permission prompt for direct user gestures.
function useWebcam(autoActive) {
  const videoRef = React.useRef(null);
  const [state, setState] = React.useState('idle'); // idle | requesting | live | denied | unsupported
  const streamRef = React.useRef(null);

  // Re-attach the stream whenever the video element re-mounts (state flips
  // from 'requesting' to 'live' cause a remount; setting srcObject before
  // the element exists silently drops the stream).
  React.useEffect(() => {
    if (state === 'live' && videoRef.current && streamRef.current) {
      if (videoRef.current.srcObject !== streamRef.current) {
        videoRef.current.srcObject = streamRef.current;
      }
    }
  }, [state]);

  const request = React.useCallback(async () => {
    if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
      setState('unsupported'); return;
    }
    setState('requesting');
    try {
      const s = await navigator.mediaDevices.getUserMedia({
        video: { width: 720, height: 720, facingMode: 'user' }, audio: false,
      });
      streamRef.current = s;
      setState('live');
    } catch (e) {
      setState('denied');
    }
  }, []);

  React.useEffect(() => {
    return () => {
      if (streamRef.current) streamRef.current.getTracks().forEach(t => t.stop());
    };
  }, []);

  return { videoRef, state, request };
}

Object.assign(window, {
  TIERS_VOLTAGE, TIERS_PANTHEON, TIERS_STRATA,
  getTiers, tierFor, progressToNextTier, fmtMu, hashShort, flagOf,
  CATEGORIES, OPPONENTS,
  TierChip, SimPortrait, useTick, useInterval, useWebcam,
});
