"use client";
import Image from "next/image";
import { useEffect, useMemo, useRef, useState } from "react";
import { JoinGameModal } from "../../components/roll/JoinGameModal";
import { useSelector } from "react-redux";
import { User } from "@/constants/types";
import { fetchInventoryCards, InventoryCard } from "@/app/lib/api/inventory";
import { getSocket } from "@/app/components/utils/socket";
import Header from "@/app/components/roll/Header";
import RollStrip from "@/app/components/roll/RollStripe";
import RoundControls from "@/app/components/roll/RoundControl";
import JoinGamePanel from "@/app/components/roll/JoinGamePanel";
import NotAvailable from "@/app/components/ui/NotAvailable";

type TierName = "2X" | "14X" | "7X";

type Tier = {
  id: string;
  name: TierName;
  multiplier: string;
  playLabel: string;
  buttonClass: string;
  headerAccentClass: string;
  image: string;
};

const tiers: Tier[] = [
  {
    id: "tier-2x",
    name: "2X",
    multiplier: "2x",
    playLabel: "PLAY(2x)",
    buttonClass: "bg-[#FFAA4A] hover:bg-yellow-400 shadow",
    headerAccentClass: "bg-lime-500/20",
    image: "/images/roll/gold.svg",
  },
  {
    id: "tier-14x",
    name: "14X",
    multiplier: "14x",
    playLabel: "PLAY(14x)",
    buttonClass: "bg-[#137E6A] greenShadow hover:bg-emerald-400 ",
    headerAccentClass: "bg-emerald-500/20",
    image: "/images/roll/green.svg",
  },
  {
    id: "tier-7x",
    name: "7X",
    multiplier: "7x",
    playLabel: "PLAY(7x)",
    buttonClass: "bg-[#422C53] purpleShadow hover:bg-purple-400 ",
    headerAccentClass: "bg-purple-500/20",
    image: "/images/roll/purple.svg",
  },
  {
    id: "tier-2x-red",
    name: "2X",
    multiplier: "2x",
    playLabel: "PLAY(2x)",
    buttonClass: "bg-[#D22F31] purpleShadow hover:bg-red-400 ",
    headerAccentClass: "bg-purple-500/20",
    image: "/images/roll/red.svg",
  },
];

type TimerUpdatePayload = {
  remainingSec: number;
  durationSec: number;
};

type TierEntry = {
  id: string;
  name: string;
  tier: TierName;
  cards: Array<{ inv_id: number; price: number; image: string }>;
  totalValue: number;
  image?: string;
};
type SpinPhase = "idle" | "spinning" | "stopping" | "showWinner";

const Inner = () => {
  const [winner, setWinner] = useState(false);
  const [isJoinModalOpen, setJoinModalOpen] = useState(false);
  const [xValue, setXvalue] = useState<string>("1.5X");
  const TOTAL_ICONS = 24;
  const CARD_W = 128; // approximate card width in px
  const GAP = 16; // space between cards
  const ITEM_W = CARD_W + GAP;
  const segWidth = TOTAL_ICONS * ITEM_W;
  const socketRef = useRef<ReturnType<typeof getSocket> | null>(null);
  const spinningStartedRef = useRef(false);
  // spin state for the top strip
  const [stripPhase, setStripPhase] = useState<SpinPhase>("idle");
  const [stripSpinning, setStripSpinning] = useState(false);
  const [stripOffset, setStripOffset] = useState(0);
  const [winnerTrackIndex, setWinnerTrackIndex] = useState<number | null>(null);
  const user = useSelector((state: any) => state.auth.user) as User | null;

  const [isCountingDown, setIsCountingDown] = useState(false);
  const [showWinnerEffects, setShowWinnerEffects] = useState(false);
  const [stopTransition, setStopTransition] = useState<string | undefined>(
    undefined,
  );
  const CARDS_PAGE_SIZE = 20;
  const [visibleCardsCount, setVisibleCardsCount] = useState(CARDS_PAGE_SIZE);
  const [isLoadingCards, setIsLoadingCards] = useState(false);
  const [cardsError, setCardsError] = useState<string | null>(null);
  const [cards, setCards] = useState<InventoryCard[]>([]);
  const [hasLoadedCards, setHasLoadedCards] = useState(false);
  // const PROGRESS_DURATION = 1000;
  // refs for the spinning track
  const stripViewportRef = useRef<HTMLDivElement | null>(null);
  const stripTrackRef = useRef<HTMLDivElement | null>(null);
  const countdownTimeoutRef = useRef<NodeJS.Timeout | null>(null);
  const [myCards, setMyCards] = useState<any[]>([]);
  const [progressPct, setProgressPct] = useState(0);
  const [serverOffsetMs, setServerOffsetMs] = useState(0); // serverNow - clientNow
  const [roundEndAt, setRoundEndAt] = useState<number | null>(null);
  const [roundDurSec, setRoundDurSec] = useState(15);

  const [remainingTime, setRemainingTime] = useState(15);
  const [roundDuration, setRoundDuration] = useState(15);
  const joinedRef = useRef<Record<string, boolean>>({});
  const [spinTick, setSpinTick] = useState(0); // increments each spin (also used for randomizing)
  const socket = getSocket();
  const spinAnimRef = useRef<Animation | null>(null);
  const [offset, setOffset] = useState<number | null>(null);
  const lastAppliedSpinRef = useRef<string>(""); // prevents re-applying same spin
  const isRun = useRef(false);
  const [rollPhase, setRollPhase] = useState<"countdown" | "break">(
    "countdown",
  );
  const [endAt, setEndAt] = useState<number | null>(null);
  const [durationSec, setDurationSec] = useState(10);
  const [winnerTier, setWinnerTier] = useState<string | null>(null);

  const [hasMoreInv, setHasMoreInv] = useState(true);
  const [isLoadingMoreInv, setIsLoadingMoreInv] = useState(false);
  const betSubmittedRef = useRef(false);
  const pinnedWinnerKeyRef = useRef<string>("");
  const lockWinnerIndexRef = useRef(false);

  const resultToImgIdx: Record<string, number> = {
    "tier-2x-red": 0, // red
    "tier-14x": 1, // green
    "tier-7x": 2, // purple
    "tier-2x": 3, // gold
  };
  const spinStopAtRef = useRef<number | null>(null);

  const [playersByTier, setPlayersByTier] = useState<
    Record<string, TierEntry[]>
  >({
    "tier-2x": [],
    "tier-14x": [],
    "tier-7x": [],
    "tier-2x-red": [],
  });

  let userName = (user as any)?.name;

  const stripImages = [
    "/images/roll/red.svg",
    "/images/roll/green.svg",
    "/images/roll/purple.svg",
    "/images/roll/gold.svg",
  ];

  const PROGRESS_DURATION = 3500;
  // ((roundDuration * 1000 - remainingTime) / (roundDuration * 1000)) * 100;
  const repeats = 8;

  const stripTrack = useMemo(() => {
    const total = TOTAL_ICONS * repeats;
    return Array.from({ length: total }, (_, idx) => idx % stripImages.length);
  }, [TOTAL_ICONS, repeats, stripImages.length]);

  const getTargetIndex = (roundId: number, winnerTier: string) => {
    const want = resultToImgIdx[winnerTier]; // 0/1/2/3
    if (want === undefined) return 30;

    const base = 30 + (roundId % 10) * 3;
    for (let i = base; i < stripTrack.length; i++) {
      if (stripTrack[i] === want) return i;
    }
    for (let i = 0; i < stripTrack.length; i++) {
      if (stripTrack[i] === want) return i;
    }
    return base;
  };

  const stopSpinToIndex = (targetIndex: number) => {
    const vp = stripViewportRef.current;
    const trackEl = stripTrackRef.current;
    if (!vp || !trackEl) return;

    const anim = spinAnimRef.current;
    if (!anim) return;

    // freeze current position
    anim.pause();

    let currentX = 0;
    const tr = getComputedStyle(trackEl).transform;
    if (tr && tr !== "none") {
      const m = new DOMMatrix(tr);
      currentX = m.m41;
    }

    trackEl.style.transform = `translateX(${currentX}px)`;

    const vpRect = vp.getBoundingClientRect();
    const viewportCenterX = vpRect.left + vpRect.width / 2;

    const targetNode = trackEl.querySelector<HTMLElement>(
      `[data-strip-index="${targetIndex}"]`,
    );

    // fallback: nearest center if missing
    let bestCenterX = 0;
    if (targetNode) {
      const rect = targetNode.getBoundingClientRect();
      bestCenterX = rect.left + rect.width / 2;
    } else {
      const nodes = trackEl.querySelectorAll<HTMLElement>("[data-strip-index]");
      let bestDist = Infinity;
      nodes.forEach((node) => {
        const rect = node.getBoundingClientRect();
        const c = rect.left + rect.width / 2;
        const d = Math.abs(c - viewportCenterX);
        if (d < bestDist) {
          bestDist = d;
          bestCenterX = c;
        }
      });
    }

    const delta = viewportCenterX - bestCenterX;
    const direction = delta >= 0 ? 1 : -1;

    const overshootPx = 28;
    const overshootTarget = currentX + delta + direction * overshootPx;
    const finalTarget = currentX + delta;

    // cancel AFTER freeze
    anim.cancel();
    spinAnimRef.current = null;

    setStripPhase("stopping");
    setWinnerTrackIndex(targetIndex);

    setStopTransition("none");
    setStripOffset(currentX);
    trackEl.style.transform = "";

    requestAnimationFrame(() => {
      requestAnimationFrame(() => {
        setStopTransition("transform 1.1s cubic-bezier(0.18, 0.75, 0.22, 1)");
        setStripOffset(overshootTarget);

        setTimeout(() => {
          setStopTransition("transform 0.9s cubic-bezier(0.25, 1.25, 0.5, 1)");
          setStripOffset(finalTarget);

          setTimeout(() => {
            setStripPhase("showWinner");
            setWinner(true);

            setTimeout(() => {
              setShowWinnerEffects(true);
              setTimeout(() => setStripSpinning(false), 400);
            }, 200);
          }, 900);
        }, 1100);
      });
    });
  };

  const loadCards = async (userId: any, token: any) => {
    if (!user) return;
    try {
      setIsLoadingCards(true);
      setCardsError(null);

      const data = await fetchInventoryCards(userId, "yes", token);
      setCards(data.cards);
      setOffset(data.offset);
      setHasMoreInv(data.cards.length > 0);
      setHasLoadedCards(true);
    } catch (err: any) {
      console.error(err);
      const msg =
        err?.response?.data?.error ||
        err?.response?.data?.message ||
        err?.message;
      setCardsError(msg || "Failed to load cards");
    } finally {
      setIsLoadingCards(false);
    }
  };

  const loadMoreInventory = async () => {
    if (!user?.userId || !user?.token) return;
    if (!hasMoreInv) return;
    if (isLoadingMoreInv) return;
    if (offset == null) return;

    try {
      setIsLoadingMoreInv(true);

      const data = await fetchInventoryCards(
        user.userId,
        "yes",
        user.token,
        offset,
      );

      // 🔴 STOP CONDITION
      if (data.cards.length === 0) {
        setHasMoreInv(false); // ❌ no more requests
        return;
      }

      setCards((prev) => [...prev, ...data.cards]); // ✅ append
      setOffset(data.offset ?? null); // ✅ next offset
    } catch (err) {
      console.error(err);
    } finally {
      setIsLoadingMoreInv(false);
    }
  };

  const handleStripSpin = () => {
    if (stripSpinning || isCountingDown) return;
    setSpinTick((t) => t + 1);

    // Start countdown/progress bar first
    setIsCountingDown(true);
    setWinner(false);
    setWinnerTrackIndex(null);

    setIsCountingDown(true);
  };

  const startSpinAnimation = () => {
    setStripSpinning(true);
    setStopTransition(undefined);
    setShowWinnerEffects(false);
    setWinner(false);
    setWinnerTrackIndex(null);

    setStripPhase("spinning");

    // Helper: safely set speed
    const setSpeed = (v: number) => {
      if (spinAnimRef.current) spinAnimRef.current.playbackRate = v;
    };

    // Smooth speed ramp (fast -> medium -> slow -> very slow -> ultra slow)
    setTimeout(() => setSpeed(0.6), 0);

    setTimeout(() => {
      setSpeed(3.0); // fast

      setTimeout(() => {
        setSpeed(1.8);

        setTimeout(() => {
          setSpeed(1.0);

          setTimeout(() => {
            setSpeed(0.6);

            setTimeout(() => {
              setSpeed(0.4);

              setTimeout(() => {
                setSpeed(0.25);

                // ✅ extra end phases (more slow, more smooth)
                setTimeout(() => {
                  setSpeed(0.18);

                  setTimeout(() => {
                    setSpeed(0.12);

                    // ✅ final stop after 충분 slow time
                    setTimeout(() => {
                      const vp = stripViewportRef.current;
                      const trackEl = stripTrackRef.current;
                      if (!vp || !trackEl) {
                        setStripSpinning(false);
                        return;
                      }

                      const anim = spinAnimRef.current;
                      if (!anim) {
                        setStripSpinning(false);
                        return;
                      }

                      // freeze exact position
                      anim.pause();

                      let currentX = 0;
                      const tr = getComputedStyle(trackEl).transform;
                      if (tr && tr !== "none") {
                        const m = new DOMMatrix(tr);
                        currentX = m.m41;
                      }

                      trackEl.style.transform = `translateX(${currentX}px)`;

                      const vpRect = vp.getBoundingClientRect();
                      const viewportCenterX = vpRect.left + vpRect.width / 2;
                      const nodes =
                        trackEl.querySelectorAll<HTMLElement>(
                          "[data-strip-index]",
                        );

                      let bestIdx = -1;
                      let bestDist = Infinity;
                      let bestCenterX = 0;

                      nodes.forEach((node) => {
                        const rect = node.getBoundingClientRect();
                        const cardCenterX = rect.left + rect.width / 2;
                        const d = Math.abs(cardCenterX - viewportCenterX);
                        if (d < bestDist) {
                          bestDist = d;
                          bestCenterX = cardCenterX;
                          bestIdx = Number(
                            node.getAttribute("data-strip-index") ?? -1,
                          );
                        }
                      });

                      if (bestIdx === -1) {
                        setStripSpinning(false);
                        return;
                      }

                      const delta = viewportCenterX - bestCenterX;
                      const direction = delta >= 0 ? 1 : -1;

                      const overshootPx = 28; // ✅ small overshoot for smooth bounce
                      const overshootTarget =
                        currentX + delta + direction * overshootPx;
                      const finalTarget = currentX + delta;

                      // cancel AFTER freeze
                      anim.cancel();
                      spinAnimRef.current = null;

                      setStripPhase("stopping");
                      setWinnerTrackIndex(bestIdx);

                      // start from exact current position (no transition)
                      setStopTransition("none");
                      setStripOffset(currentX);

                      trackEl.style.transform = "";

                      // animate overshoot -> settle
                      requestAnimationFrame(() => {
                        requestAnimationFrame(() => {
                          setStopTransition(
                            "transform 1.1s cubic-bezier(0.18, 0.75, 0.22, 1)",
                          );
                          setStripOffset(overshootTarget);

                          setTimeout(() => {
                            setStopTransition(
                              "transform 0.9s cubic-bezier(0.25, 1.25, 0.5, 1)",
                            );
                            setStripOffset(finalTarget);

                            setTimeout(() => {
                              setStripPhase("showWinner");
                              setWinner(true);

                              setTimeout(() => {
                                setShowWinnerEffects(true);
                                setTimeout(() => setStripSpinning(false), 400);
                              }, 200);
                            }, 900);
                          }, 1100);
                        });
                      });
                    }, 2200); // ✅ longer final slow time (was 1500)
                  }, 1400);
                }, 1200);
              }, 1000);
            }, 900);
          }, 800);
        }, 800);
      }, 0);
    }, 700);
  };

  const playCard = (payload: {
    image: any;
    name: any;
    userId: any;
    xValue: "1.5X" | "2X" | "4X";
    totalValue: number;
    cards: any[];
  }) => {
    console.log("payload from parant", payload);
    socket.emit("playCard", {
      userId: payload?.userId,
      name: payload?.name,
      image: payload?.image,
      tier: payload.xValue,
      cards: JSON.stringify(payload.cards),
      totalValue: payload.totalValue,
    });
  };

  const applySpinSchedule = (spin: {
    roundId: number;
    winnerTier: string;
    startAt: number;
    stopAt: number;
    serverNow: number;
  }) => {
    const key = `${spin.roundId}-${spin.startAt}-${spin.stopAt}-${spin.winnerTier}`;
    if (lastAppliedSpinRef.current === key) return;
    lastAppliedSpinRef.current = key;

    // sync clock + store stop time
    setServerOffsetMs(spin.serverNow - Date.now());
    spinStopAtRef.current = spin.stopAt;

    // reset UI
    setWinner(false);
    setShowWinnerEffects(false);
    setWinnerTrackIndex(null);
    setWinnerTier(null);

    const nowServer = Date.now() + (spin.serverNow - Date.now());

    // ✅ CASE A: refresh AFTER stop => directly show winner
    if (nowServer >= spin.stopAt) {
      snapToWinner(spin);
      return;
    }

    // ✅ CASE B: refresh DURING spin => start immediately (msToStart = 0)
    // ✅ CASE C: refresh BEFORE spin => will start at correct time
    const msToStart = Math.max(0, spin.startAt - nowServer);
    window.setTimeout(() => {
      setStripSpinning(true);
      setStripPhase("spinning");
      setSpinTick((t) => t + 1);
    }, msToStart);

    const msToStop = Math.max(0, spin.stopAt - nowServer);
    window.setTimeout(() => {
      setWinnerTier(spin.winnerTier);
      const idx = getTargetIndex(spin.roundId, spin.winnerTier);
      stopSpinToIndex(idx);
    }, msToStop);
  };

  const snapToWinner = (spin: {
    roundId: number;
    winnerTier: string;
    startAt: number;
    stopAt: number;
    serverNow: number;
  }) => {
    const idx = getTargetIndex(spin.roundId, spin.winnerTier);

    // ✅ stop any running animation to avoid jerk
    if (spinAnimRef.current) {
      try {
        spinAnimRef.current.cancel();
      } catch {}
      spinAnimRef.current = null;
    }
    setWinnerTier(spin.winnerTier);

    setStopTransition("none");
    setStripSpinning(false);
    setStripPhase("showWinner");
    setWinner(true);
    setShowWinnerEffects(true);
    lockWinnerIndexRef.current = true;

    setWinnerTrackIndex(idx);

    const vp = stripViewportRef.current;

    if (!vp) return;

    const viewportW = vp.getBoundingClientRect().width;
    const centerOffset = viewportW / 2 - CARD_W / 2;

    // ✅ pure math, no DOM query => no vibration
    const finalX = -(idx * ITEM_W) + centerOffset;

    setStripOffset(finalX);
    requestAnimationFrame(() => {
      requestAnimationFrame(() => {
        const vp = stripViewportRef.current;
        const trackEl = stripTrackRef.current;
        if (!vp || !trackEl) return;

        const vpRect = vp.getBoundingClientRect();
        const viewportCenterX = vpRect.left + vpRect.width / 2;

        const node = trackEl.querySelector<HTMLElement>(
          `[data-strip-index="${idx}"]`,
        );
        if (!node) return;

        const rect = node.getBoundingClientRect();
        const nodeCenterX = rect.left + rect.width / 2;

        const delta = viewportCenterX - nodeCenterX;

        // ✅ shift so target comes exactly under arrow
        setStripOffset((prev) => prev + delta);
      });
    });
  };

  useEffect(() => {
    if (stripPhase === "showWinner" && winnerTrackIndex !== null) {
      setShowWinnerEffects(true);
    }
  }, [stripPhase, winnerTrackIndex]);

  useEffect(() => {
    if (rollPhase === "countdown" && progressPct === 0) {
      setWinnerTier(null);
    }
  }, [rollPhase, progressPct]);

  useEffect(() => {
    const s = getSocket();
    if (!s.connected) s.connect();

    s.emit("roll:enter"); // ✅ start loop on server
    s.emit("requestRollTimer"); // ✅ snapshot

    const onTimer = (p: {
      phase: "countdown" | "break";
      durationSec: number;
      endAt: number;
      serverNow: number;
      spin: null | {
        roundId: number;
        winnerTier: string;
        startAt: number;
        stopAt: number;
      };
      lastResult: null | {
        roundId: number;
        winnerTier: string;
        stopAt: number;
      };
    }) => {
      setRollPhase(p.phase);
      setDurationSec(p.durationSec);
      setEndAt(p.endAt);
      setServerOffsetMs(p.serverNow - Date.now());

      // ✅ clock sync
      setServerOffsetMs(p.serverNow - Date.now());

      if (p.phase === "break" && p.spin) {
        applySpinSchedule({ ...p.spin, serverNow: p.serverNow });
      }

      if (p.phase === "countdown" && p.lastResult) {
        const key = `${p.lastResult.roundId}-${p.lastResult.winnerTier}-${p.lastResult.stopAt}`;

        // ✅ same result pe bar bar snap mat karo
        if (pinnedWinnerKeyRef.current !== key) {
          pinnedWinnerKeyRef.current = key;

          snapToWinner({
            roundId: p.lastResult.roundId,
            winnerTier: p.lastResult.winnerTier,
            startAt: p.lastResult.stopAt - SPIN_DURATION_MS,
            stopAt: p.lastResult.stopAt,
            serverNow: p.serverNow,
          });
        }
      }

      if (p.phase === "countdown" && !p.lastResult) {
        lastAppliedSpinRef.current = "";
        spinStopAtRef.current = null;
        setWinnerTier(null);
      }
      // ✅ only unlock when there is NO lastResult to pin
      if (p.phase === "countdown" && !p.lastResult) {
        lockWinnerIndexRef.current = false;
      }

      if (p.phase === "break") pinnedWinnerKeyRef.current = "";
      if (p.phase === "countdown" && !p.lastResult)
        pinnedWinnerKeyRef.current = "";
    };

    s.on("roll:timer", onTimer);

    return () => {
      s.off("roll:timer", onTimer);
    };
  }, []);

  useEffect(() => {
    const s = socketRef.current;
    console.log(" from useEffect?", s?.connected, s?.id);

    // ✅ connect immediately when page mounts
    if (!s?.connected) s?.connect();

    // ✅ debug
    s?.on("connect", () => console.log("✅ connected on load:", s.id));
    s?.on("connect_error", (e) => console.log("❌ connect_error:", e));

    return () => {
      s?.off("connect");
      s?.off("connect_error");
    };
  }, []);

  useEffect(() => {
    const onTimerUpdate = ({
      remainingSec,
      durationSec,
    }: TimerUpdatePayload) => {
      setRemainingTime(remainingSec);
      setRoundDuration(durationSec);
    };

    const onRoundEnded = () => {
      // alert("Round ended!");
      console.log("🧪 roundEnded received (alert disabled for testing)");
    };

    // ✅ prevent duplicate listeners
    socket.off("timerUpdate", onTimerUpdate);
    socket.off("roundEnded", onRoundEnded);

    socket.on("timerUpdate", onTimerUpdate);
    socket.on("roundEnded", onRoundEnded);

    return () => {
      socket.off("timerUpdate", onTimerUpdate);
      socket.off("roundEnded", onRoundEnded);
    };
  }, []);

useEffect(() => {
  const socket = getSocket();

  const onUpdatePlayers = (updated: Record<string, TierEntry[]>) => {
    const safe = updated ?? {};
    
    setPlayersByTier(safe);

    const myId = socket.id;
    if (!myId) return;

    const allPlayers = Object.values(safe).flat();
    const me = allPlayers.find((p) => p.id === myId);

    setMyCards(me?.cards ?? []);
  };

  socket.on("connect", () => {
    console.log("Socket connected:", socket.id);
  });
  socket.on("updatePlayers", onUpdatePlayers);

  return () => {
    socket.off("updatePlayers", onUpdatePlayers);
  };
}, []);


  useEffect(() => {
    socketRef.current = socket;

    const onRoundState = (s: any) => {
      // ✅ clock sync
      if (typeof s.serverNow === "number") {
        setServerOffsetMs(s.serverNow - Date.now());
      }

      // ✅ progress driver
      setRoundEndAt(typeof s.endAt === "number" ? s.endAt : null);
      setRoundDurSec(Number(s.durationSec || 0));

      setRemainingTime(s.remainingSec);
      setRoundDuration(s.durationSec);

      // ✅ strip phases should follow server (not local button)

      if (s.phase === "spinning") {
        // if (!spinningStartedRef.current) {
        spinningStartedRef.current = true;

        setWinner(false);
        setShowWinnerEffects(false);
        setStripSpinning(true);
        setStripPhase("spinning");
        setSpinTick((t) => t + 1);
        // }
      }

      if (s.phase === "countdown") spinningStartedRef.current = false;
      if (s.phase === "idle") spinningStartedRef.current = false;
    };

    const onRoundResult = (p: {
      roundId: number;
      result: "1.5X" | "2X" | "4X";
    }) => {
      const idx = getTargetIndex(p.roundId, p.result);
      stopSpinToIndex(idx);
    };
    const onFinalResult = (data: any) => {
      console.log("🎯 FINAL RESULT (client):", data);
    };

    socket.on("roundState", onRoundState);
    socket.on("roundResult", onRoundResult);
    socket.on("finalResult", onFinalResult);
    // ✅ CONNECT IMMEDIATELY ON PAGE LOAD
    if (!socket.connected) socket.connect();

    return () => {
      socket.off("roundState", onRoundState);
      socket.off("roundResult", onRoundResult);
      socket.off("finalResult", onFinalResult);
      // ✅ if already connected, ask for snapshot (client-side navigation case)
      socket.emit("requestRoundState");

      // ✅ if not connected, connect and request on connect
      if (!socket.connected) socket.connect();
      socket.once("connect", () => socket.emit("requestRoundState"));
    };
  }, []);

  useEffect(() => {
    let raf = 0;

    const tick = () => {
      // ✅ break phase me bar ko 0 pe rakh do (delay feel)
      if (rollPhase === "break") {
        setProgressPct(0);
        raf = requestAnimationFrame(tick);
        return;
      }

      if (!endAt) {
        setProgressPct(0);
        raf = requestAnimationFrame(tick);
        return;
      }

      const now = Date.now() + serverOffsetMs;
      const totalMs = Math.max(1, durationSec * 1000);
      const remainingMs = Math.max(0, endAt - now);
      const elapsedMs = totalMs - remainingMs;

      const pct = Math.max(0, Math.min(100, (elapsedMs / totalMs) * 100));
      setProgressPct(pct);

      raf = requestAnimationFrame(tick);
    };

    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, [endAt, durationSec, serverOffsetMs, rollPhase]);

  // useEffect(() => {
  //   if (isRun.current) return;
  //   isRun.current = true;
  //   // if (!user) return;
  //   if (!hasLoadedCards) {
  //     (async () => {
  //       await loadCards(user?.userId, user?.token);
  //     })();
  //   }
  // }, [hasLoadedCards]);

  
  useEffect(() => {
    setVisibleCardsCount(CARDS_PAGE_SIZE);
  }, []);

  useEffect(() => {
    if (!isCountingDown) return;

    countdownTimeoutRef.current = setTimeout(() => {
      setIsCountingDown(false);
      startSpinAnimation(); // ✅ direct spin start
    }, PROGRESS_DURATION);

    return () => {
      if (countdownTimeoutRef.current) {
        clearTimeout(countdownTimeoutRef.current);
        countdownTimeoutRef.current = null;
      }
    };
  }, [isCountingDown, PROGRESS_DURATION]);

  useEffect(() => {
    // if (!user?.userId) return;

    // connect socket
    if (!socket.connected) {
      socket.connect();
    }

    socket.on("connect", () => {
      console.log("Socket connected (frontend)");

      // send user data
      socket.emit("ROLL_JOIN", {
        userId: user?.userId,
        name: (user as any)?.name,
        image: user?.image,
        balance: user?.balance,
        timestamp: Date.now(),
      });

      // handleStripSpin();
    });

    return () => {
      socket.off("connect");
    };
  }, [user?.userId]);

  useEffect(() => {
    if (stripPhase !== "showWinner") return;
    if (lockWinnerIndexRef.current) return;
    const vp = stripViewportRef.current;
    const trackEl = stripTrackRef.current;
    if (!vp || !trackEl) return;

    const vpRect = vp.getBoundingClientRect();
    const centerX = vpRect.left + vpRect.width / 2;

    const nodes =
      trackEl.querySelectorAll<HTMLDivElement>("[data-strip-index]");
    let bestIdx = -1;
    let bestDist = Infinity;

    nodes.forEach((node) => {
      const rect = node.getBoundingClientRect();
      const cardCenter = rect.left + rect.width / 2;
      const d = Math.abs(cardCenter - centerX);
      if (d < bestDist) {
        bestDist = d;
        const idxAttr = node.getAttribute("data-strip-index");
        bestIdx = idxAttr ? Number(idxAttr) : -1;
      }
    });

    if (bestIdx !== -1) {
      setWinnerTrackIndex(bestIdx);
    }
  }, [stripPhase]);
  const SPIN_DURATION_MS = 3500;

  useEffect(() => {
    const el = stripTrackRef.current;
    if (!el) return;

    // start spinning
    if (stripPhase === "spinning") {
      // clear stop/offset styles so no jump
      setStopTransition(undefined);
      setStripOffset(0);
      el.style.transform = "";

      // kill old anim
      spinAnimRef.current?.cancel();
      spinAnimRef.current = null;

      const anim = el.animate(
        [
          { transform: "translateX(0px)" },
          { transform: `translateX(${-segWidth}px)` },
        ],
        { duration: 1200, iterations: Infinity, easing: "linear" },
      );

      spinAnimRef.current = anim;
      anim.playbackRate = 1; // ✅ FIXED speed for everyone

      // ✅ align animation progress to server time (so everyone looks same)
      const stopAt = spinStopAtRef.current;
      if (stopAt) {
        const nowServer = Date.now() + serverOffsetMs;
        const startAt = stopAt - SPIN_DURATION_MS; // same as server
        const elapsed = Math.max(0, nowServer - startAt);

        // duration 1200ms used in animate(...)
        anim.currentTime = elapsed % 1200;
      }

      spinAnimRef.current = anim;

      // ✅ IMPORTANT: ramp AFTER anim exists (smooth)
      const t: number[] = [];

      const setSpeed = (v: number) => {
        if (spinAnimRef.current) spinAnimRef.current.playbackRate = v;
      };

      // Start a little slow then go fast then gradually slow

      return () => {
        t.forEach(clearTimeout);
        spinAnimRef.current?.cancel();
        spinAnimRef.current = null;
      };
    }

    return;
  }, [stripPhase, segWidth, spinTick]);

  useEffect(() => {
    const s = socketRef.current; // or const s = useAppSocket()
    if (!s) return;

    const enterRoll = () => {
      s.emit("roll:enter"); // ✅ THIS starts round on server when first viewer
      s.emit("requestRoundState"); // ✅ get snapshot immediately
    };

    // if already connected (client-side navigation case)
    if (s.connected) enterRoll();

    // if connect happens after
    s.on("connect", enterRoll);

    return () => {
      // ✅ important: leave roll room when page unmounts
      s.emit("roll:leave");
      s.off("connect", enterRoll);
    };
  }, []);

  useEffect(() => {
    if (rollPhase !== "countdown" && isJoinModalOpen) {
      if (!betSubmittedRef.current) {
        socket.emit("cancelJoin");
      }
      setJoinModalOpen(false);
    }
  }, [rollPhase, isJoinModalOpen]);

  useEffect(() => {
    if (stripPhase === "stopping") {
      socket.emit("winstate", winnerTier);
    }
  }, [stripPhase]);

  const reloadCards = () => {
    loadCards(user?.userId, user?.token);
  };

  return (
    <div className="min-h-screen bg-[#12171C] text-white">
      {user?.activeMode === "gems" ? (
        <NotAvailable />
      ) : (
        <>
          <div className="container mx-auto py-4">
            <Header />
            <RollStrip
              stripViewportRef={stripViewportRef}
              stripTrackRef={stripTrackRef}
              stripPhase={stripPhase}
              stripOffset={stripOffset}
              stopTransition={stopTransition}
              stripTrack={stripTrack}
              stripImages={stripImages}
              winnerTrackIndex={winnerTrackIndex}
              showWinnerEffects={showWinnerEffects}
            />

            {/* Round timer & progress bar */}
            <RoundControls
              progressPct={progressPct}
              rollPhase={rollPhase}
              winnerTier={winnerTier}
              playersByTier={playersByTier}
            />

            {/* Join the game panel */}
            <JoinGamePanel
              tiers={tiers}
              playersByTier={playersByTier}
              userName={userName}
              userImage={user?.image}
              setXvalue={setXvalue}
              setJoinModalOpen={setJoinModalOpen}
              joinedRef={joinedRef}
              socketRef={socketRef}
              rollPhase={rollPhase}
              betSubmittedRef={betSubmittedRef}
              reloadCards={reloadCards}
            />
          </div>
          <JoinGameModal
            user={user}
            xValue={xValue}
            cards={cards}
            isOpen={isJoinModalOpen}
            onClose={() => setJoinModalOpen(false)}
            playCard={playCard}
            cardLoading={isLoadingCards}
            cardsError={cardsError}
            socket={socketRef.current}
            onLoadMore={loadMoreInventory}
            hasMore={hasMoreInv}
            isLoadingMore={isLoadingMoreInv}
            betSubmittedRef={betSubmittedRef}
          />
        </>
      )}
    </div>
  );
};

export default Inner;
