import * as React from "react";
import { Dispatch, SetStateAction } from "react";

import { Tooltip } from "@dndbeyond/character-common-components/es";
import {
  ConditionUtils,
  DiceUtils,
  HigherLevelContract,
  HigherLevelEntryContract,
  ModifierUtils,
  RuleData,
  RuleDataUtils,
  Spell,
  SpellConditionContract,
  SpellModifierContract,
  SpellUtils,
  CharacterTheme,
} from "@dndbeyond/character-rules-engine/es";
import { GameLogContext } from "@dndbeyond/game-log-components";
import {
  RollKinds,
  RollTypes,
} from "@dndbeyond/pocket-dimension-dice/constants";
import { RollContext } from "@dndbeyond/pocket-dimension-dice/types";

import Damage from "../Damage";
import { DigitalDiceWrapper } from "../Dice";
import { HealingIcon } from "../Icons";
import { DiceComponentUtils } from "../utils";

interface Props {
  spell: Spell;
  castLevel: number;
  characterLevel: number;
  ruleData: RuleData;
  diceEnabled: boolean;
  theme: CharacterTheme;
  isCriticalHit?: boolean;
  rollContext: RollContext;
  rollCallback: () => void;
}
class SpellDamageEffect extends React.PureComponent<Props> {
  static defaultProps = {
    diceEnabled: false,
  };

  getPoints = (
    atHigherLevels: HigherLevelContract | null
  ): Array<HigherLevelEntryContract> => {
    let points: Array<HigherLevelEntryContract> = [];
    if (atHigherLevels && atHigherLevels.points !== null) {
      points = atHigherLevels.points;
    }
    return points;
  };

  render() {
    const {
      spell,
      castLevel,
      characterLevel,
      ruleData,
      diceEnabled,
      theme,
      isCriticalHit,
      rollContext,
      rollCallback,
    } = this.props;

    let isScaled: boolean = false;
    const modifiers = SpellUtils.getModifiers(spell);
    const tags = SpellUtils.getTags(spell);
    const conditions = SpellUtils.getConditions(spell);

    let displayNode: React.ReactNode;
    let damageModifiers = modifiers.filter((modifier) =>
      ModifierUtils.isSpellDamageModifier(modifier)
    );
    if (damageModifiers.length) {
      let modifier: SpellModifierContract = damageModifiers[0];
      const friendlySubtypeName =
        ModifierUtils.getFriendlySubtypeName(modifier);
      const atHigherLevels = ModifierUtils.getAtHigherLevels(modifier);
      let scaleType = SpellUtils.getScaleType(spell);
      let points = this.getPoints(atHigherLevels);
      let atHigherDamage = SpellUtils.getSpellScaledAtHigher(
        spell,
        scaleType,
        points,
        characterLevel,
        castLevel
      );
      let scaledDamageDie = SpellUtils.getSpellFinalScaledDie(
        spell,
        modifier,
        atHigherDamage
      );
      isScaled =
        isScaled ||
        SpellUtils.isSpellScaledByCastLevel(
          spell,
          scaleType,
          points,
          castLevel
        );

      displayNode = (
        <span className="ddbc-spell-damage-effect__damages">
          <DigitalDiceWrapper
            diceNotation={DiceUtils.renderDice(scaledDamageDie)}
            rollType={RollTypes.Damage}
            action={SpellUtils.getName(spell)}
            rollKind={isCriticalHit ? RollKinds.CriticalHit : undefined}
            isDiceEnabled={diceEnabled}
            entityId={String(rollContext.entityId)}
            entityType={String(rollContext.entityType)}
            name={String(rollContext.name)}
            rollCallback={rollCallback}
          >
            <Damage
              damage={DiceComponentUtils.getDamageDiceNotation(
                scaledDamageDie,
                isCriticalHit
              )}
              type={friendlySubtypeName || ""}
              showInfoTooltip={false}
              theme={theme}
            />
          </DigitalDiceWrapper>
        </span>
      );
    }

    let hitPointHealingModifiers = modifiers.filter((modifier) =>
      ModifierUtils.isSpellHealingHitPointsModifier(modifier)
    );
    if (!displayNode && hitPointHealingModifiers.length) {
      let modifier: SpellModifierContract = hitPointHealingModifiers[0];
      const atHigherLevels = ModifierUtils.getAtHigherLevels(modifier);
      let scaleType = SpellUtils.getScaleType(spell);
      let points = this.getPoints(atHigherLevels);
      let atHigherHealing = SpellUtils.getSpellScaledAtHigher(
        spell,
        scaleType,
        points,
        characterLevel,
        castLevel
      );
      let scaledHealingDie = SpellUtils.getSpellFinalScaledDie(
        spell,
        modifier,
        atHigherHealing,
        castLevel
      );
      isScaled =
        isScaled ||
        SpellUtils.isSpellScaledByCastLevel(
          spell,
          scaleType,
          points,
          castLevel
        );

      displayNode = (
        <span className="ddbc-spell-damage-effect__healing ddbc-spell-damage-effect__healing--hp">
          <DigitalDiceWrapper
            isContextMenuEnabled={false}
            diceNotation={DiceUtils.renderDice(scaledHealingDie)}
            rollType={RollTypes.Heal}
            action={SpellUtils.getName(spell)}
            isDiceEnabled={diceEnabled}
            entityId={String(rollContext.entityId)}
            entityType={String(rollContext.entityType)}
            name={String(rollContext.name)}
          >
            {DiceUtils.renderDice(scaledHealingDie)}{" "}
            <HealingIcon theme={theme} isHp={true} />
          </DigitalDiceWrapper>
        </span>
      );
    }

    const tempHitPointHealingModifiers = modifiers.filter((modifier) =>
      ModifierUtils.isSpellHealingTempHitPointsModifier(modifier)
    );
    if (!displayNode && tempHitPointHealingModifiers.length) {
      let modifier = tempHitPointHealingModifiers[0];
      const atHigherLevels = ModifierUtils.getAtHigherLevels(modifier);
      let scaleType = SpellUtils.getScaleType(spell);
      let points = this.getPoints(atHigherLevels);
      let atHigherHealing = SpellUtils.getSpellScaledAtHigher(
        spell,
        scaleType,
        points,
        characterLevel,
        castLevel
      );
      let scaledHealingDie = SpellUtils.getSpellFinalScaledDie(
        spell,
        modifier,
        atHigherHealing,
        castLevel
      );
      isScaled =
        isScaled ||
        SpellUtils.isSpellScaledByCastLevel(
          spell,
          scaleType,
          points,
          castLevel
        );

      displayNode = (
        <span className="ddbc-spell-damage-effect__healing ddbc-spell-damage-effect__healing--temp">
          <DigitalDiceWrapper
            isContextMenuEnabled={false}
            diceNotation={DiceUtils.renderDice(scaledHealingDie)}
            rollType={RollTypes.Heal}
            action={SpellUtils.getName(spell)}
            isDiceEnabled={diceEnabled}
            entityId={String(rollContext.entityId)}
            entityType={String(rollContext.entityType)}
            name={String(rollContext.name)}
          >
            {DiceUtils.renderDice(scaledHealingDie)}{" "}
            <HealingIcon theme={theme} isTemp={true} />
          </DigitalDiceWrapper>
        </span>
      );
    }

    if (!displayNode && conditions.length) {
      let spellCondition: SpellConditionContract = conditions[0];
      let condition = RuleDataUtils.getCondition(
        spellCondition.conditionId,
        ruleData
      );
      let name: string =
        condition === null ? "" : ConditionUtils.getName(condition);
      displayNode = (
        <span
          className={`ddbc-spell-damage-effect__conditions ${
            theme.isDarkMode
              ? "ddbc-spell-damage-effect__conditions--dark-mode"
              : ""
          }`}
        >
          <Tooltip
            isDarkMode={theme.isDarkMode}
            title={`${name}${
              spellCondition.exception ? `, ${spellCondition.exception}` : ""
            }`}
          >
            {name}
          </Tooltip>
        </span>
      );
    }

    if (!displayNode && tags.length) {
      displayNode = (
        <span
          className={`ddbc-spell-damage-effect__tags ${
            theme.isDarkMode ? "ddbc-spell-damage-effect__tags--dark-mode" : ""
          }`}
        >
          {tags[0]}
        </span>
      );
    }

    let hasMore: boolean = damageModifiers.length > 1;
    hasMore = hasMore || hitPointHealingModifiers.length > 1;
    hasMore = hasMore || tempHitPointHealingModifiers.length > 1;
    hasMore = hasMore || conditions.length > 1;
    hasMore = hasMore || tags.length > 1;

    return (
      <div
        className={`ddbc-spell-damage-effect  ${
          isScaled ? "ddbc-spell-damage-effect--scaled" : ""
        } ${theme.isDarkMode ? "ddbc-spell-damage-effect--dark-mode" : ""}`}
      >
        {displayNode}
        {hasMore ? "*" : ""}
      </div>
    );
  }
}

SpellDamageEffect.contextType = GameLogContext;

export default SpellDamageEffect;
