Source: web/src/components/gameTable/GameActionPanel/GameActionPanelV2.js

import React, { useState, useEffect } from "react";
import "./gameActionPanelV2.css";
import Button from "../../button/Button.tsx";
import * as actions from "../../../store/actions/clientInteractionsCreator.js";
import { useDispatch } from "react-redux";
import { useTranslation } from "../../Utiles/Translations";
import { useGameTable } from "../../Utiles/GameTableProvider.jsx";

/**
 * GameActionButtons provides an interface for user actions in a game context,
 * such as betting, raising, checking, calling, and folding.
 */
const GameActionButtons = () => {
  const { isFocus, playerMoney, gameCurrentBet, gamePlayerCurrentBet } =
    useGameTable();
  const { getTranslatedWord } = useTranslation();
  const [coins, setCoins] = useState(playerMoney);
  const [min, setMin] = useState(20); // minimum amount to raise
  const [max, setMax] = useState(playerMoney); // maximum amount to raise set to coins
  const [step, setStep] = useState(Math.floor(coins / 5)); // toggle coin steps : setted to 5% of the coins
  const [amount, setAmount] = useState(min); //amount to raise
  const [showPopup, setShowPopup] = useState(false); // popUp to show raise panel
  const [coinsAfterRaise, setCoinsAfterRaise] = useState(0); // calculating the amount that player will be left after the raise to show before
  const dispatch = useDispatch();

  /**
   * Formats a number to a readable string with commas.
   * @param {number} number - The number to format.
   * @returns {string} The formatted number.
   */
  const formatNumber = (number) => {
    return new Intl.NumberFormat().format(number);
  };

  // Increments the bet amount within the allowable range
  const handleIncrement = () => {
    setAmount((prevAmount) => {
      const newAmount = Math.min(prevAmount + step, max);
      setCoinsAfterRaise(coins - newAmount);
      return newAmount;
    });
  };

  // Decrements the bet amount within the allowable range
  const handleDecrement = () => {
    setAmount((prevAmount) => {
      const newAmount = Math.max(prevAmount - step, min);
      setCoinsAfterRaise(coins - newAmount);
      return newAmount;
    });
  };

  // Handles direct input changes to the bet amount
  const handleChange = (event) => {
    const input = event.target.value;
    // Remove commas for parsing
    const newAmount = parseInt(input.replace(/,/g, ""), 10);
    if (!isNaN(newAmount)) {
      // Only update if the parsed value is a valid number
      setAmount(newAmount); // Update the actual number
    }
  };

  // Commits the bet with the Redux action
  const handleBet = (amount) => {
    amount = Math.round(amount);
    dispatch(actions.bet(amount));
    setAmount(amount);
  };

  // Toggles the raise panel or submits the raise
  const handleRaise = () => {
    if (!showPopup) {
      setShowPopup(true);
    } else {
      if (amount < min || amount > max) {
        const validatedAmount = Math.max(min, Math.min(max, amount));
        setAmount(validatedAmount);
      } else {
        handleBet(amount);
        setAmount(min);
        setShowPopup(false);
      }
    }
  };

  // Updates the label for the check/call button based on current game state
  const getCheckOrCallLabel = () => {
    if (gameCurrentBet > 0 && gameCurrentBet !== gamePlayerCurrentBet) {
      if (playerMoney <= gameCurrentBet - gamePlayerCurrentBet) {
        return `${getTranslatedWord("gameActionPanel.call")} ${playerMoney} SC`;
      } else {
        return `${getTranslatedWord("gameActionPanel.call")} ${
          gameCurrentBet - gamePlayerCurrentBet
        } SC`;
      }
    } else {
      return getTranslatedWord("gameActionPanel.check");
    }
  };

  // Handles the check or call action
  const handleCheckOrCall = () => {
    if (isFocus) {
      if (gameCurrentBet > 0) {
        if (playerMoney < gameCurrentBet - gamePlayerCurrentBet) {
          dispatch(actions.bet(playerMoney));
        } else {
          dispatch(actions.bet(gameCurrentBet - gamePlayerCurrentBet));
        }
      } else {
        dispatch(actions.check());
      }
      setAmount(min);
      setShowPopup(false);
    }
  };

  // Handles the fold action
  const handleFold = () => {
    if (isFocus) {
      setAmount(min);
      setShowPopup(false);
      dispatch(actions.fold());
    }
  };

  // Effect to update minimum bet when the game's current bet changes
  useEffect(() => {
    if (gameCurrentBet) {
      setMin(gameCurrentBet - gamePlayerCurrentBet);
    } else {
      setMin(20);
    }
  }, [gameCurrentBet, gamePlayerCurrentBet]);

  // Effect to adjust the player's remaining coins after setting a bet amount
  useEffect(() => {
    setCoinsAfterRaise(coins - amount);
  }, [amount, coins]);

  // Hide the raise panel when the player is not focused
  useEffect(() => {
    if (!isFocus) {
      setShowPopup(false);
    }
  }, [isFocus]);

  // Update local state with the latest player money amount
  useEffect(() => {
    setCoins(playerMoney);
  }, [playerMoney]);

  useEffect(() => {
    setMax(coins);
    setStep(Math.floor(coins / 5));
  }, [coins]);

  return (
    <div className="container-gameAction">
      <div className="container-cashSituation">
        <div className="userCoinCashs">
          {getTranslatedWord("gameActionPanel.currentSC")}:{" "}
          {formatNumber(coins)}
        </div>

        {showPopup && (coinsAfterRaise || amount) && (
          <>
            <div className="userCoinCashs">
              {getTranslatedWord("gameActionPanel.afterSC")}:{" "}
              {formatNumber(coinsAfterRaise)}
            </div>
            {gamePlayerCurrentBet ? (
              <div className="userCoinCashs">
                {getTranslatedWord("gameActionPanel.totalRaise")}:{" "}
                {formatNumber(gamePlayerCurrentBet)}+{formatNumber(amount)}
              </div>
            ) : null}
          </>
        )}
      </div>
      {/* UI for bet sizing quick select buttons and the bet size input */}
      {showPopup && (
        <>
          <div className="container-raiseButtons">
            <Button
              styleClass={"btn-mainAction"}
              onClick={() => setAmount(min)}
              label={"Min"}
            />
            <Button
              styleClass={"btn-mainAction"}
              onClick={() => setAmount(Math.floor(coins / 4))}
              label={"1/4"}
            />
            <Button
              styleClass={"btn-mainAction"}
              onClick={() => setAmount(Math.floor(coins / 2))}
              label={"1/2"}
            />
            <Button
              styleClass={"btn-mainAction"}
              onClick={() => setAmount(Math.floor((coins * 3) / 4))}
              label={"3/4"}
            />
            <Button
              styleClass={"btn-mainAction"}
              onClick={() => setAmount(coins)}
              label={"All in"}
            />
          </div>
          <div className={`container-raiseAdjuster`}>
            <Button
              styleClass="btn-raiseDecrement"
              label={"−"}
              onClick={handleDecrement}
            />
            <input
              type="text"
              className="raiseValueDisplay"
              value={amount === "" ? "" : formatNumber(amount)}
              onChange={handleChange}
            />
            SC
            <Button
              styleClass="btn-raiseIncrement"
              label={"+"}
              onClick={handleIncrement}
            />
          </div>
        </>
      )}
      {/* Main action buttons for raising, checking/calling, and folding */}
      <div className={`container-ActionButtons`}>
        <Button
          styleClass={`btn-mainAction ${!isFocus && "disabled"} ${
            isFocus && amount !== 0 && showPopup && "raise"
          }`}
          onClick={handleRaise}
          label={`${getTranslatedWord("gameActionPanel.raise")}`}
        />
        <Button
          styleClass={`btn-mainAction ${!isFocus && "disabled"}`}
          onClick={handleCheckOrCall}
          label={getCheckOrCallLabel()}
        />
        <Button
          styleClass={`btn-fold btn-mainAction ${!isFocus && "disabled"}`}
          onClick={handleFold}
          label={getTranslatedWord("gameActionPanel.fold")}
        />
      </div>
    </div>
  );
};

export default GameActionButtons;