import clsx from "clsx";
import { FC, FocusEvent, useMemo, useState } from "react";
import { useDispatch } from "react-redux";

import {
  characterActions,
  ClassUtils,
  Constants,
  DiceUtils,
  FormatUtils,
  HelperUtils,
  RuleDataUtils,
} from "@dndbeyond/character-rules-engine";

import { ConfirmModal, ConfirmModalProps } from "~/components/ConfirmModal";
import { useCharacterEngine } from "~/hooks/useCharacterEngine";
import {
  HP_BASE_MAX_VALUE,
  HP_BONUS_VALUE,
  HP_OVERRIDE_MAX_VALUE,
} from "~/subApps/sheet/constants";

import { InputField } from "../InputField";
import styles from "./styles.module.css";

export interface HpManageModalProps
  extends Omit<ConfirmModalProps, "onConfirm"> {}

export const HpManageModal: FC<HpManageModalProps> = ({
  onClose,
  ...props
}) => {
  const dispatch = useDispatch();
  const { hpInfo, preferences, ruleData } = useCharacterEngine();

  const [baseHp, setBaseHp] = useState(hpInfo.baseHp);
  const [bonusHp, setBonusHp] = useState(hpInfo.bonusHp);
  const [overrideHp, setOverrideHp] = useState(hpInfo.overrideHp);

  const reset = () => {
    setBaseHp(hpInfo.baseHp);
    setBonusHp(hpInfo.bonusHp);
    setOverrideHp(hpInfo.overrideHp);
  };

  const handleClose = () => {
    onClose();
    reset();
  };

  const onConfirm = () => {
    if (baseHp !== hpInfo.baseHp) {
      dispatch(characterActions.baseHitPointsSet(baseHp));
    }

    if (bonusHp !== hpInfo.bonusHp) {
      dispatch(characterActions.bonusHitPointsSet(bonusHp));
    }

    if (overrideHp !== hpInfo.overrideHp) {
      dispatch(characterActions.overrideHitPointsSet(overrideHp));
    }

    onClose();
    reset();
  };

  const handleBaseHp = (e: FocusEvent<HTMLInputElement>) => {
    let parsedNumber = HelperUtils.parseInputInt(
      e.target.value,
      RuleDataUtils.getMinimumHpTotal(ruleData)
    );

    const clampedValue = HelperUtils.clampInt(
      parsedNumber,
      RuleDataUtils.getMinimumHpTotal(ruleData),
      HP_BASE_MAX_VALUE
    );

    setBaseHp(clampedValue);
  };

  const handleBonusHp = (e: FocusEvent<HTMLInputElement>) => {
    let parsedNumber = HelperUtils.parseInputInt(e.target.value);

    if (parsedNumber === null) return parsedNumber;

    const clampedValue = HelperUtils.clampInt(
      parsedNumber,
      HP_BONUS_VALUE.MIN,
      HP_BONUS_VALUE.MAX
    );

    setBonusHp(clampedValue);
  };

  const handleOverrideHp = (e: FocusEvent<HTMLInputElement>) => {
    let parsedNumber = HelperUtils.parseInputInt(e.target.value, null);

    if (parsedNumber === null) {
      setOverrideHp(null);
      return parsedNumber;
    }

    const clampedValue = HelperUtils.clampInt(
      parsedNumber,
      RuleDataUtils.getMinimumHpTotal(ruleData)
    );

    const clampedOverride = HelperUtils.clampInt(
      clampedValue,
      RuleDataUtils.getMinimumHpTotal(ruleData),
      HP_OVERRIDE_MAX_VALUE
    );

    setOverrideHp(clampedOverride);
  };

  const totalHp = useMemo(() => {
    let totalHp: number =
      baseHp + hpInfo.totalHitPointSources + (bonusHp === null ? 0 : bonusHp);
    if (overrideHp !== null) {
      totalHp = overrideHp;
    }

    return Math.max(RuleDataUtils.getMinimumHpTotal(ruleData), totalHp);
  }, [baseHp, bonusHp, overrideHp, hpInfo, ruleData]);

  return (
    <ConfirmModal
      onClose={handleClose}
      heading="Manage Hit Points"
      onConfirm={onConfirm}
      confirmButtonText="Apply"
      useMobileFullScreen
      {...props}
    >
      {/* Total Max Hit Points */}
      <div className={styles.hpManageModal}>
        <p className={styles.total}>
          <span className={styles.totalLabel}>Maximum Hit Points</span>
          <span className={styles.totalValue}>{totalHp}</span>
        </p>

        {/* Hit Point Controls */}
        <div className={styles.controls}>
          {preferences.hitPointType ===
          Constants.PreferenceHitPointTypeEnum.FIXED ? (
            <p className={styles.baseHp}>
              <span className={clsx([styles.baseHpLabel])}>Fixed HP</span>
              <span className={styles.baseHpValue}>{baseHp}</span>
            </p>
          ) : (
            <InputField
              className={styles.inputField}
              label="Rolled HP"
              initialValue={baseHp}
              type="number"
              onBlur={handleBaseHp}
            />
          )}
          <InputField
            className={styles.inputField}
            label="HP Modifier"
            initialValue={bonusHp}
            type="number"
            placeholder="--"
            onBlur={handleBonusHp}
          />
          <InputField
            className={styles.inputField}
            label="Override HP"
            initialValue={overrideHp}
            type="number"
            placeholder={"--"}
            inputProps={{
              min: RuleDataUtils.getMinimumHpTotal(ruleData),
              max: HP_OVERRIDE_MAX_VALUE,
            }}
            onBlur={handleOverrideHp}
          />
        </div>

        {/* Hit Point Bonus Sources */}
        {hpInfo.hitPointSources.length > 0 && (
          <div className={styles.hpSources}>
            <h3>Hit Point Bonuses</h3>
            {hpInfo.hitPointSources.map((hitPointSource, idx) => (
              <p key={`${idx}:${hitPointSource.source}`}>
                {FormatUtils.renderSignedNumber(hitPointSource.amount)} from{" "}
                {hitPointSource.source}
              </p>
            ))}
          </div>
        )}

        {/* Hit Dice & Potential Values */}
        <div className={styles.info}>
          <div className={styles.infoGroup}>
            <h3>Hit Dice</h3>
            {hpInfo.classesHitDice.map((classHitDice) => (
              <div key={ClassUtils.getId(classHitDice.charClass)}>
                <span className={styles.infoLabel}>
                  {ClassUtils.getName(classHitDice.charClass)}:
                </span>
                <span>{DiceUtils.renderDie(classHitDice.dice)}</span>
              </div>
            ))}
          </div>
          <div className={styles.infoGroup}>
            <h3>Potential Values</h3>
            <div>
              <span className={styles.infoLabel}>Total Fixed Value HP:</span>
              <span>{hpInfo.totalFixedValueHp}</span>
            </div>
            <div>
              <span className={styles.infoLabel}>Total Average HP:</span>
              <span>{hpInfo.totalAverageHp}</span>
            </div>
            <div>
              <span className={styles.infoLabel}>Total Possible HP:</span>
              <span>{hpInfo.possibleMaxHitPoints}</span>
            </div>
          </div>
        </div>

        {/* HP Help */}
        <div className={styles.help}>
          <h3>Max Hit Points</h3>
          <p>
            Your hit point maximum is determined by the number you roll for your
            hit dice each level or a fixed value determined by your hit dice and
            your Constitution modifier
          </p>

          <h3>Bonus Hit Points</h3>
          <p>
            Use this field to record any miscellaneous bonus hit points you want
            to add to your normal hit point maximum. These hit points are
            different from temporary hit points, which you can add on your
            character sheet during play.
          </p>

          <h3>Override Hit Points</h3>
          <p>
            Use this field to override your typical hit point maximum. The
            number you enter here will display as your hit point maximum on your
            character sheet.
          </p>
        </div>
      </div>
    </ConfirmModal>
  );
};
