import { fetchSessionUser, fetchRouletteState, postRouletteSpin } from "../auth/session.js";
import { showToast } from "../components/toast.js";
import { t } from "../config/site.js";

const DEFAULT_WHEEL_ORDER = [
  0, 32, 15, 19, 4, 21, 2, 25, 17, 34, 6, 27, 13, 36, 11, 30, 8, 23, 10, 5, 24, 16, 33, 1, 20, 14, 31, 9, 22, 18,
  29, 7, 28, 12, 35, 3, 26,
];
const DEFAULT_REDS = [1, 3, 5, 7, 9, 12, 14, 16, 18, 19, 21, 23, 25, 27, 30, 32, 34, 36];

const ROULETTE_MUTE_KEY = "roulette_sound_muted";

function fmtCoins(n) {
  const v = Math.max(0, Math.floor(Number(n) || 0));
  try {
    return new Intl.NumberFormat(undefined, { maximumFractionDigits: 0 }).format(v);
  } catch {
    return String(v);
  }
}

const ROULETTE_PAGE_STYLE = `
    .rl-page {
      --rl-accent: #69b1ff;
      --rl-accent-dim: #1677ff;
      --rl-highlight-bright: #dbeafe;
      max-width: 56rem;
      margin-left: auto;
      margin-right: auto;
    }
    @keyframes rlWinPulse {
      0%, 100% { filter: drop-shadow(0 0 0 rgba(22, 119, 255, 0)); }
      50% { filter: drop-shadow(0 0 22px rgba(22, 119, 255, 0.5)); }
    }
    @keyframes rlMarqueeBlink {
      0%, 100% { opacity: 0.35; transform: scale(0.85); }
      50% { opacity: 1; transform: scale(1); }
    }
    @keyframes rlShine {
      0% { opacity: 0.2; transform: translateX(-120%) skewX(-12deg); }
      100% { opacity: 0.35; transform: translateX(120%) skewX(-12deg); }
    }
    .rl-toolbar { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 0.75rem; margin-bottom: 1.25rem; font-size: 0.875rem; }
    .rl-toolbar a {
      color: var(--rl-accent); font-weight: 500;
      display: inline-flex; align-items: center; gap: 0.35rem;
    }
    .rl-toolbar a::before { content: "←"; opacity: 0.7; }
    .rl-toolbar a:hover { color: var(--rl-highlight-bright); text-decoration: underline; }
    .rl-toolbar-tools { display: flex; flex-wrap: wrap; align-items: center; gap: 0.65rem; }
    .rl-sound-toggle {
      font-size: 0.8125rem; padding: 0.4rem 0.75rem; border-radius: 999px;
      border: 1px solid rgba(22,119,255,0.28); background: rgba(0,0,0,0.35); color: #cbd5e1; cursor: pointer;
    }
    .rl-sound-toggle:hover { border-color: rgba(22,119,255,0.5); color: var(--rl-highlight-bright); }
    .rl-balance-pill {
      display: inline-flex; align-items: center; gap: 0.5rem;
      padding: 0.4rem 0.9rem 0.4rem 0.65rem; border-radius: 999px; font-size: 0.8125rem; color: #94a3b8;
      background: linear-gradient(135deg, rgba(30,42,61,0.9) 0%, rgba(8,12,20,0.95) 100%);
      border: 1px solid rgba(22,119,255,0.35);
      box-shadow: inset 0 1px 0 rgba(255,255,255,0.06), 0 4px 16px -6px rgba(0,0,0,0.5);
    }
    .rl-balance-pill__icon {
      width: 1.5rem; height: 1.5rem; border-radius: 50%;
      display: flex; align-items: center; justify-content: center; font-size: 0.75rem;
      background: linear-gradient(145deg, #b8d4f5 0%, var(--rl-accent-dim) 55%, #355a8a 100%);
      color: #0a0e14; font-weight: 800;
    }
    .rl-balance-pill strong { color: var(--rl-highlight-bright); font-weight: 800; font-variant-numeric: tabular-nums; font-size: 0.9375rem; }
    .rl-shell {
      position: relative; border-radius: 1.75rem; overflow: hidden;
      border: 2px solid rgba(22,119,255,0.35);
      background:
        radial-gradient(ellipse 120% 80% at 50% -20%, rgba(22,119,255,0.16), transparent 50%),
        linear-gradient(168deg, #1a2438 0%, #121a28 38%, #080c14 100%);
      box-shadow: 0 32px 64px -24px rgba(0,0,0,0.75), inset 0 1px 0 rgba(255,255,255,0.08);
      padding: 0;
    }
    .rl-marquee {
      display: flex; align-items: center; justify-content: center; flex-wrap: wrap;
      gap: 0.45rem 0.65rem; padding: 0.85rem 1rem 0.8rem; margin: 0; min-height: 3rem;
      background: linear-gradient(180deg, rgba(22,119,255,0.16) 0%, rgba(0,0,0,0.22) 100%);
      border-bottom: 1px solid rgba(22,119,255,0.28);
    }
    .rl-marquee__lights { display: flex; gap: 5px; flex-shrink: 0; }
    .rl-marquee__dot {
      width: 6px; height: 6px; border-radius: 50%;
      background: var(--rl-accent); box-shadow: 0 0 6px var(--rl-accent);
      animation: rlMarqueeBlink 1.2s ease-in-out infinite;
    }
    .rl-marquee__dot:nth-child(2) { animation-delay: 0.15s; }
    .rl-marquee__dot:nth-child(3) { animation-delay: 0.3s; }
    .rl-marquee__dot:nth-child(4) { animation-delay: 0.45s; }
    .rl-marquee__dot:nth-child(5) { animation-delay: 0.6s; }
    @media (max-width: 400px) { .rl-marquee__lights { display: none; } }
    .rl-marquee__title {
      flex: 1 1 auto; min-width: 0; font-size: clamp(0.6875rem, 2.4vw, 0.8125rem);
      font-weight: 800; letter-spacing: 0.08em; line-height: 1.35; text-align: center;
      text-transform: uppercase; color: var(--rl-highlight-bright);
      text-shadow: 0 0 18px rgba(22,119,255,0.4); padding: 0.1rem 0.35rem;
    }
    .rl-shell__inner { padding: 1rem 1rem 1.35rem; }
    @media (min-width: 640px) { .rl-shell__inner { padding: 1.25rem 1.5rem 1.5rem; } }
    .rl-machine-top {
      display: flex; align-items: center; justify-content: space-between; gap: 0.75rem;
      margin-bottom: 0.85rem;
    }
    .rl-badge {
      font-size: 10px; font-weight: 700; letter-spacing: 0.16em; text-transform: uppercase;
      color: #69b1ff; border: 1px solid rgba(22,119,255,0.35); border-radius: 999px; padding: 0.25rem 0.65rem;
      background: rgba(22,119,255,0.1);
    }
    .rl-stage-wrap {
      display: flex; flex-direction: column; align-items: center; gap: 1rem;
    }
    .rl-stage {
      position: relative; width: min(100%, 22rem); aspect-ratio: 1;
      filter: drop-shadow(0 24px 48px rgba(0,0,0,0.55));
    }
    .rl-pointer {
      position: absolute; left: 50%; top: -2px; z-index: 20; transform: translateX(-50%);
      width: 0; height: 0; border-left: 14px solid transparent; border-right: 14px solid transparent;
      border-top: 22px solid #69b1ff;
      filter: drop-shadow(0 3px 6px rgba(0,0,0,0.5));
    }
    .rl-wheel-wrap {
      position: absolute; inset: 10px; border-radius: 50%;
      background: radial-gradient(circle at 35% 30%, rgba(255,255,255,0.12), transparent 42%),
        radial-gradient(circle at 50% 50%, #0f172a 0%, #020617 62%, #000 100%);
      box-shadow:
        inset 0 0 0 3px rgba(22, 119, 255, 0.45),
        inset 0 0 0 8px rgba(15, 23, 42, 0.9),
        0 0 0 1px rgba(255,255,255,0.08);
    }
    .rl-wheel-rot {
      position: absolute; inset: 0; border-radius: 50%;
      transition: none;
      will-change: transform, filter;
    }
    .rl-wheel-rot.rl-wheel-blur {
      filter: blur(0.85px) brightness(1.06);
    }
    @media (prefers-reduced-motion: reduce) {
      .rl-wheel-rot.rl-wheel-blur { filter: none; }
    }
    .rl-wheel-gloss {
      pointer-events: none; position: absolute; inset: 0; border-radius: 50%; z-index: 5;
      background: linear-gradient(125deg, rgba(255,255,255,0.14) 0%, transparent 38%, transparent 58%, rgba(0,0,0,0.35) 100%);
      box-shadow: inset 0 0 40px rgba(0,0,0,0.35);
    }
    .rl-wheel-shine {
      pointer-events: none; position: absolute; inset: 12%; border-radius: 50%; z-index: 6; overflow: hidden; opacity: 0.5;
    }
    .rl-wheel-shine::after {
      content: ""; position: absolute; inset: 0;
      background: linear-gradient(105deg, transparent 0%, rgba(255,255,255,0.06) 45%, transparent 70%);
      animation: rlShine 3.5s ease-in-out infinite;
    }
    .rl-center-cap {
      position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); z-index: 10;
      width: 18%; height: 18%; border-radius: 50%;
      background: radial-gradient(circle at 30% 25%, #475569, #0f172a 70%, #020617);
      box-shadow:
        inset 0 1px 2px rgba(255,255,255,0.2),
        0 4px 14px rgba(0,0,0,0.65);
      border: 1px solid rgba(22, 119, 255, 0.4);
    }
    .rl-msg {
      text-align: center; font-size: 0.9375rem; color: #cbd5e1; min-height: 1.5rem; max-width: 36rem;
    }
    .rl-msg--win { color: #69b1ff; font-weight: 600; }
    .rl-playfield {
      display: grid; gap: 1.25rem; align-items: start;
    }
    @media (min-width: 1024px) {
      .rl-playfield { grid-template-columns: minmax(220px, 280px) minmax(0, 1fr); gap: 1.5rem; }
      .rl-stage-wrap { position: sticky; top: 0.5rem; }
    }
    .rl-felt-wrap { min-width: 0; }
    .rl-felt-head { margin-bottom: 0.5rem; }
    .rl-felt-title {
      font-size: 11px; font-weight: 700; letter-spacing: 0.14em; text-transform: uppercase; color: #94a3b8;
    }
    .rl-felt-hint { margin-top: 0.25rem; font-size: 0.75rem; color: #64748b; line-height: 1.4; }
    .rl-felt {
      border-radius: 12px;
      padding: 7px;
      background: linear-gradient(180deg, #0f172a 0%, #1e293b 35%, #0f172a 100%);
      border: 1px solid rgba(148, 163, 184, 0.28);
      box-shadow: inset 0 1px 0 rgba(255,255,255,0.07), 0 20px 48px -24px rgba(0,0,0,0.75);
    }

    .rl-felt-table {
      display: grid;
      grid-template-columns: 2.65rem repeat(12, minmax(14px, 1fr)) 1.85rem;
      grid-template-rows: repeat(3, minmax(28px, 1fr)) minmax(2.25rem, auto) minmax(2rem, auto);
      gap: 3px;
    }
    @media (min-width: 480px) {
      .rl-felt-table {
        grid-template-columns: 3rem repeat(12, minmax(18px, 1fr)) 2.1rem;
        gap: 4px;
      }
    }
    .rl-felt-spacer {
      pointer-events: none;
      background: rgba(15, 23, 42, 0.65);
      border-radius: 5px;
      border: 1px solid rgba(255,255,255,0.07);
      min-height: 0.35rem;
    }
    .rl-felt-spacer--left { grid-column: 1; grid-row: 4 / span 2; }
    .rl-felt-spacer--right { grid-column: 14; grid-row: 4 / span 2; }
    .rl-cell {
      position: relative; margin: 0; padding: 0; border: none; cursor: pointer;
      font-family: inherit; font-weight: 800; font-size: clamp(0.65rem, 2.1vw, 0.8rem);
      line-height: 1; color: #f8fafc;
      border-radius: 4px;
      border: 1px solid rgba(0,0,0,0.45);
      box-shadow: inset 0 1px 0 rgba(255,255,255,0.12);
      transition: transform 0.12s ease, box-shadow 0.15s ease, filter 0.15s ease;
    }
    .rl-cell:hover:not(:disabled) { filter: brightness(1.12); z-index: 2; }
    .rl-cell:active:not(:disabled) { transform: scale(0.97); }
    .rl-cell:disabled { cursor: not-allowed; opacity: 0.55; }
    .rl-cell-txt { pointer-events: none; }
    .rl-felt-table .rl-zero {
      grid-column: 1;
      grid-row: 1 / span 3;
      border-radius: 7px;
      background: linear-gradient(145deg, #22c55e 0%, #15803d 55%, #14532d 100%);
      font-size: clamp(0.85rem, 2.5vw, 1.05rem);
    }
    .rl-num.rl-red {
      background: linear-gradient(145deg, #ff4545 0%, #cc1818 100%);
    }
    .rl-num.rl-black {
      background: linear-gradient(145deg, #334155 0%, #0f172a 100%);
    }
    .rl-colbet {
      font-size: clamp(0.55rem, 1.8vw, 0.65rem);
      font-weight: 800;
      background: linear-gradient(145deg, #475569 0%, #1e293b 100%);
      color: #e2e8f0;
    }
    .rl-cr1 { grid-column: 14; grid-row: 1; }
    .rl-cr2 { grid-column: 14; grid-row: 2; }
    .rl-cr3 { grid-column: 14; grid-row: 3; }
    .rl-dozen {
      min-height: 2.2rem;
      font-size: clamp(0.58rem, 1.65vw, 0.72rem);
      font-weight: 800;
      letter-spacing: 0.02em;
      border-radius: 6px;
      background: linear-gradient(180deg, #334155 0%, #1e293b 100%);
      color: #f8fafc;
      border: 1px solid rgba(255,255,255,0.14);
      text-transform: uppercase;
    }
    .rl-dz1 { grid-column: 2 / span 4; grid-row: 4; }
    .rl-dz2 { grid-column: 6 / span 4; grid-row: 4; }
    .rl-dz3 { grid-column: 10 / span 4; grid-row: 4; }
    .rl-out {
      min-height: 2rem;
      font-size: clamp(0.55rem, 1.55vw, 0.7rem);
      font-weight: 800;
      letter-spacing: 0.04em;
      border-radius: 5px;
      background: linear-gradient(180deg, #475569 0%, #1e293b 100%);
      color: #f8fafc;
      border: 1px solid rgba(255,255,255,0.12);
      text-transform: uppercase;
    }
    .rl-out-low { grid-column: 2 / span 2; grid-row: 5; }
    .rl-out-even { grid-column: 4 / span 2; grid-row: 5; }
    .rl-out-odd { grid-column: 10 / span 2; grid-row: 5; }
    .rl-out-high { grid-column: 12 / span 2; grid-row: 5; }
    .rl-out-redcell {
      grid-column: 6 / span 2;
      grid-row: 5;
      background: linear-gradient(145deg, #ff4545 0%, #cc1818 100%);
      border: 1px solid rgba(0,0,0,0.35);
    }
    .rl-out-blackcell {
      grid-column: 8 / span 2;
      grid-row: 5;
      background: linear-gradient(145deg, #0f172a 0%, #020617 100%);
      border: 1px solid rgba(255,255,255,0.1);
    }
    .rl-pick {
      box-shadow: 0 0 0 2px #69b1ff, 0 0 18px rgba(22, 119, 255, 0.35) !important;
      z-index: 3;
    }
    .rl-fiche {
      position: absolute; bottom: 3px; left: 50%; transform: translateX(-50%);
      min-width: 1.4rem; min-height: 1.4rem; padding: 0 4px; border-radius: 999px;
      background: linear-gradient(145deg, #91caff, #0958d8);
      border: 2px solid #e8f2fc;
      box-shadow: 0 2px 8px rgba(0,0,0,0.5);
      font-size: 9px; font-weight: 900; color: #181f2e;
      display: flex; align-items: center; justify-content: center;
      pointer-events: none; z-index: 4;
    }
    @keyframes rlWinFlash {
      0%, 100% { box-shadow: 0 0 0 2px #22c55e, 0 0 20px rgba(34, 197, 94, 0.45); }
      50% { box-shadow: 0 0 0 3px #4ade80, 0 0 28px rgba(74, 222, 128, 0.55); }
    }
    .rl-win-flash { animation: rlWinFlash 0.85s ease-in-out 2; z-index: 5; }
    .rl-console {
      margin-top: 1rem; padding: 1rem; border-radius: 1.1rem;
      background: linear-gradient(180deg, rgba(0,0,0,0.35) 0%, rgba(0,0,0,0.15) 100%);
      border: 1px solid rgba(255,255,255,0.06);
    }
    .rl-controls { display: flex; flex-direction: column; gap: 1rem; }
    .rl-bet-row { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 0.75rem; }
    .rl-field { flex: 1; min-width: 8rem; }
    .rl-label { display: block; font-size: 11px; font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase; color: #64748b; margin-bottom: 0.35rem; }
    .rl-input {
      width: 100%; border-radius: 0.75rem; border: 1px solid rgba(22,119,255,0.28);
      background: rgba(0,0,0,0.4); padding: 0.7rem 0.9rem; color: var(--rl-highlight-bright);
      font-size: 1rem; font-weight: 700; font-variant-numeric: tabular-nums; outline: none;
    }
    .rl-input:focus { border-color: rgba(22,119,255,0.5); box-shadow: 0 0 0 3px rgba(22,119,255,0.12); }
    .rl-spin-btn {
      flex: 1; min-width: 10rem; min-height: 3rem;
      border-radius: 1rem; padding: 0.75rem 1.5rem; font-weight: 800; font-size: 1rem;
      letter-spacing: 0.06em; text-transform: uppercase;
      background: linear-gradient(180deg, #7eb8f0 0%, var(--rl-accent-dim) 48%, #355a8a 100%);
      color: #0a0e14; border: 2px solid rgba(22,119,255,0.45);
      box-shadow: 0 12px 32px -8px rgba(22,119,255,0.45);
      cursor: pointer; transition: transform 0.15s ease, filter 0.15s ease;
    }
    .rl-spin-btn:hover:not(:disabled) { filter: brightness(1.06); }
    .rl-spin-btn:active:not(:disabled) { transform: scale(0.98); }
    .rl-spin-btn:disabled { opacity: 0.45; cursor: not-allowed; }
    .rl-chips { display: flex; flex-wrap: wrap; gap: 0.5rem; }
    .rl-chip {
      min-width: 2.75rem; height: 2.75rem; border-radius: 50%; padding: 0;
      display: inline-flex; align-items: center; justify-content: center;
      font-size: 0.75rem; font-weight: 800; font-variant-numeric: tabular-nums;
      background: radial-gradient(circle at 35% 30%, #3d4f68 0%, #1a2435 55%, #0d121c 100%);
      border: 3px dashed rgba(22,119,255,0.35); color: var(--rl-highlight-bright);
      box-shadow: 0 4px 12px rgba(0,0,0,0.35); cursor: pointer;
      transition: transform 0.15s, border-color 0.2s;
    }
    .rl-chip:hover:not(:disabled) {
      transform: translateY(-2px) scale(1.05);
      border-color: rgba(22,119,255,0.6);
    }
    .rl-chip:disabled { opacity: 0.4; cursor: not-allowed; }
    .rl-pay {
      margin-top: 1.25rem; padding: 1.15rem 1rem 0.5rem; border-radius: 1rem;
      background: rgba(0,0,0,0.2); border: 1px solid rgba(255,255,255,0.05);
    }
    .rl-pay-footer { margin-top: 0.85rem; text-align: center; font-size: 0.6875rem; color: #64748b; line-height: 1.55; }
    .rl-pay h3 { font-size: 0.75rem; font-weight: 700; letter-spacing: 0.14em; text-transform: uppercase; color: #64748b; margin-bottom: 0.75rem; }
    .rl-pay-grid { display: grid; gap: 0.5rem; grid-template-columns: repeat(2, 1fr); font-size: 0.8125rem; color: #cbd5e1; }
    @media (min-width: 640px) { .rl-pay-grid { grid-template-columns: repeat(3, 1fr); } }
    .rl-pay-item {
      display: flex; justify-content: space-between; gap: 0.5rem; padding: 0.45rem 0.6rem; border-radius: 0.65rem;
      background: rgba(0,0,0,0.22); border: 1px solid rgba(255,255,255,0.06);
    }
    .rl-pay-item span:last-child { font-weight: 700; color: #69b1ff; font-variant-numeric: tabular-nums; }
    @media (prefers-reduced-motion: reduce) {
      .rl-wheel-rot { transition-duration: 0.35s; }
      .rl-wheel-shine::after { animation: none; opacity: 0.25; }
    }

    /* Light theme */
    [data-theme="light"] .rl-page { --rl-highlight-bright: #0958d8; }
    [data-theme="light"] .rl-toolbar a { color: #1677ff; }
    [data-theme="light"] .rl-toolbar a:hover { color: #0958d8; }
    [data-theme="light"] .rl-shell {
      border-color: rgba(22, 119, 255, 0.3);
      background: linear-gradient(168deg, #fff 0%, #f1f5f9 50%, #e8eef5 100%);
      box-shadow: 0 20px 50px -20px rgba(15, 23, 42, 0.12);
    }
    [data-theme="light"] .rl-marquee {
      background: linear-gradient(180deg, rgba(22,119,255,0.1) 0%, #f8fafc 100%);
    }
    [data-theme="light"] .rl-balance-pill { background: #fff; border-color: rgba(22,119,255,0.28); }
    [data-theme="light"] .rl-balance-pill strong { color: #0958d8; }
    [data-theme="light"] .rl-console { background: #f1f5f9; border-color: rgba(22,119,255,0.12); }
    [data-theme="light"] .rl-badge {
      color: #0958d8;
      border-color: rgba(22, 119, 255, 0.35);
      background: rgba(22, 119, 255, 0.1);
    }
    [data-theme="light"] .rl-limits-hint { color: #64748b; }
    [data-theme="light"] .rl-sound-toggle {
      color: #334155;
      background: #fff;
      border-color: rgba(22, 119, 255, 0.18);
    }
    [data-theme="light"] .rl-sound-toggle:hover {
      color: #0f172a;
      border-color: rgba(22, 119, 255, 0.32);
    }
    [data-theme="light"] .rl-msg { color: #334155; }
    [data-theme="light"] .rl-msg--win { color: #1677ff; }
    [data-theme="light"] .rl-felt-title { color: #475569; }
    [data-theme="light"] .rl-felt-hint { color: #64748b; }
    [data-theme="light"] .rl-label { color: #64748b; }
    [data-theme="light"] .rl-input {
      color: #0f172a;
      background: #fff;
      border-color: rgba(22, 119, 255, 0.22);
    }
    [data-theme="light"] .rl-chip {
      color: #334155;
      background: #f1f5f9;
      border-color: rgba(22, 119, 255, 0.18);
    }
    [data-theme="light"] .rl-chip:hover:not(:disabled) {
      color: #0f172a;
      background: #e2e8f0;
      border-color: rgba(22, 119, 255, 0.35);
    }
    [data-theme="light"] .rl-pay { background: #f8fafc; border-color: rgba(22,119,255,0.1); }
    [data-theme="light"] .rl-pay h3 { color: #64748b; }
    [data-theme="light"] .rl-pay-grid { color: #334155; }
    [data-theme="light"] .rl-pay-item {
      background: #f1f5f9;
      border-color: rgba(22, 119, 255, 0.12);
    }
    [data-theme="light"] .rl-pay-item span:last-child { color: #1677ff; }
    [data-theme="light"] .rl-guest-panel {
      color: #334155;
      background: #fff;
      border-color: rgba(22, 119, 255, 0.16);
    }
    [data-theme="light"] .rl-guest-panel .rl-guest-title { color: #0f172a; }
    [data-theme="light"] .rl-guest-panel .rl-guest-hint { color: #64748b; }
    [data-theme="light"] .rl-error-panel { color: #475569; }
    [data-theme="light"] .rl-toolbar-back { color: #64748b; }
    [data-theme="light"] .rl-toolbar-back:hover { color: #0f172a; }
    [data-theme="light"] .rl-pay-footer { color: #64748b; }
`;

function ensureRouletteStyles() {
  if (typeof document === "undefined") return;
  const id = "roulette-page-styles";
  let el = document.getElementById(id);
  if (!el) {
    el = document.createElement("style");
    el.id = id;
    document.head.appendChild(el);
  }
  if (el.textContent !== ROULETTE_PAGE_STYLE) el.textContent = ROULETTE_PAGE_STYLE;
}

function wedgePath(cx, cy, rOut, rIn, a0deg, a1deg) {
  const rad = Math.PI / 180;
  const a0 = a0deg * rad;
  const a1 = a1deg * rad;
  const x0o = cx + rOut * Math.cos(a0);
  const y0o = cy + rOut * Math.sin(a0);
  const x1o = cx + rOut * Math.cos(a1);
  const y1o = cy + rOut * Math.sin(a1);
  const x0i = cx + rIn * Math.cos(a1);
  const y0i = cy + rIn * Math.sin(a1);
  const x1i = cx + rIn * Math.cos(a0);
  const y1i = cy + rIn * Math.sin(a0);
  const large = a1deg - a0deg > 180 ? 1 : 0;
  return `M ${x0o} ${y0o} A ${rOut} ${rOut} 0 ${large} 1 ${x1o} ${y1o} L ${x0i} ${y0i} A ${rIn} ${rIn} 0 ${large} 0 ${x1i} ${y1i} Z`;
}

function buildWheelSvgInner(wheelOrder, reds) {
  const n = wheelOrder.length || 37;
  const seg = 360 / n;
  const cx = 200;
  const cy = 200;
  const rOut = 198;
  const rIn = 78;
  const rMid = (rOut + rIn) / 2;
  const redSet = new Set(reds);
  const paths = [];
  const labels = [];
  for (let i = 0; i < n; i++) {
    const num = wheelOrder[i];
    const a0 = i * seg - 90;
    const a1 = (i + 1) * seg - 90;
    let fill = "#0f172a";
    if (num === 0) fill = "#15803d";
    else if (redSet.has(num)) fill = "#e01a1a";
    const d = wedgePath(cx, cy, rOut, rIn, a0, a1);
    paths.push(`<path d="${d}" fill="${fill}" stroke="rgba(0,0,0,0.35)" stroke-width="1"/>`);
    const mid = (i + 0.5) * seg - 90;
    const mr = (mid * Math.PI) / 180;
    const tx = cx + rMid * Math.cos(mr);
    const ty = cy + rMid * Math.sin(mr);
    const fs = num >= 10 ? 13 : 15;
    labels.push(
      `<text x="${tx}" y="${ty}" text-anchor="middle" dominant-baseline="middle" fill="rgba(248,250,252,0.95)" font-size="${fs}" font-weight="800" font-family="system-ui,Segoe UI,sans-serif" transform="rotate(${mid + 90} ${tx} ${ty})">${num}</text>`
    );
  }
  return `
    <svg viewBox="0 0 400 400" width="100%" height="100%" aria-hidden="true" shape-rendering="geometricPrecision">
      ${paths.join("")}
      ${labels.join("")}
    </svg>
  `;
}

function nextRotation(current, winAngleDeg, fullSpins = 5) {
  const α = ((winAngleDeg % 360) + 360) % 360;
  const cur = ((current % 360) + 360) % 360;
  let delta = (-α - cur) % 360;
  if (delta < 0) delta += 360;
  if (delta === 0) delta = 360;
  return current + fullSpins * 360 + delta;
}

function errMsg(data) {
  const e = data?.error;
  if (e === "coins") return t("bjErrCoins");
  if (e === "bet_range")
    return t("rouletteErrBetRange")
      .replace("{min}", String(data?.min ?? 1))
      .replace("{max}", String(data?.max ?? 1000));
  return data?.message || t("rouletteErrGeneric");
}

let audioCtx = null;

function isSoundMuted() {
  try {
    return globalThis.localStorage?.getItem(ROULETTE_MUTE_KEY) === "1";
  } catch {
    return false;
  }
}

function setSoundMuted(muted) {
  try {
    globalThis.localStorage?.setItem(ROULETTE_MUTE_KEY, muted ? "1" : "0");
  } catch {

  }
}

function getAudioCtx() {
  if (typeof globalThis.AudioContext === "undefined" && typeof globalThis.webkitAudioContext === "undefined") {
    return null;
  }
  if (!audioCtx) {
    const Ctx = globalThis.AudioContext || globalThis.webkitAudioContext;
    audioCtx = new Ctx();
  }
  return audioCtx;
}

async function unlockAudio() {
  const ctx = getAudioCtx();
  if (!ctx) return;
  try {
    if (ctx.state === "suspended") await ctx.resume();
  } catch {

  }
}

function playTone(freq, dur, vol = 0.06, type = "sine", when = 0) {
  const ctx = getAudioCtx();
  if (!ctx || isSoundMuted()) return;
  const t0 = ctx.currentTime + when;
  const osc = ctx.createOscillator();
  const g = ctx.createGain();
  osc.type = type;
  osc.frequency.setValueAtTime(freq, t0);
  g.gain.setValueAtTime(0, t0);
  g.gain.linearRampToValueAtTime(vol, t0 + 0.01);
  g.gain.exponentialRampToValueAtTime(0.0001, t0 + dur);
  osc.connect(g);
  g.connect(ctx.destination);
  osc.start(t0);
  osc.stop(t0 + dur + 0.02);
}

function playNoiseBurst(dur, vol = 0.04, when = 0) {
  const ctx = getAudioCtx();
  if (!ctx || isSoundMuted()) return;
  const t0 = ctx.currentTime + when;
  const len = Math.max(1, Math.floor(ctx.sampleRate * dur));
  const buf = ctx.createBuffer(1, len, ctx.sampleRate);
  const data = buf.getChannelData(0);
  for (let i = 0; i < len; i++) data[i] = (Math.random() * 2 - 1) * (1 - i / len);
  const src = ctx.createBufferSource();
  src.buffer = buf;
  const g = ctx.createGain();
  g.gain.setValueAtTime(0, t0);
  g.gain.linearRampToValueAtTime(vol, t0 + 0.004);
  g.gain.exponentialRampToValueAtTime(0.0001, t0 + dur);
  src.connect(g);
  g.connect(ctx.destination);
  src.start(t0);
  src.stop(t0 + dur);
}

function sfxSpinStart() {
  playNoiseBurst(0.06, 0.07, 0);
  playTone(220, 0.12, 0.07, "triangle", 0.02);
  playTone(165, 0.14, 0.05, "sine", 0.05);
}

function sfxTickWait() {
  playNoiseBurst(0.008, 0.02, 0);
  playTone(380 + Math.random() * 60, 0.016, 0.022, "sine", 0);
}

function sfxTickBall() {
  playNoiseBurst(0.012, 0.034, 0);
  playTone(560 + Math.random() * 140, 0.024, 0.04, "triangle", 0.002);
}

function sfxDecelHum(durationMs) {
  const ctx = getAudioCtx();
  if (!ctx || isSoundMuted()) return;
  const sec = Math.max(0.35, durationMs / 1000);
  const t0 = ctx.currentTime;
  const osc = ctx.createOscillator();
  const g = ctx.createGain();
  osc.type = "sine";
  osc.frequency.setValueAtTime(58, t0);
  osc.frequency.exponentialRampToValueAtTime(26, t0 + sec * 0.92);
  g.gain.setValueAtTime(0, t0);
  g.gain.linearRampToValueAtTime(0.042, t0 + 0.07);
  g.gain.exponentialRampToValueAtTime(0.0001, t0 + sec);
  osc.connect(g);
  g.connect(ctx.destination);
  osc.start(t0);
  osc.stop(t0 + sec + 0.05);
}

function easeOutQuint(t) {
  return 1 - Math.pow(1 - t, 5);
}

function runWheelDecel(el, fromDeg, toDeg, durationMs, isActive) {
  return new Promise((resolve) => {
    if (!el) {
      resolve();
      return;
    }
    const start = performance.now();
    let lastTick = fromDeg;
    function frame(now) {
      if (!isActive()) {
        resolve();
        return;
      }
      const u = Math.min(1, (now - start) / durationMs);
      const angle = fromDeg + (toDeg - fromDeg) * easeOutQuint(u);
      el.style.transform = `rotate(${angle}deg)`;
      if (!isSoundMuted() && Math.abs(angle - lastTick) >= 3.1) {
        sfxTickBall();
        lastTick = angle;
      }
      if (u < 1) {
        requestAnimationFrame(frame);
      } else {
        resolve();
      }
    }
    requestAnimationFrame(frame);
  });
}

function sfxLand() {
  playNoiseBurst(0.04, 0.055, 0);
  playTone(880, 0.06, 0.07, "sine", 0.005);
  playTone(440, 0.08, 0.05, "triangle", 0.04);
}

function sfxWin() {
  playTone(523, 0.1, 0.065, "sine", 0);
  playTone(659, 0.12, 0.055, "sine", 0.08);
  playTone(784, 0.14, 0.05, "sine", 0.16);
}

function sfxLose() {
  playTone(180, 0.14, 0.06, "sawtooth", 0);
  playTone(140, 0.16, 0.05, "sawtooth", 0.08);
}

function prefersReducedMotion() {
  try {
    return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
  } catch {
    return false;
  }
}

function buildSpinPayload(bet, betType, straightNum, dozenCol) {
  const b = { action: "spin", bet, betType };
  if (betType === "straight") b.betValue = straightNum ?? 0;
  else if (betType === "dozen" || betType === "column") b.betValue = dozenCol;
  return b;
}

export async function renderRoulettePage(outlet) {
  ensureRouletteStyles();
  const user = await fetchSessionUser();

  if (!user) {
    outlet.innerHTML = `
      <section class="rl-page px-1 pb-6">
        <div class="rl-guest-panel rounded-2xl border border-white/10 bg-white/[0.03] p-8 text-center">
          <h1 class="rl-guest-title text-2xl font-bold sm:text-3xl">${t("rouletteGuestTitle")}</h1>
          <p class="rl-guest-hint mt-2 text-sm">${t("rouletteGuestHint")}</p>
          <button type="button" id="rl-open-login" class="k-btn k-btn--primary btn-site-primary mt-6 rounded-xl px-5 py-2.5 text-sm font-semibold">${t("bjOpenLogin")}</button>
        </div>
      </section>`;
    outlet.querySelector("#rl-open-login")?.addEventListener("click", () => globalThis.__siteOpenLogin?.());
    return;
  }

  const state = await fetchRouletteState();
  if (!state.ok || !state.data) {
    outlet.innerHTML = `
      <section class="rl-page rl-error-panel px-1 pb-6">
        <p class="text-sm">
          ${t("rouletteLoadErrorBeforeLink")} <a href="#/oyunlar" class="font-semibold text-accent underline">${t("gamesNavigateBack")}</a> ${t("rouletteLoadErrorAfterLink")}
        </p>
      </section>`;
    return;
  }

  const raw = state.data;
  let coins = typeof raw.coins === "number" ? raw.coins : 0;
  const meta = raw.meta && typeof raw.meta === "object" ? raw.meta : {};
  const wheelOrder = Array.isArray(meta.wheelOrder) && meta.wheelOrder.length ? meta.wheelOrder.map(Number) : DEFAULT_WHEEL_ORDER;
  const reds = Array.isArray(meta.reds) && meta.reds.length ? meta.reds.map(Number) : DEFAULT_REDS;
  const limits = {
    min: Number(meta.minBet) || 1,
    max: Number(meta.maxBet) || 1000,
  };
  const betDefault = Math.min(25, Math.max(limits.min, Math.min(limits.max, coins || limits.min)));

  let wheelRotation = 0;
  let animGeneration = 0;

  let betType = "red";
  let straightNum = 17;
  let dozenCol = 1;

  function wheelInnerHtml() {
    return buildWheelSvgInner(wheelOrder, reds);
  }

  function currentRlKey() {
    if (betType === "straight") return `straight:${straightNum}`;
    if (betType === "dozen" || betType === "column") return `${betType}:${dozenCol}`;
    return betType;
  }

  function applyDataRl(rl) {
    const i = rl.indexOf(":");
    if (i === -1) {
      betType =  (rl);
      return;
    }
    const a = rl.slice(0, i);
    const b = rl.slice(i + 1);
    if (a === "straight") {
      betType = "straight";
      straightNum = parseInt(b, 10);
      return;
    }
    if (a === "dozen" || a === "column") {
      betType =  (a);
      dozenCol = Math.max(1, Math.min(3, parseInt(b, 10) || 1));
    }
  }

  function buildFeltHtml() {
    const numCells = [];
    for (let r = 0; r < 3; r++) {
      for (let c = 0; c < 12; c++) {
        const n = 3 * (c + 1) - r;
        const isR = reds.includes(n);
        numCells.push(
          `<button type="button" class="rl-cell rl-num ${isR ? "rl-red" : "rl-black"}" style="grid-column:${c + 2};grid-row:${r + 1}" data-rl="straight:${n}"><span class="rl-cell-txt">${n}</span></button>`
        );
      }
    }
    const colLbl = t("rouletteFeltColShort");
    const ar = (s) =>
      String(s)
        .replace(/&/g, "&amp;")
        .replace(/"/g, "&quot;")
        .replace(/</g, "&lt;");
    const redAria = ar(t("rouletteBetRed"));
    const blackAria = ar(t("rouletteBetBlack"));
    return `
      <div class="rl-felt">
        <div class="rl-felt-table">
          <button type="button" class="rl-cell rl-zero" data-rl="straight:0"><span class="rl-cell-txt">0</span></button>
          ${numCells.join("")}
          <button type="button" class="rl-cell rl-colbet rl-cr1" data-rl="column:3"><span class="rl-cell-txt">${colLbl}</span></button>
          <button type="button" class="rl-cell rl-colbet rl-cr2" data-rl="column:2"><span class="rl-cell-txt">${colLbl}</span></button>
          <button type="button" class="rl-cell rl-colbet rl-cr3" data-rl="column:1"><span class="rl-cell-txt">${colLbl}</span></button>
          <div class="rl-felt-spacer rl-felt-spacer--left" aria-hidden="true"></div>
          <div class="rl-felt-spacer rl-felt-spacer--right" aria-hidden="true"></div>
          <button type="button" class="rl-cell rl-dozen rl-dz1" data-rl="dozen:1">${t("rouletteFeltDozen1Tbl")}</button>
          <button type="button" class="rl-cell rl-dozen rl-dz2" data-rl="dozen:2">${t("rouletteFeltDozen2Tbl")}</button>
          <button type="button" class="rl-cell rl-dozen rl-dz3" data-rl="dozen:3">${t("rouletteFeltDozen3Tbl")}</button>
          <button type="button" class="rl-cell rl-out rl-out-low" data-rl="low">${t("rouletteBetLow")}</button>
          <button type="button" class="rl-cell rl-out rl-out-even" data-rl="even">${t("rouletteFeltEvenLbl")}</button>
          <button type="button" class="rl-cell rl-out rl-out-redcell" data-rl="red" aria-label="${redAria}" title="${redAria}"></button>
          <button type="button" class="rl-cell rl-out rl-out-blackcell" data-rl="black" aria-label="${blackAria}" title="${blackAria}"></button>
          <button type="button" class="rl-cell rl-out rl-out-odd" data-rl="odd">${t("rouletteFeltOddLbl")}</button>
          <button type="button" class="rl-cell rl-out rl-out-high" data-rl="high">${t("rouletteBetHigh")}</button>
        </div>
      </div>`;
  }

  function syncPickVisual() {
    const betInput = outlet.querySelector("#rl-bet");
    let amt = Math.max(
      limits.min,
      Math.min(limits.max, parseInt(String(betInput?.value ?? limits.min), 10) || limits.min)
    );
    if (!Number.isFinite(amt)) amt = limits.min;
    outlet.querySelectorAll(".rl-cell").forEach((el) => {
      el.classList.remove("rl-pick");
      el.querySelectorAll(".rl-fiche").forEach((f) => f.remove());
    });
    const key = currentRlKey();
    const btn = outlet.querySelector(`[data-rl="${key}"]`);
    if (btn) {
      btn.classList.add("rl-pick");
      const chip = document.createElement("span");
      chip.className = "rl-fiche";
      chip.textContent = String(amt);
      btn.appendChild(chip);
    }
  }

  function mountHtml() {
    const svgContent = wheelInnerHtml();
    const payItems = [
      { k: "rouletteBetStraight", v: "36×" },
      { k: "rouletteBetRed", v: "2×" },
      { k: "rouletteBetBlack", v: "2×" },
      { k: "rouletteBetEven", v: "2×" },
      { k: "rouletteBetOdd", v: "2×" },
      { k: "rouletteBetLow", v: "2×" },
      { k: "rouletteBetHigh", v: "2×" },
      { k: "roulettePayDozenShort", v: "3×" },
      { k: "roulettePayColumnShort", v: "3×" },
    ];
    const payHtml = payItems
      .map((row) => `<div class="rl-pay-item"><span>${t(row.k)}</span><span>${row.v}</span></div>`)
      .join("");

    outlet.innerHTML = `
      <div class="rl-page px-1 pb-6">
        <div class="rl-toolbar">
          <a href="#/oyunlar">${t("bjBackToGames")}</a>
          <div class="rl-toolbar-tools">
            <button type="button" id="rl-toggle-sound" class="rl-sound-toggle" title="${t("bjSoundToggle")}">
              ${isSoundMuted() ? t("bjSoundOff") : t("bjSoundOn")}
            </button>
            <div class="rl-balance-pill">
              <span class="rl-balance-pill__icon" aria-hidden="true">₵</span>
              <span>${t("bjBalance")} <strong id="rl-coins-disp">${fmtCoins(coins)}</strong></span>
            </div>
          </div>
        </div>
        <div class="rl-shell">
          <div class="rl-marquee">
            <div class="rl-marquee__lights">${[1, 2, 3, 4, 5].map(() => `<span class="rl-marquee__dot"></span>`).join("")}</div>
            <span class="rl-marquee__title">${t("rouletteGuestTitle")}</span>
            <div class="rl-marquee__lights">${[1, 2, 3, 4, 5].map(() => `<span class="rl-marquee__dot"></span>`).join("")}</div>
          </div>
          <div class="rl-shell__inner">
          <div class="rl-machine-top">
            <span class="rl-badge">${t("rouletteBadgeSuper")}</span>
            <span class="rl-limits-hint">${t("rouletteLimitsHint").replace("{min}", String(limits.min)).replace("{max}", String(limits.max))}</span>
          </div>
          <div class="rl-playfield">
            <div class="rl-stage-wrap">
              <div class="rl-stage" aria-label="Roulette">
                <div class="rl-pointer" title="${t("roulettePointer")}"></div>
                <div class="rl-wheel-wrap">
                  <div class="rl-wheel-rot" id="rl-wheel-rot" style="transform: rotate(${wheelRotation}deg)">
                    ${svgContent}
                  </div>
                  <div class="rl-wheel-gloss" aria-hidden="true"></div>
                  <div class="rl-wheel-shine" aria-hidden="true"></div>
                  <div class="rl-center-cap" aria-hidden="true"></div>
                </div>
              </div>
              <p class="rl-msg" id="rl-msg"></p>
            </div>
            <div class="rl-felt-wrap">
              <div class="rl-felt-head">
                <p class="rl-felt-title">${t("rouletteFeltTitle")}</p>
                <p class="rl-felt-hint">${t("rouletteFeltHint")}</p>
              </div>
              ${buildFeltHtml()}
            </div>
          </div>
          <div class="rl-console">
          <div class="rl-controls">
            <div class="rl-bet-row">
              <div class="rl-field">
                <label for="rl-bet" class="rl-label">${t("rouletteBetLabel")}</label>
                <input type="number" id="rl-bet" class="rl-input" min="${limits.min}" max="${limits.max}" step="1" value="${betDefault}" />
              </div>
              <button type="button" class="rl-spin-btn" id="rl-spin">${t("rouletteSpin")}</button>
            </div>
            <div class="rl-chips">
              ${[10, 25, 50, 100, 250, 500]
                .filter((x) => x <= limits.max)
                .map((x) => `<button type="button" class="rl-chip" data-bet="${x}">${x}</button>`)
                .join("")}
            </div>
          </div>
          </div>
          <div class="rl-pay">
            <h3>${t("roulettePayTitle")}</h3>
            <div class="rl-pay-grid">${payHtml}</div>
            <p class="rl-pay-footer">${t("roulettePayFooter")}</p>
          </div>
          </div>
        </div>
      </div>`;
  }

  mountHtml();
  syncPickVisual();

  function bindUi() {
    const spinBtn = outlet.querySelector("#rl-spin");
    const betInput = outlet.querySelector("#rl-bet");
    const wheelEl = outlet.querySelector("#rl-wheel-rot");
    const msgEl = outlet.querySelector("#rl-msg");
    const coinsEl = outlet.querySelector("#rl-coins-disp");
    const soundBtn = outlet.querySelector("#rl-toggle-sound");

    outlet.querySelector("#rl-toggle-sound")?.addEventListener("click", () => {
      setSoundMuted(!isSoundMuted());
      if (soundBtn) soundBtn.textContent = isSoundMuted() ? t("bjSoundOff") : t("bjSoundOn");
    });

    outlet.querySelectorAll(".rl-chip").forEach((btn) => {
      btn.addEventListener("click", () => {
        const v = Number(btn.getAttribute("data-bet"));
        const el = outlet.querySelector("#rl-bet");
        if (el) el.value = String(Math.min(v, coins, limits.max));
        syncPickVisual();
      });
    });

    betInput?.addEventListener("input", () => syncPickVisual());

    outlet.querySelectorAll(".rl-cell").forEach((btn) => {
      btn.addEventListener("click", () => {
        const rl = btn.getAttribute("data-rl");
        if (!rl) return;
        applyDataRl(rl);
        syncPickVisual();
      });
    });

    spinBtn?.addEventListener("click", async () => {
      const rawBet = betInput?.value ?? "10";
      const bet = Math.max(limits.min, Math.min(limits.max, parseInt(String(rawBet), 10) || 0));
      if (bet < limits.min || bet > limits.max) {
        showToast(t("rouletteToastBet").replace("{min}", String(limits.min)).replace("{max}", String(limits.max)), "error");
        return;
      }
      if (bet > coins) {
        showToast(t("bjErrCoins"), "error");
        return;
      }

      const payload = buildSpinPayload(bet, betType, straightNum, dozenCol);
      const gen = ++animGeneration;
      const chips = outlet.querySelectorAll(".rl-chip");
      spinBtn?.setAttribute("disabled", "disabled");
      betInput?.setAttribute("disabled", "disabled");
      chips.forEach((c) => c.setAttribute("disabled", "disabled"));
      outlet.querySelectorAll(".rl-cell").forEach((b) => b.setAttribute("disabled", "disabled"));
      soundBtn?.setAttribute("disabled", "disabled");
      if (msgEl) {
        msgEl.textContent = t("rouletteSpinning");
        msgEl.classList.remove("rl-msg--win");
      }

      await unlockAudio();
      sfxSpinStart();

      const reduced = prefersReducedMotion();
      let angWait = wheelRotation;
      let waitFr = 0;
      let rafWait = 0;

      function waitTick() {
        if (gen !== animGeneration) return;
        angWait += 11 + Math.random() * 2.6;
        if (wheelEl) {
          wheelEl.style.transform = `rotate(${angWait}deg)`;
          wheelEl.classList.add("rl-wheel-blur");
        }
        waitFr += 1;
        if (!reduced && waitFr % 3 === 0 && !isSoundMuted()) sfxTickWait();
        rafWait = requestAnimationFrame(waitTick);
      }

      if (!reduced) {
        rafWait = requestAnimationFrame(waitTick);
      }

      const out = await postRouletteSpin(payload);

      if (!reduced) {
        cancelAnimationFrame(rafWait);
        wheelRotation = angWait;
      }

      if (!out.ok || gen !== animGeneration) {
        wheelEl?.classList.remove("rl-wheel-blur");
        spinBtn?.removeAttribute("disabled");
        betInput?.removeAttribute("disabled");
        chips.forEach((c) => c.removeAttribute("disabled"));
        outlet.querySelectorAll(".rl-cell").forEach((b) => b.removeAttribute("disabled"));
        soundBtn?.removeAttribute("disabled");
        if (msgEl) msgEl.textContent = "";
        showToast(errMsg(out.data || {}), "error");
        return;
      }

      const data = out.data || {};
      if (typeof data.coins === "number") coins = data.coins;
      const winAngle = typeof data.winAngleDeg === "number" ? data.winAngleDeg : 0;
      const winning = typeof data.winning === "number" ? data.winning : 0;
      const payout = typeof data.payout === "number" ? data.payout : 0;
      const net = typeof data.net === "number" ? data.net : payout - bet;
      const msg = String(data.message || "");

      const fullSpins = reduced ? 1 : 6;
      const nextRot = nextRotation(wheelRotation, winAngle, fullSpins);
      const decelMs = reduced ? 380 : 5200;

      if (!reduced && !isSoundMuted()) sfxDecelHum(decelMs);

      await runWheelDecel(wheelEl, wheelRotation, nextRot, decelMs, () => gen === animGeneration);
      wheelRotation = nextRot;
      wheelEl?.classList.remove("rl-wheel-blur");

      if (gen !== animGeneration) return;

      sfxLand();
      if (payout > 0) sfxWin();
      else sfxLose();

      if (coinsEl) coinsEl.textContent = fmtCoins(coins);
      if (msgEl) {
        msgEl.textContent = t("rouletteWinMsg")
          .replace("{msg}", msg)
          .replace("{num}", String(winning))
          .replace("{payout}", String(payout))
          .replace("{net}", String(net));
        msgEl.classList.toggle("rl-msg--win", payout > 0);
      }
      if (payout > 0) {
        showToast(t("rouletteToastWin").replace("{payout}", String(payout)), "success");
      }

      const winCell = outlet.querySelector(`[data-rl="straight:${winning}"]`);
      winCell?.classList.add("rl-win-flash");
      window.setTimeout(() => winCell?.classList.remove("rl-win-flash"), 1800);

      spinBtn?.removeAttribute("disabled");
      betInput?.removeAttribute("disabled");
      chips.forEach((c) => c.removeAttribute("disabled"));
      outlet.querySelectorAll(".rl-cell").forEach((b) => b.removeAttribute("disabled"));
      soundBtn?.removeAttribute("disabled");
      syncPickVisual();

      void globalThis.__siteRefreshUser?.();
    });
  }

  bindUi();
}
