import clsx from "clsx";
import { FC, HTMLAttributes, ReactNode } from "react";

import {
  characterActions,
  CharacterUtils,
  CharClass,
  ClassUtils,
  Constants,
  FormatUtils,
  HelperUtils,
  HtmlSelectOption,
} from "@dndbeyond/character-rules-engine";
import CloseIcon from "@dndbeyond/fontawesome-cache/svgs/solid/x.svg";

import { Button } from "~/components/Button";
import { Select } from "~/components/Select";
import { useCharacterEngine } from "~/hooks/useCharacterEngine";
import { useDispatch } from "~/hooks/useDispatch";
import { useModalManager } from "~/subApps/builder/contexts/ModalManager";

import styles from "./styles.module.css";

export interface ClassHeaderProps extends HTMLAttributes<HTMLDivElement> {
  charClass: CharClass;
  isMulticlass: boolean;
  levelsRemaining: number;
}

export const ClassHeader: FC<ClassHeaderProps> = ({
  charClass,
  isMulticlass,
  levelsRemaining,
  className,
  ...props
}) => {
  const dispatch = useDispatch();
  const { preferences, ruleData, classes, currentLevel, totalClassLevel } =
    useCharacterEngine();
  const { createModal } = useModalManager();

  const portraitAvatarUrl = ClassUtils.getPortraitUrl(charClass);
  const isStartingClass = ClassUtils.isStartingClass(charClass);
  const level = ClassUtils.getLevel(charClass);
  const name = ClassUtils.getName(charClass);
  const subclassDefinition = ClassUtils.getSubclass(charClass);
  const classId = ClassUtils.getActiveId(charClass);

  const levelsDiff: number = currentLevel - totalClassLevel;
  const isTypeMilestone: boolean =
    preferences.progressionType ===
    Constants.PreferenceProgressionTypeEnum.MILESTONE;
  const isTypeXp: boolean =
    preferences.progressionType === Constants.PreferenceProgressionTypeEnum.XP;
  const hasLevelXpDiff: boolean = levelsDiff !== 0 && isTypeXp;

  let levelOptions: Array<HtmlSelectOption> = [];
  for (let i = 1; i <= level + levelsRemaining; i++) {
    levelOptions.push({
      label: "" + i,
      value: i,
    });
  }

  const handleLevelChangePromise = (
    newLevel: string,
    oldLevel: string,
    accept: () => void,
    reject: () => void
  ): void => {
    const newLevelValue = HelperUtils.parseInputInt(newLevel, 0);
    const oldLevelValue = HelperUtils.parseInputInt(oldLevel, 0);

    const newTotalClassLevel: number = classes.reduce(
      (acc: number, oldClass) =>
        (acc += oldClass.id === charClass.id ? newLevelValue : oldClass.level),
      0
    );
    const isLevelUp: boolean = newLevelValue > oldLevelValue;
    const isLevelDown: boolean = newLevelValue < oldLevelValue;

    // Modals to handle milestone
    if (isLevelUp && isTypeMilestone) {
      dispatch(characterActions.classLevelSetRequest(classId, newLevelValue));
      accept();
    }

    if (isLevelDown && isTypeMilestone) {
      createModal({
        content: (
          <div>
            {renderModalIntro(currentLevel, newTotalClassLevel)}
            <p>Are you sure you want to level down in the {name} class?</p>
            <p>
              Your hit points will be reduced by the fixed amount and class
              feature choices you have made for the higher levels will be lost.
            </p>
          </div>
        ),
        props: {
          heading: "Level Down",
          color: "secondary",
          confirmButtonText: "Level Down",
          size: "fit-content",
          onConfirm: () => {
            dispatch(
              characterActions.classLevelSetRequest(classId, newLevelValue)
            );
            accept();
          },
          onClose: () => {
            reject();
          },
        },
      });
    }

    // Modals to handle progression
    if (isLevelUp && isTypeXp) {
      if (newTotalClassLevel <= currentLevel) {
        dispatch(characterActions.classLevelSetRequest(classId, newLevelValue));
        accept();
      } else {
        createModal({
          content: (
            <div>
              {renderModalIntro(currentLevel, newTotalClassLevel)}
              <p>Are you sure you want to level up in the {name} class?</p>
              <p>
                Your XP total will be increased to{" "}
                {FormatUtils.renderLocaleNumber(
                  CharacterUtils.deriveCurrentLevelXp(
                    newTotalClassLevel,
                    ruleData
                  )
                )}{" "}
                to match your new level.
              </p>
            </div>
          ),
          props: {
            heading: "Level Up",
            confirmButtonText: "Level Up",
            size: "fit-content",
            onConfirm: () => {
              dispatch(
                characterActions.classLevelSetRequest(
                  classId,
                  newLevelValue,
                  CharacterUtils.deriveCurrentLevelXp(
                    newTotalClassLevel,
                    ruleData
                  )
                )
              );
              accept();
            },
            onClose: () => {
              reject();
            },
          },
        });
      }
    }

    if (isLevelDown && isTypeXp) {
      createModal({
        content: (
          <div>
            {renderModalIntro(currentLevel, newTotalClassLevel)}
            <p>Are you sure you want to level down in the {name} class?</p>
            <p>
              Your hit points will be reduced by the fixed amount and class
              feature choices you have made for the higher levels will be lost.
            </p>
            <p>
              Your XP total will be decreased to{" "}
              {FormatUtils.renderLocaleNumber(
                CharacterUtils.deriveCurrentLevelXp(
                  newTotalClassLevel,
                  ruleData
                )
              )}{" "}
              to match your new level.
            </p>
          </div>
        ),
        props: {
          heading: "Level Down",
          confirmButtonText: "Level Down",
          color: "secondary",
          size: "fit-content",
          onConfirm: () => {
            dispatch(
              characterActions.classLevelSetRequest(
                classId,
                newLevelValue,
                CharacterUtils.deriveCurrentLevelXp(
                  newTotalClassLevel,
                  ruleData
                )
              )
            );
            accept();
          },
          onClose: () => {
            reject();
          },
        },
      });
    }
  };

  const handleRemoveClass = (): void => {
    const newTotalClassLevel: number = classes.reduce(
      (acc: number, oldClass) =>
        (acc += oldClass.id === charClass.id ? 0 : oldClass.level),
      0
    );

    createModal({
      content: (
        <div>
          {renderModalIntro()}
          <p>
            Are you sure you want to remove all your levels in the {name} class?
          </p>
          <p>
            Your hit points will be reduced by the fixed amount and class
            feature choices you have made for the higher levels will be lost.
          </p>
          {isTypeXp && (
            <p>Your XP total will be decreased to match your new level.</p>
          )}
        </div>
      ),
      props: {
        heading: "Remove Class",
        variant: "remove",
        size: "fit-content",
        confirmButtonText: "Remove",
        onConfirm: () => {
          let newCharacterXp: number | null = isTypeXp
            ? CharacterUtils.deriveCurrentLevelXp(newTotalClassLevel, ruleData)
            : null;
          dispatch(
            characterActions.classRemoveRequest(charClass.id, newCharacterXp)
          );
        },
        onClose: () => {}, //Do nothing
      },
    });
  };

  const renderModalIntro = (
    currentLevel: number | null = null,
    newLevel: number | null = null
  ): React.ReactNode => {
    return (
      <div className={styles.modalIntro}>
        {classPortraitName}
        {currentLevel !== null && newLevel !== null && (
          <div className={styles.modalLevels}>
            <div className={styles.modalLevel}>
              Current Level: {currentLevel}
            </div>
            <div className={styles.modalLevel}>New Level: {newLevel}</div>
          </div>
        )}
      </div>
    );
  };

  const classPortraitName: ReactNode = (
    <>
      <img
        className={styles.classImg}
        src={portraitAvatarUrl}
        alt={`${name} Class avatar`}
      />
      <div className={styles.nameGroup}>
        {subclassDefinition && (
          <div className={styles.subclassName}>{subclassDefinition.name}</div>
        )}
        <div className={styles.name} data-testid="class-name">
          {name}
        </div>
      </div>
    </>
  );

  return (
    <div
      className={clsx([styles.classHeader, styles.container, className])}
      {...props}
    >
      {isMulticlass && isStartingClass && (
        <div className={styles.startingClass}>Starting Class</div>
      )}
      {classPortraitName}
      <div className={styles.container}>
        <div className={styles.levelManager}>
          <label
            className={styles.levelLabel}
            htmlFor={`class-level-${classId}`}
          >
            {hasLevelXpDiff && <span className={styles.todoIcon}>!</span>}
            Level
          </label>
          <Select
            className={styles.levelSelect}
            id={`class-level-${classId}`}
            value={level}
            options={levelOptions}
            onChangeConfirm={handleLevelChangePromise}
            name={`class-level-select-${classId}`}
            searchThreshold={null}
            hidePlaceholderOption
          />
        </div>
        <Button
          className={styles.removeClassButton}
          size="x-small"
          variant="text"
          onClick={handleRemoveClass}
          forceThemeMode="light"
        >
          <CloseIcon className={styles.icon} />
        </Button>
      </div>
    </div>
  );
};
