"use client";

import Image from "next/image";
import Link from "next/link";
import type { CSSProperties, ReactNode } from "react";
import { useEffect, useRef, useState } from "react";
import {
  ArrowLeft,
  BarChart3,
  Check,
  Copy,
  CreditCard,
  Loader2,
  Settings,
  ShieldCheck,
  Sparkles,
  Star,
  X
} from "lucide-react";
import { CasinoStakeInput } from "@/components/casino/casino-stake-input";
import { GameDetailsPanel } from "@/components/casino/game-details-panel";
import { StakeLimitNote } from "@/components/casino/stake-limit-note";
import { formatAmountInput, formatAmountInputFixed, parseAmountInput } from "@/lib/amount-input";
import { notifyWalletUpdated } from "@/lib/client-events";
import { miniGames } from "@/lib/casino-games";
import { cn } from "@/lib/utils";

type CardSuit = "spades" | "hearts" | "diamonds" | "clubs";
type Card = {
  suit: CardSuit;
  rank: string;
  code: string;
  hidden?: boolean;
};

type BlackjackHand = {
  id: string;
  cards: Card[];
  stake: string;
  status: "PLAYING" | "STAND" | "BUST" | "BLACKJACK" | "WON" | "LOST" | "PUSH";
  doubled?: boolean;
  payout?: string;
  profit?: string;
  result?: string;
  total: number;
  soft: boolean;
  active: boolean;
};

type BlackjackSideBet = {
  type: "perfectPairs" | "twentyOneThree";
  label: string;
  selection: string;
  stake: string;
  multiplier: string;
  payout: string;
  profit: string;
  status: "WON" | "LOST";
  cards: string[];
};

type BlackjackRound = {
  id: string;
  currency: string;
  stake: string;
  payout: string;
  profit: string;
  multiplier: string;
  chance: string;
  target: string;
  direction: string;
  result: string;
  nonce: number;
  status: "ACTIVE" | "WON" | "LOST" | "VOID";
  serverSeedHash: string;
  createdAt: string;
  dealerCards: Card[];
  dealerTotal: number;
  dealerFinalTotal: number | null;
  dealerResult?: "stand" | "bust" | "blackjack";
  hands: BlackjackHand[];
  sideBets: BlackjackSideBet[];
  activeHandIndex: number;
  availableActions: {
    hit: boolean;
    stand: boolean;
    double: boolean;
    split: boolean;
  };
};

type BlackjackState = {
  config: {
    minStake: string;
    maxStake: string;
    maxSideBet: string;
    maxPayout: string;
    blackjackPayout: string;
    houseEdgeBps: number;
  };
  currency: string;
  seedPair: {
    id: string;
    clientSeed: string;
    serverSeedHash: string;
    nonce: number;
  };
  stats: {
    stake: string;
    profit: string;
    wins: number;
    losses: number;
  };
  activeRound: BlackjackRound | null;
  recentRounds: BlackjackRound[];
};

type ResultFlash = {
  tone: "won" | "lost" | "push";
  title: string;
  amount: string;
};

const DEFAULT_STATE: BlackjackState = {
  config: {
    minStake: "0.01",
    maxStake: "10000",
    maxSideBet: "1000",
    maxPayout: "100000",
    blackjackPayout: "2.5",
    houseEdgeBps: 52
  },
  currency: "EUR",
  seedPair: {
    id: "",
    clientSeed: "",
    serverSeedHash: "",
    nonce: 0
  },
  stats: {
    stake: "0",
    profit: "0",
    wins: 0,
    losses: 0
  },
  activeRound: null,
  recentRounds: []
};

const suitSymbol: Record<CardSuit, string> = {
  spades: "♠",
  hearts: "♥",
  diamonds: "♦",
  clubs: "♣"
};

const actionCopy = {
  hit: { label: "Piocher", icon: "↧", tone: "text-felt" },
  stand: { label: "Rester", icon: "✋", tone: "text-ember" },
  split: { label: "Splitter", icon: "↱", tone: "text-blue-400" },
  double: { label: "Doubler", icon: "▰", tone: "text-gold" }
};

const CARD_DEAL_DURATION_MS = 1360;
const CARD_DEAL_STAGGER_MS = 340;
const DEAL_REVEAL_BUFFER_MS = 140;

export function BlackjackGame() {
  const [state, setState] = useState<BlackjackState>(DEFAULT_STATE);
  const [loading, setLoading] = useState(true);
  const [guest, setGuest] = useState(false);
  const [message, setMessage] = useState("");
  const [stake, setStake] = useState("0");
  const [perfectPairsStake, setPerfectPairsStake] = useState("0");
  const [twentyOneThreeStake, setTwentyOneThreeStake] = useState("0");
  const [tab, setTab] = useState<"standard" | "side">("standard");
  const [busy, setBusy] = useState(false);
  const [lastRound, setLastRound] = useState<BlackjackRound | null>(null);
  const [settingsOpen, setSettingsOpen] = useState(false);
  const [statsOpen, setStatsOpen] = useState(false);
  const [fairnessOpen, setFairnessOpen] = useState(false);
  const [resultFlash, setResultFlash] = useState<ResultFlash | null>(null);
  const [detailsReady, setDetailsReady] = useState(true);
  const [clientSeedInput, setClientSeedInput] = useState("");
  const [revealedSeed, setRevealedSeed] = useState<null | { serverSeed: string; clientSeed: string; nonce: number; serverSeedHash: string }>(null);
  const [verifyInput, setVerifyInput] = useState({ serverSeed: "", clientSeed: "", nonce: "" });
  const [verifyResult, setVerifyResult] = useState<null | { serverSeedHash: string; openingDeal: { player: Card[]; dealer: Card[] } }>(null);
  const detailsTimerRef = useRef<number | null>(null);
  const resultShowTimerRef = useRef<number | null>(null);
  const resultHideTimerRef = useRef<number | null>(null);

  const activeRound = state.activeRound;
  const displayRound = activeRound ?? lastRound;
  const otherGames = miniGames.filter((game) => game.slug !== "blackjack");
  const baseStakeAmount = toAmount(stake);
  const perfectPairsAmount = toAmount(perfectPairsStake);
  const twentyOneThreeAmount = toAmount(twentyOneThreeStake);
  const sideStakeAmount = perfectPairsAmount + twentyOneThreeAmount;
  const potentialProfit = Math.max(0, baseStakeAmount * 1.5);
  const sideBetPotential = Math.max(0, perfectPairsAmount * 25 + twentyOneThreeAmount * 100);
  const recentRounds = state.recentRounds.slice(0, 9);

  useEffect(() => {
    void loadState();
  }, []);

  useEffect(() => {
    return () => {
      clearWindowTimer(detailsTimerRef.current);
      clearWindowTimer(resultShowTimerRef.current);
      clearWindowTimer(resultHideTimerRef.current);
    };
  }, []);

  async function loadState() {
    try {
      const response = await fetch("/api/casino/blackjack/state", { cache: "no-store" });
      const payload = await response.json();
      if (!response.ok || !payload.ok) {
        const unauthenticated = response.status === 401 || payload.error?.code === "UNAUTHORIZED";
        setGuest(unauthenticated);
        setMessage(unauthenticated ? "Connecte-toi pour jouer au Blackjack." : payload.error?.message ?? "Blackjack indisponible.");
        return;
      }
      setState(payload.data);
      setClientSeedInput(payload.data.seedPair.clientSeed);
      setLastRound(payload.data.activeRound ?? payload.data.recentRounds[0] ?? null);
    } catch {
      setMessage("Impossible de charger Blackjack pour le moment.");
    } finally {
      setLoading(false);
    }
  }

  async function startRound() {
    if (guest) {
      window.location.href = "/login";
      return;
    }
    if (activeRound) {
      setMessage("Termine la main en cours avant de relancer.");
      return;
    }
    const amount = toAmount(stake);
    if (!Number.isFinite(amount) || amount <= 0) {
      setMessage("Saisis une mise valide.");
      return;
    }

    setBusy(true);
    setMessage("");
    setResultFlash(null);
    try {
      const response = await fetch("/api/casino/blackjack/start", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          currency: state.currency,
          stake: amount,
          sideBets: {
            perfectPairs: perfectPairsAmount,
            twentyOneThree: twentyOneThreeAmount
          },
          clientRequestId: crypto.randomUUID()
        })
      });
      const payload = await response.json();
      if (!response.ok || !payload.ok) {
        setMessage(payload.error?.message ?? "Main refusée.");
        return;
      }

      const round: BlackjackRound = payload.data.round;
      const revealDelay = applyRound(round);
      notifyWalletUpdated();
      if (round.status === "ACTIVE") {
        setWindowTimeout(() => setMessage("Cartes distribuées. À toi de jouer."), revealDelay);
      } else {
        scheduleResult(round, revealDelay);
      }
    } catch {
      setMessage("Erreur réseau pendant la distribution.");
    } finally {
      setBusy(false);
    }
  }

  async function playAction(action: keyof BlackjackRound["availableActions"]) {
    if (!activeRound || busy || !activeRound.availableActions[action]) return;

    setBusy(true);
    setMessage("");
    try {
      const response = await fetch("/api/casino/blackjack/action", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ roundId: activeRound.id, action })
      });
      const payload = await response.json();
      if (!response.ok || !payload.ok) {
        setMessage(payload.error?.message ?? "Action refusée.");
        return;
      }

      const round: BlackjackRound = payload.data.round;
      const revealDelay = applyRound(round);
      notifyWalletUpdated();
      if (round.status !== "ACTIVE") {
        scheduleResult(round, revealDelay);
      }
    } catch {
      setMessage("Erreur réseau pendant l'action.");
    } finally {
      setBusy(false);
    }
  }

  function applyRound(round: BlackjackRound) {
    const revealDelay = getRoundRevealDelay(round, state.activeRound ?? lastRound);
    clearWindowTimer(detailsTimerRef.current);
    if (revealDelay > 80) {
      setDetailsReady(false);
      detailsTimerRef.current = setWindowTimeout(() => {
        setDetailsReady(true);
        detailsTimerRef.current = null;
      }, revealDelay);
    } else {
      setDetailsReady(true);
    }

    setState((current) => ({
      ...current,
      seedPair: round.nonce >= current.seedPair.nonce ? { ...current.seedPair, nonce: round.nonce + 1 } : current.seedPair,
      activeRound: round.status === "ACTIVE" ? round : null,
      recentRounds: round.status === "ACTIVE" ? current.recentRounds : upsertRecent(round, current.recentRounds)
    }));
    setLastRound(round);
    return revealDelay;
  }

  function scheduleResult(round: BlackjackRound, delay = 0) {
    clearWindowTimer(resultShowTimerRef.current);
    clearWindowTimer(resultHideTimerRef.current);

    const profit = Number(round.profit);
    const flash: ResultFlash =
      profit > 0
        ? { tone: "won", title: "Gagné", amount: `+ ${formatMoney(profit, round.currency)}` }
        : profit < 0
          ? { tone: "lost", title: "Perdu", amount: `- ${formatMoney(Math.abs(profit), round.currency)}` }
          : { tone: "push", title: "Égalité", amount: formatMoney(0, round.currency) };

    resultShowTimerRef.current = setWindowTimeout(() => {
      setResultFlash(flash);
      resultShowTimerRef.current = null;
      resultHideTimerRef.current = setWindowTimeout(() => {
        setResultFlash(null);
        resultHideTimerRef.current = null;
      }, 1500);
    }, delay);
  }

  async function rotateSeeds() {
    setMessage("");
    try {
      const response = await fetch("/api/casino/blackjack/seeds/rotate", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ clientSeed: clientSeedInput })
      });
      const payload = await response.json();
      if (!response.ok || !payload.ok) {
        setMessage(payload.error?.message ?? "Rotation impossible.");
        return;
      }

      setRevealedSeed(payload.data.previous);
      setState((current) => ({ ...current, seedPair: payload.data.current }));
      setClientSeedInput(payload.data.current.clientSeed);
      setMessage("Nouvelle paire de seeds active.");
    } catch {
      setMessage("Rotation des seeds indisponible.");
    }
  }

  async function verifySeed() {
    try {
      const response = await fetch("/api/casino/blackjack/verify", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(verifyInput)
      });
      const payload = await response.json();
      if (!response.ok || !payload.ok) {
        setMessage(payload.error?.message ?? "Vérification impossible.");
        return;
      }
      setVerifyResult(payload.data);
    } catch {
      setMessage("Vérification indisponible.");
    }
  }

  function halveStake() {
    setStake(formatInputAmount(toAmount(stake) / 2));
  }

  function doubleStake() {
    setStake(formatInputAmount(toAmount(stake) * 2));
  }

  function halvePerfectPairsStake() {
    setPerfectPairsStake(formatInputAmount(toAmount(perfectPairsStake) / 2));
  }

  function doublePerfectPairsStake() {
    setPerfectPairsStake(formatInputAmount(toAmount(perfectPairsStake) * 2));
  }

  function halveTwentyOneThreeStake() {
    setTwentyOneThreeStake(formatInputAmount(toAmount(twentyOneThreeStake) / 2));
  }

  function doubleTwentyOneThreeStake() {
    setTwentyOneThreeStake(formatInputAmount(toAmount(twentyOneThreeStake) * 2));
  }

  if (loading) {
    return (
      <section className="grid min-h-[360px] place-items-center rounded-lg border border-white/10 bg-[#111720]">
        <Loader2 className="h-7 w-7 animate-spin text-felt" />
      </section>
    );
  }

  return (
    <div className="space-y-3.5">
      <div className="flex items-center gap-2">
        <Link href="/casino" className="grid h-9 w-9 place-items-center rounded-lg border border-white/10 bg-white/[0.04] text-slate-200">
          <ArrowLeft className="h-4 w-4" />
        </Link>
        <div className="flex h-9 items-center gap-2 rounded-lg bg-white/[0.055] px-3 text-sm font-black text-felt">
          <CreditCard className="h-4 w-4" />
          Blackjack
        </div>
      </div>

      <section className="overflow-hidden rounded-lg border border-white/10 bg-[#0e1219] shadow-panel">
        <div className="relative border-b border-white/10 bg-[#11161f] p-3 sm:p-4">
          <div className="mb-3 flex items-center gap-1.5 overflow-x-auto pb-1 scrollbar-thin">
            {recentRounds.length ? (
              recentRounds.map((round) => (
                <button
                  key={round.id}
                  className={cn(
                    "h-7 shrink-0 rounded-full px-2.5 text-[11px] font-black",
                    round.status === "WON" ? "bg-felt/[0.12] text-felt" : round.status === "VOID" ? "bg-white/[0.07] text-slate-300" : "bg-ember/[0.12] text-ember"
                  )}
                  onClick={() => setLastRound(round)}
                >
                  {round.status === "WON" ? "+" : round.status === "LOST" ? "-" : "="}
                  {Number(round.multiplier).toLocaleString("fr-FR", { maximumFractionDigits: 2 })}x
                </button>
              ))
            ) : (
              <span className="h-7 rounded-full bg-white/[0.04] px-2.5 pt-1.5 text-[11px] font-bold text-slate-500">Aucune main</span>
            )}
          </div>

          <div className="relative mx-auto max-w-[860px] overflow-hidden rounded-xl border border-white/10 bg-[#10141b] px-3 pb-4 pt-3">
            <DeckStack />

            <div className="grid min-h-[248px] gap-4 sm:min-h-[300px]">
              <HandRow title="Croupier" score={displayRound ? displayRound.dealerFinalTotal ?? displayRound.dealerTotal : null} cards={displayRound?.dealerCards ?? []} align="dealer" revealDetails={detailsReady} />

              <div className="mx-auto my-1 flex max-w-[240px] flex-col items-center gap-1.5 rounded-lg border border-white/10 bg-black/20 px-3 py-2 text-center">
                <span className="text-[9px] font-black uppercase tracking-[0.08em] text-slate-500">Blackjack paie 3:2</span>
                <span className="text-[9px] font-black uppercase tracking-[0.08em] text-slate-500">Le croupier tire à 17</span>
              </div>

              <div className="space-y-2">
                {(displayRound?.hands.length ? displayRound.hands : [null]).map((hand, index) => (
                  <PlayerHandRow key={hand?.id ?? "empty"} hand={hand} index={index} currency={displayRound?.currency ?? state.currency} revealDetails={detailsReady} />
                ))}
              </div>
            </div>

            {resultFlash ? (
              <div className="pointer-events-none absolute inset-0 z-30 grid place-items-center">
                <div
                  className={cn(
                    "animate-blackjack-result-toast min-w-[150px] rounded-xl border px-4 py-3 text-center shadow-[0_18px_50px_rgba(0,0,0,.44)] backdrop-blur-md",
                    resultFlash.tone === "won" && "border-felt/45 bg-felt/[0.16]",
                    resultFlash.tone === "lost" && "border-ember/45 bg-ember/[0.16]",
                    resultFlash.tone === "push" && "border-white/20 bg-slate-400/[0.12]"
                  )}
                >
                  <p className={cn("text-lg font-black", resultFlash.tone === "won" ? "text-felt" : resultFlash.tone === "lost" ? "text-ember" : "text-slate-100")}>
                    {resultFlash.title}
                  </p>
                  <p className="mt-0.5 text-sm font-black text-white">{resultFlash.amount}</p>
                </div>
              </div>
            ) : null}
          </div>

          <div className="mt-3 grid grid-cols-2 gap-2">
            <ActionButton action="hit" disabled={!detailsReady || !activeRound?.availableActions.hit || busy} onClick={() => void playAction("hit")} />
            <ActionButton action="stand" disabled={!detailsReady || !activeRound?.availableActions.stand || busy} onClick={() => void playAction("stand")} />
            <ActionButton action="split" disabled={!detailsReady || !activeRound?.availableActions.split || busy} onClick={() => void playAction("split")} />
            <ActionButton action="double" disabled={!detailsReady || !activeRound?.availableActions.double || busy} onClick={() => void playAction("double")} />
          </div>
        </div>

        <div className="border-b border-white/10 p-3 sm:p-4">
          <button
            onClick={startRound}
            disabled={busy || Boolean(activeRound) || !detailsReady}
            className="grid h-11 w-full place-items-center rounded-lg bg-gradient-to-r from-felt to-emerald-300 text-sm font-black text-ink-950 shadow-[0_16px_36px_rgba(15,191,143,.22)] transition active:scale-[.99] disabled:cursor-not-allowed disabled:opacity-55"
          >
            {busy ? <Loader2 className="h-4 w-4 animate-spin" /> : activeRound ? "Main en cours" : "Pari"}
          </button>
          <StakeLimitNote min={state.config.minStake} max={state.config.maxStake} sideMax={tab === "side" ? state.config.maxSideBet : undefined} currency={state.currency} />

          {message ? <p className="mt-2 rounded-lg border border-white/10 bg-white/[0.035] px-3 py-2 text-xs font-bold text-slate-300">{message}</p> : null}

          <div className="mt-3 space-y-3">
            <MoneyInput
              label="Montant du pari"
              value={stake}
              currency={state.currency}
              disabled={Boolean(activeRound)}
              onChange={setStake}
              onHalf={halveStake}
              onDouble={doubleStake}
            />

            {tab === "side" ? (
              <div className="space-y-3">
                <div className="rounded-lg border border-gold/20 bg-gold/[0.045] p-3">
                  <div className="flex items-center gap-2 text-xs font-black text-slate-200">
                    <ShieldCheck className="h-4 w-4 text-gold" />
                    Mises secondaires
                  </div>
                  <p className="mt-1.5 text-[11px] leading-5 text-slate-500">
                    Paires parfaites paie jusqu'à 25:1. 21+3 paie jusqu'à 100:1 selon les deux cartes joueur et la carte visible croupier.
                  </p>
                </div>
                <MoneyInput
                  label="Paires parfaites"
                  value={perfectPairsStake}
                  currency={state.currency}
                  disabled={Boolean(activeRound)}
                  onChange={setPerfectPairsStake}
                  onHalf={halvePerfectPairsStake}
                  onDouble={doublePerfectPairsStake}
                />
                <MoneyInput
                  label="21 + 3"
                  value={twentyOneThreeStake}
                  currency={state.currency}
                  disabled={Boolean(activeRound)}
                  onChange={setTwentyOneThreeStake}
                  onHalf={halveTwentyOneThreeStake}
                  onDouble={doubleTwentyOneThreeStake}
                />
                <div className="flex items-center justify-between rounded-lg border border-white/10 bg-black/18 px-3 py-2 text-[11px] font-bold text-slate-400">
                  <span>Potentiel side bets</span>
                  <span className="font-black text-gold">{formatMoney(sideBetPotential, state.currency)}</span>
                </div>
                {detailsReady && displayRound?.sideBets.length ? <SideBetResults sideBets={displayRound.sideBets} currency={displayRound.currency} /> : null}
              </div>
            ) : (
              <div>
                <div className="mb-1.5 flex items-center justify-between text-[12px] font-bold text-slate-200">
                  <span>Profit blackjack potentiel</span>
                  <span className="text-slate-500">{formatAmountInputFixed(potentialProfit)} {state.currency}</span>
                </div>
                <div className="flex h-10 items-center gap-2 rounded-lg border border-white/10 bg-black/20 px-3 text-sm font-semibold text-white">
                  <span className="grid h-5 w-5 place-items-center rounded-full bg-felt text-[12px] font-black text-ink-950">€</span>
                  {formatInputAmount(potentialProfit)}
                </div>
              </div>
            )}

            <div className="grid grid-cols-2 gap-1.5 rounded-lg bg-white/[0.055] p-1.5">
              <button onClick={() => setTab("standard")} className={cn("h-9 rounded-md text-xs font-black transition", tab === "standard" ? "bg-[#373d4b] text-white" : "text-slate-400 hover:text-white")}>
                Standard
              </button>
              <button onClick={() => setTab("side")} className={cn("h-9 rounded-md text-xs font-black transition", tab === "side" ? "bg-[#373d4b] text-white" : "text-slate-400 hover:text-white")}>
                Mise secondaire
              </button>
            </div>
          </div>
        </div>

        <div className="relative flex h-12 items-center justify-between px-3 sm:px-4">
          <div className="flex items-center gap-2">
            <IconButton active={settingsOpen} label="Paramètres" onClick={() => setSettingsOpen((value) => !value)} icon={<Settings className="h-4 w-4" />} />
            <IconButton active={statsOpen} label="Stats" onClick={() => setStatsOpen(true)} icon={<BarChart3 className="h-4 w-4" />} />
            <IconButton label="Favori" onClick={() => setMessage("Blackjack ajouté à tes favoris.")} icon={<Star className="h-4 w-4" />} />
          </div>
          <button onClick={() => setFairnessOpen(true)} className="flex items-center gap-1.5 text-xs font-black text-slate-400 underline decoration-slate-600 underline-offset-4">
            Équité
            <Check className="h-3.5 w-3.5 text-felt" />
          </button>

          {settingsOpen ? (
            <div className="absolute bottom-11 left-3 z-20 w-[250px] rounded-lg border border-white/10 bg-[#1b202a] p-2 shadow-panel">
              <SettingRow label="Turbo" />
              <SettingRow label="Silencieux" />
              <SettingRow label="Animations" checked />
            </div>
          ) : null}
        </div>
      </section>

      {fairnessOpen ? (
        <FairnessSheet
          state={state}
          revealedSeed={revealedSeed}
          clientSeedInput={clientSeedInput}
          verifyInput={verifyInput}
          verifyResult={verifyResult}
          setClientSeedInput={setClientSeedInput}
          setVerifyInput={setVerifyInput}
          onRotate={rotateSeeds}
          onVerify={verifySeed}
          onClose={() => setFairnessOpen(false)}
        />
      ) : null}

      <GameDetailsPanel kind="blackjack" rounds={state.recentRounds} currency={state.currency} />
      <MoreGames games={otherGames} />
      {statsOpen ? <StatsModal state={state} onClose={() => setStatsOpen(false)} /> : null}
    </div>
  );
}

function HandRow({ title, score, cards, align, revealDetails = true }: { title: string; score: number | null; cards: Card[]; align: "dealer" | "player"; revealDetails?: boolean }) {
  return (
    <div className={cn("flex min-h-[78px] flex-col gap-2", align === "dealer" ? "items-start" : "items-center")}>
      <div className="flex items-center gap-2">
        <span className="text-[11px] font-black uppercase tracking-[0.08em] text-slate-500">{title}</span>
        {score !== null && revealDetails ? <span className="rounded-md bg-white/[0.07] px-2 py-0.5 text-[11px] font-black text-white">{score}</span> : null}
      </div>
      <div className="flex min-h-[54px] flex-wrap items-center gap-1.5">
        {cards.length ? cards.map((card, index) => <PlayingCard key={`${card.code}-${index}-${card.hidden ? "hidden" : "open"}`} card={card} index={index} target={align} />) : <EmptyCards />}
      </div>
    </div>
  );
}

function PlayerHandRow({ hand, index, currency, revealDetails }: { hand: BlackjackHand | null; index: number; currency: string; revealDetails: boolean }) {
  if (!hand) {
    return <HandRow title="Joueur" score={null} cards={[]} align="player" revealDetails={revealDetails} />;
  }

  return (
    <div
      className={cn(
        "rounded-lg border px-2.5 py-2 transition",
        hand.active ? "border-felt/35 bg-felt/[0.055] shadow-[0_0_24px_rgba(15,191,143,.08)]" : "border-white/10 bg-black/18"
      )}
    >
      <div className="mb-1.5 flex items-center justify-between">
        <div className="flex items-center gap-2">
          <span className="text-[11px] font-black uppercase tracking-[0.08em] text-slate-500">{index > 0 ? `Main ${index + 1}` : "Joueur"}</span>
          {revealDetails ? <span className="rounded-md bg-white/[0.07] px-2 py-0.5 text-[11px] font-black text-white">{hand.total}</span> : null}
          {hand.doubled ? <span className="rounded-md bg-gold/15 px-2 py-0.5 text-[10px] font-black text-gold">Doublé</span> : null}
        </div>
        {revealDetails && hand.status !== "PLAYING" && hand.status !== "STAND" ? <ResultBadge status={hand.status} profit={hand.profit} currency={currency} /> : null}
      </div>
      <div className="flex min-h-[54px] flex-wrap items-center gap-1.5">
        {hand.cards.map((card, cardIndex) => <PlayingCard key={`${hand.id}-${card.code}-${cardIndex}`} card={card} index={cardIndex} target="player" />)}
      </div>
    </div>
  );
}

function DeckStack() {
  return (
    <div aria-hidden="true" className="blackjack-deck-stack pointer-events-none absolute right-3 top-[42%] z-10 h-[58px] w-[44px] -translate-y-1/2 sm:right-5 sm:top-[38%]">
      <div className="absolute left-2 top-2 h-[52px] w-[38px] rounded-md border border-white/10 bg-emerald-950 shadow-[0_16px_30px_rgba(0,0,0,.32)]" />
      <div className="absolute left-1 top-1 h-[52px] w-[38px] rounded-md border border-felt/25 bg-gradient-to-br from-emerald-900 to-felt shadow-[0_16px_35px_rgba(15,191,143,.18)]" />
      <div className="animate-blackjack-deck-pulse absolute left-0 top-0 h-[52px] w-[38px] rounded-md border border-emerald-200/35 bg-gradient-to-br from-emerald-600 via-emerald-800 to-felt shadow-[0_18px_38px_rgba(15,191,143,.28)]">
        <div className="absolute inset-1 rounded border border-white/30" />
        <Sparkles className="absolute left-1/2 top-1/2 h-4 w-4 -translate-x-1/2 -translate-y-1/2 text-white/80" />
      </div>
    </div>
  );
}

function cardDealDelay(target: "dealer" | "player", index: number) {
  return target === "dealer" ? 170 + index * CARD_DEAL_STAGGER_MS : index * CARD_DEAL_STAGGER_MS;
}

function PlayingCard({ card, index, target }: { card: Card; index: number; target: "dealer" | "player" }) {
  const dealClass = target === "dealer" ? "animate-blackjack-deal-dealer" : "animate-blackjack-deal-player";
  const dealStyle = {
    animationDelay: `${cardDealDelay(target, index)}ms`,
    "--deal-x": target === "dealer" ? "150px" : "178px",
    "--deal-y": target === "dealer" ? "18px" : "-84px",
    "--deal-rot": target === "dealer" ? "5deg" : "8deg",
    "--deal-mid-x": target === "dealer" ? "56px" : "66px",
    "--deal-mid-y": target === "dealer" ? "7px" : "-32px",
    "--deal-mid-rot": target === "dealer" ? "2deg" : "3deg"
  } as CSSProperties;

  if (card.hidden) {
    return (
      <div
        className={cn(dealClass, "relative h-[54px] w-[38px] rounded-md border border-felt/35 bg-gradient-to-br from-emerald-800 to-felt shadow-[0_10px_24px_rgba(0,0,0,.28)]")}
        style={dealStyle}
      >
        <div className="absolute inset-1 rounded border border-white/30" />
      </div>
    );
  }

  const red = card.suit === "hearts" || card.suit === "diamonds";
  return (
    <div
      className={cn(dealClass, "relative h-[54px] w-[38px] rounded-md border border-white/75 bg-white px-1 py-1 text-[#111827] shadow-[0_10px_24px_rgba(0,0,0,.28)]")}
      style={dealStyle}
    >
      <div className={cn("text-[12px] font-black leading-none", red ? "text-ember" : "text-[#111827]")}>{card.rank}</div>
      <div className={cn("absolute bottom-1 right-1 text-[15px] leading-none", red ? "text-ember" : "text-[#111827]")}>{suitSymbol[card.suit]}</div>
    </div>
  );
}

function EmptyCards() {
  return (
    <div className="flex gap-1.5 opacity-35">
      <div className="h-[54px] w-[38px] rounded-md border border-dashed border-white/20 bg-white/[0.03]" />
      <div className="h-[54px] w-[38px] rounded-md border border-dashed border-white/20 bg-white/[0.03]" />
    </div>
  );
}

function ActionButton({ action, disabled, onClick }: { action: keyof typeof actionCopy; disabled: boolean; onClick: () => void }) {
  const copy = actionCopy[action];
  return (
    <button
      onClick={onClick}
      disabled={disabled}
      className="flex h-11 items-center justify-between rounded-lg bg-white/[0.055] px-3 text-left text-sm font-bold text-slate-300 transition hover:bg-white/[0.08] disabled:cursor-not-allowed disabled:opacity-35"
    >
      <span>{copy.label}</span>
      <span className={cn("text-lg font-black", copy.tone)}>{copy.icon}</span>
    </button>
  );
}

function SideBetResults({ sideBets, currency }: { sideBets: BlackjackSideBet[]; currency: string }) {
  return (
    <div className="space-y-2">
      {sideBets.map((sideBet) => (
        <div key={sideBet.type} className="rounded-lg border border-white/10 bg-black/18 p-3">
          <div className="flex items-start justify-between gap-3">
            <div className="min-w-0">
              <p className="text-xs font-black text-white">{sideBet.label}</p>
              <p className="mt-0.5 truncate text-[11px] font-bold text-slate-500">{sideBet.selection}</p>
            </div>
            <span
              className={cn(
                "shrink-0 rounded-md px-2 py-0.5 text-[10px] font-black",
                sideBet.status === "WON" ? "bg-felt/[0.12] text-felt" : "bg-ember/[0.12] text-ember"
              )}
            >
              {sideBet.status === "WON" ? `+${formatMoney(sideBet.profit, currency)}` : "Perdu"}
            </span>
          </div>
          <div className="mt-2 flex items-center justify-between text-[11px] font-bold text-slate-400">
            <span>{sideBet.cards.join(" · ")}</span>
            <span className="font-black text-slate-200">{Number(sideBet.multiplier).toLocaleString("fr-FR", { maximumFractionDigits: 2 })}x</span>
          </div>
        </div>
      ))}
    </div>
  );
}

function MoneyInput({
  label,
  value,
  currency,
  disabled,
  onChange,
  onHalf,
  onDouble
}: {
  label: string;
  value: string;
  currency: string;
  disabled: boolean;
  onChange: (value: string) => void;
  onHalf: () => void;
  onDouble: () => void;
}) {
  return (
    <CasinoStakeInput label={label} value={value} currency={currency} disabled={disabled} onChange={onChange} onHalf={onHalf} onDouble={onDouble} />
  );
}

function IconButton({ icon, label, active, onClick }: { icon: ReactNode; label: string; active?: boolean; onClick: () => void }) {
  return (
    <button
      onClick={onClick}
      aria-label={label}
      className={cn("grid h-8 w-8 place-items-center rounded-md text-slate-300 transition hover:bg-white/[0.06] hover:text-white", active && "bg-felt/[0.12] text-felt")}
    >
      {icon}
    </button>
  );
}

function SettingRow({ label, checked = false }: { label: string; checked?: boolean }) {
  return (
    <div className="flex h-10 items-center justify-between rounded-md px-2 text-xs font-bold text-slate-200">
      <span>{label}</span>
      <span className={cn("h-5 w-9 rounded-full p-0.5", checked ? "bg-felt" : "bg-black/50")}>
        <span className={cn("block h-4 w-4 rounded-full bg-white transition", checked && "translate-x-4")} />
      </span>
    </div>
  );
}

function FairnessSheet({
  state,
  revealedSeed,
  clientSeedInput,
  verifyInput,
  verifyResult,
  setClientSeedInput,
  setVerifyInput,
  onRotate,
  onVerify,
  onClose
}: {
  state: BlackjackState;
  revealedSeed: null | { serverSeed: string; clientSeed: string; nonce: number; serverSeedHash: string };
  clientSeedInput: string;
  verifyInput: { serverSeed: string; clientSeed: string; nonce: string };
  verifyResult: null | { serverSeedHash: string; openingDeal: { player: Card[]; dealer: Card[] } };
  setClientSeedInput: (value: string) => void;
  setVerifyInput: (value: { serverSeed: string; clientSeed: string; nonce: string }) => void;
  onRotate: () => Promise<void>;
  onVerify: () => Promise<void>;
  onClose: () => void;
}) {
  const [tab, setTab] = useState<"seeds" | "verify">("seeds");

  return (
    <div className="fixed inset-x-0 bottom-0 z-50 mx-auto max-w-[960px] rounded-t-2xl border border-white/10 bg-[#111720] p-4 shadow-[0_-24px_70px_rgba(0,0,0,.45)]">
      <div className="mx-auto mb-4 h-1.5 w-12 rounded-full bg-slate-600" />
      <div className="flex items-center justify-between">
        <h2 className="text-xl font-black text-white">Équité</h2>
        <button onClick={onClose} className="grid h-9 w-9 place-items-center rounded-lg bg-white/[0.06] text-slate-200">
          <X className="h-5 w-5" />
        </button>
      </div>

      <div className="mt-4 grid grid-cols-2 border-b border-white/10 text-sm font-black">
        <button onClick={() => setTab("seeds")} className={cn("h-10 border-b-2", tab === "seeds" ? "border-felt text-felt" : "border-transparent text-slate-300")}>Seeds</button>
        <button onClick={() => setTab("verify")} className={cn("h-10 border-b-2", tab === "verify" ? "border-felt text-felt" : "border-transparent text-slate-300")}>Vérifier</button>
      </div>

      {tab === "seeds" ? (
        <div className="mt-4 max-h-[58vh] space-y-3 overflow-y-auto pr-1 scrollbar-thin">
          <SeedField label="Seed de client actif" value={state.seedPair.clientSeed} />
          <SeedField label="Seed de serveur actif (haché)" value={state.seedPair.serverSeedHash} />
          <SeedField label="Total des paris effectués avec la paire" value={String(state.seedPair.nonce)} />
          <div className="pt-2">
            <label className="mb-1.5 block text-xs font-black text-white">Nouvelle seed de client</label>
            <div className="flex gap-2">
              <input value={clientSeedInput} onChange={(event) => setClientSeedInput(event.target.value)} className="min-w-0 flex-1 rounded-lg border border-white/10 bg-[#202631] px-3 text-sm font-bold text-white outline-none" />
              <button onClick={() => void onRotate()} className="h-11 rounded-lg bg-felt px-4 text-sm font-black text-ink-950">Changer</button>
            </div>
          </div>
          {revealedSeed ? (
            <div className="rounded-lg border border-felt/20 bg-felt/[0.06] p-3">
              <p className="text-xs font-black text-felt">Seed précédente révélée</p>
              <SeedField label="Server seed" value={revealedSeed.serverSeed} compact />
            </div>
          ) : null}
        </div>
      ) : (
        <div className="mt-4 max-h-[58vh] space-y-3 overflow-y-auto pr-1 scrollbar-thin">
          <VerifyInput label="Seed de serveur" value={verifyInput.serverSeed} onChange={(value) => setVerifyInput({ ...verifyInput, serverSeed: value })} />
          <VerifyInput label="Seed de client" value={verifyInput.clientSeed} onChange={(value) => setVerifyInput({ ...verifyInput, clientSeed: value })} />
          <VerifyInput label="Nonce" value={verifyInput.nonce} onChange={(value) => setVerifyInput({ ...verifyInput, nonce: value })} />
          <button onClick={() => void onVerify()} className="h-11 w-full rounded-lg bg-felt text-sm font-black text-ink-950">Vérifier la distribution</button>
          {verifyResult ? (
            <div className="rounded-lg border border-white/10 bg-black/20 p-3 text-xs font-bold text-slate-300">
              <p className="text-felt">Hash : {verifyResult.serverSeedHash}</p>
              <p className="mt-2">Joueur : {verifyResult.openingDeal.player.map((card) => card.code).join(", ")}</p>
              <p>Croupier : {verifyResult.openingDeal.dealer.map((card) => card.code).join(", ")}</p>
            </div>
          ) : null}
        </div>
      )}
    </div>
  );
}

function SeedField({ label, value, compact = false }: { label: string; value: string; compact?: boolean }) {
  return (
    <div>
      <label className="mb-1.5 block text-xs font-black text-white">{label}</label>
      <div className={cn("flex items-center gap-2 rounded-lg border border-white/10 bg-black/20 px-3", compact ? "h-9" : "h-11")}>
        <span className="min-w-0 flex-1 truncate text-xs font-bold text-slate-400">{value}</span>
        <button onClick={() => void navigator.clipboard.writeText(value)} className="text-slate-300">
          <Copy className="h-4 w-4" />
        </button>
      </div>
    </div>
  );
}

function VerifyInput({ label, value, onChange }: { label: string; value: string; onChange: (value: string) => void }) {
  return (
    <label className="block">
      <span className="mb-1.5 block text-xs font-black text-white">{label}</span>
      <input value={value} onChange={(event) => onChange(event.target.value)} className="h-11 w-full rounded-lg border border-white/10 bg-[#202631] px-3 text-sm font-bold text-white outline-none" />
    </label>
  );
}

function StatsModal({ state, onClose }: { state: BlackjackState; onClose: () => void }) {
  return (
    <div className="fixed inset-0 z-50 grid place-items-center bg-black/70 p-4">
      <div className="w-full max-w-sm rounded-xl border border-white/10 bg-[#111720] p-4 shadow-panel">
        <div className="flex items-center justify-between">
          <h2 className="text-lg font-black text-white">Live Stats</h2>
          <button onClick={onClose} className="grid h-8 w-8 place-items-center rounded-lg bg-white/[0.06] text-slate-200">
            <X className="h-4 w-4" />
          </button>
        </div>
        <div className="mt-4 grid grid-cols-2 gap-2">
          <StatTile label="Profit" value={formatMoney(state.stats.profit, state.currency)} tone={Number(state.stats.profit) >= 0 ? "text-felt" : "text-ember"} />
          <StatTile label="Misé" value={formatMoney(state.stats.stake, state.currency)} />
          <StatTile label="Gains" value={state.stats.wins.toString()} tone="text-felt" />
          <StatTile label="Pertes" value={state.stats.losses.toString()} tone="text-ember" />
        </div>
      </div>
    </div>
  );
}

function StatTile({ label, value, tone = "text-white" }: { label: string; value: string; tone?: string }) {
  return (
    <div className="rounded-lg bg-white/[0.055] p-3">
      <p className="text-xs font-bold text-slate-500">{label}</p>
      <p className={cn("mt-1 text-sm font-black", tone)}>{value}</p>
    </div>
  );
}

function ResultBadge({ status, profit, currency }: { status: BlackjackHand["status"]; profit?: string; currency: string }) {
  const tone = status === "WON" ? "text-felt bg-felt/[0.12]" : status === "PUSH" ? "text-slate-200 bg-white/[0.08]" : "text-ember bg-ember/[0.12]";
  const label = status === "WON" ? `+${formatMoney(Number(profit ?? 0), currency)}` : status === "PUSH" ? "Push" : "Perdu";
  return <span className={cn("rounded-md px-2 py-0.5 text-[10px] font-black", tone)}>{label}</span>;
}

function MoreGames({ games }: { games: typeof miniGames }) {
  return (
    <section className="space-y-2.5">
      <div className="flex items-center gap-2">
        <span className="grid h-7 w-7 place-items-center rounded-lg bg-felt text-ink-950">
          <Sparkles className="h-4 w-4" />
        </span>
        <h2 className="text-lg font-black text-white">Plus de ZiraBet Games</h2>
      </div>
      <div className="-mx-1 flex gap-2 overflow-x-auto px-1 pb-1 scrollbar-thin">
        {games.slice(0, 5).map((game) => (
          <Link key={game.slug} href={`/casino/games/${game.slug}`} className="relative aspect-[150/205] w-[112px] shrink-0 overflow-hidden rounded-lg border border-white/10 bg-[#151a22] sm:w-[138px]">
            <Image src={game.image} alt="" fill sizes="140px" className="object-cover" />
            <div className="absolute inset-0 bg-gradient-to-t from-black/82 via-transparent to-transparent" />
            <div className="absolute inset-x-0 bottom-0 p-2">
              <p className="text-sm font-black uppercase leading-none text-white">{game.title}</p>
            </div>
          </Link>
        ))}
      </div>
    </section>
  );
}

function upsertRecent(round: BlackjackRound, rounds: BlackjackRound[]) {
  return [round, ...rounds.filter((item) => item.id !== round.id)].slice(0, 25);
}

function getRoundRevealDelay(round: BlackjackRound, previousRound: BlackjackRound | null) {
  const previous = previousRound?.id === round.id ? previousRound : null;
  let delay = 0;

  delay = Math.max(delay, getCardsRevealDelay(round.dealerCards, previous?.dealerCards ?? [], "dealer"));

  const previousHands = new Map((previous?.hands ?? []).map((hand) => [hand.id, hand]));
  round.hands.forEach((hand) => {
    delay = Math.max(delay, getCardsRevealDelay(hand.cards, previousHands.get(hand.id)?.cards ?? [], "player"));
  });

  return delay > 0 ? delay + DEAL_REVEAL_BUFFER_MS : 0;
}

function getCardsRevealDelay(cards: Card[], previousCards: Card[], target: "dealer" | "player") {
  let delay = 0;

  cards.forEach((card, index) => {
    const previous = previousCards[index];
    const isNewOrRevealed = !previous || previous.code !== card.code || previous.hidden !== card.hidden;
    if (isNewOrRevealed) {
      delay = Math.max(delay, cardDealDelay(target, index) + CARD_DEAL_DURATION_MS);
    }
  });

  return delay;
}

function formatInputAmount(value: number) {
  return formatAmountInput(value);
}

function toAmount(value: string) {
  return parseAmountInput(value);
}

function formatMoney(value: string | number, currency: string) {
  return new Intl.NumberFormat("fr-FR", { style: "currency", currency }).format(Number(value));
}

function setWindowTimeout(callback: () => void, delay: number) {
  if (typeof window === "undefined") return null;
  return window.setTimeout(callback, Math.max(0, delay));
}

function clearWindowTimer(timer: number | null) {
  if (typeof window === "undefined" || timer === null) return;
  window.clearTimeout(timer);
}
