import clsx from "clsx";
import React, { useContext } from "react";
import { connect, DispatchProp } from "react-redux";
import { AnyAction } from "redux";

import {
  AlignmentContract,
  Background,
  CharacterTraits,
  Constants,
  SizeContract,
  rulesEngineSelectors,
  CharacterTheme,
  CharacterFeaturesManager,
  SnippetData,
  RuleData,
  AbilityLookup,
  DataOriginRefData,
  FeatManager,
  Action,
  ActionUtils,
  Spell,
  SpellUtils,
} from "@dndbeyond/character-rules-engine/es";

import { InfoItem } from "~/components/InfoItem";
import { NumberDisplay } from "~/components/NumberDisplay";
import { TabFilter } from "~/components/TabFilter";
import { useSidebar } from "~/contexts/Sidebar";
import { PaneInfo } from "~/contexts/Sidebar/Sidebar";
import a11yStyles from "~/styles/accessibility.module.css";
import { PaneComponentEnum } from "~/subApps/sheet/components/Sidebar/types";
import { CharacterFeaturesManagerContext } from "~/tools/js/Shared/managers/CharacterFeaturesManagerContext";

import {
  handleActionUseSet,
  handleSpellUseSet,
} from "../../../../../handlers/commonHandlers";
import { appEnvSelectors } from "../../../Shared/selectors";
import { PaneIdentifierUtils } from "../../../Shared/utils";
import BackgroundDetail from "../../components/BackgroundDetail";
import ContentGroup from "../../components/ContentGroup";
import TraitContent from "../../components/TraitContent";
import { SheetAppState } from "../../typings";
import styles from "./styles.module.css";

const DEFAULT_VALUE = "--";

// TODO: We are typing with AnyAction here to work around issues with UnknownAction, the type that was introduced in Redux 5.
// If we decide to modernize to Redux 5, we will need to refactor much of the Rules Engine redux usage.
interface Props extends DispatchProp<AnyAction> {
  isVertical: boolean;
  background: Background | null;
  alignment: AlignmentContract | null;
  height: string | null;
  weight: number | null;
  size: SizeContract | null;
  faith: string | null;
  skin: string | null;
  eyes: string | null;
  hair: string | null;
  age: number | null;
  gender: string | null;
  traits: CharacterTraits;
  isReadonly: boolean;
  theme: CharacterTheme;
  characterFeaturesManager: CharacterFeaturesManager;
  snippetData: SnippetData;
  ruleData: RuleData;
  abilityLookup: AbilityLookup;
  dataOriginRefData: DataOriginRefData;
  proficiencyBonus: number;
  paneContext: PaneInfo;
}
class Description extends React.PureComponent<Props> {
  static defaultProps = {
    isVertical: false,
  };

  handlePhysicalCharacteristicsClick = (): void => {
    const {
      paneContext: { paneHistoryStart },
      isReadonly,
    } = this.props;

    if (!isReadonly) {
      paneHistoryStart(PaneComponentEnum.DESCRIPTION);
    }
  };

  handleBackgroundClick = (): void => {
    const {
      paneContext: { paneHistoryStart },
    } = this.props;

    paneHistoryStart(PaneComponentEnum.BACKGROUND);
  };

  handleFeatClick = (feat: FeatManager): void => {
    const {
      paneContext: { paneHistoryStart },
    } = this.props;

    paneHistoryStart(
      PaneComponentEnum.FEAT_DETAIL,
      PaneIdentifierUtils.generateFeat(feat.getId())
    );
  };

  handleTraitShow = (key: Constants.TraitTypeEnum): void => {
    const {
      paneContext: { paneHistoryStart },
      isReadonly,
    } = this.props;

    if (!isReadonly) {
      paneHistoryStart(
        PaneComponentEnum.TRAIT,
        PaneIdentifierUtils.generateTrait(key)
      );
    }
  };

  handleActionClick = (action: Action): void => {
    const {
      paneContext: { paneHistoryStart },
    } = this.props;

    const mappingId = ActionUtils.getMappingId(action);
    const mappingEntityTypeId = ActionUtils.getMappingEntityTypeId(action);

    if (mappingId !== null && mappingEntityTypeId !== null) {
      paneHistoryStart(
        PaneComponentEnum.ACTION,
        PaneIdentifierUtils.generateAction(mappingId, mappingEntityTypeId)
      );
    }
  };

  handleSpellDetailClick = (spell: Spell): void => {
    const {
      paneContext: { paneHistoryStart },
    } = this.props;

    const mappingId = SpellUtils.getMappingId(spell);
    if (mappingId !== null) {
      paneHistoryStart(
        PaneComponentEnum.CHARACTER_SPELL_DETAIL,
        PaneIdentifierUtils.generateCharacterSpell(mappingId)
      );
    }
  };

  handleClick = (e: React.MouseEvent, onClick: Function): void => {
    e.stopPropagation();
    e.nativeEvent.stopImmediatePropagation();
    onClick();
  };

  renderDescriptionItem = (
    value: number | string | null,
    fallback: string = DEFAULT_VALUE
  ): React.ReactNode => {
    return value === null ? fallback : value;
  };

  render() {
    const {
      dispatch,
      background,
      characterFeaturesManager,
      snippetData,
      ruleData,
      abilityLookup,
      dataOriginRefData,
      isReadonly,
      proficiencyBonus,
      theme,
      isVertical,
      size,
      height,
      weight,
      alignment,
      gender,
      eyes,
      hair,
      skin,
      age,
      faith,
      traits,
    } = this.props;

    return (
      <section className="ct-description">
        <h2 className={a11yStyles.screenreaderOnly}>Description</h2>
        <TabFilter
          filters={[
            {
              label: "Background",
              content: (
                <ContentGroup header="Background">
                  <BackgroundDetail
                    background={background}
                    onClick={this.handleBackgroundClick}
                    featuresManager={characterFeaturesManager}
                    onActionUseSet={(action, uses) =>
                      handleActionUseSet(action, uses, dispatch)
                    }
                    onSpellUseSet={(spell, uses) =>
                      handleSpellUseSet(spell, uses, dispatch)
                    }
                    snippetData={snippetData}
                    ruleData={ruleData}
                    abilityLookup={abilityLookup}
                    dataOriginRefData={dataOriginRefData}
                    isReadonly={isReadonly}
                    proficiencyBonus={proficiencyBonus}
                    theme={theme}
                  />
                </ContentGroup>
              ),
            },
            {
              label: "Characteristics",
              content: (
                <>
                  <ContentGroup header="Characteristics">
                    <div
                      className={clsx([
                        "ct-description__physical",
                        styles.physical,
                      ])}
                      role="list"
                      onClick={(e) =>
                        this.handleClick(
                          e,
                          this.handlePhysicalCharacteristicsClick
                        )
                      }
                    >
                      <InfoItem label="Alignment" inline={isVertical}>
                        {alignment === null ? DEFAULT_VALUE : alignment.name}
                      </InfoItem>
                      <InfoItem label="Gender" inline={isVertical}>
                        {this.renderDescriptionItem(gender)}
                      </InfoItem>
                      <InfoItem label="Eyes" inline={isVertical}>
                        {this.renderDescriptionItem(eyes)}
                      </InfoItem>
                      <InfoItem label="Size" inline={isVertical}>
                        {this.renderDescriptionItem(size ? size.name : null)}
                      </InfoItem>
                      <InfoItem label="Height" inline={isVertical}>
                        {this.renderDescriptionItem(height)}
                      </InfoItem>
                      <InfoItem label="Faith" inline={isVertical}>
                        {this.renderDescriptionItem(faith)}
                      </InfoItem>
                      <InfoItem label="Hair" inline={isVertical}>
                        {this.renderDescriptionItem(hair)}
                      </InfoItem>
                      <InfoItem label="Skin" inline={isVertical}>
                        {this.renderDescriptionItem(skin)}
                      </InfoItem>
                      <InfoItem label="Age" inline={isVertical}>
                        {this.renderDescriptionItem(age)}
                      </InfoItem>
                      <InfoItem label="Weight" inline={isVertical}>
                        {weight === null ? (
                          DEFAULT_VALUE
                        ) : (
                          <NumberDisplay type="weightInLb" number={weight} />
                        )}
                      </InfoItem>
                    </div>
                  </ContentGroup>
                  <div
                    className="ct-description__traits"
                    style={
                      theme.isDarkMode
                        ? { borderColor: `${theme.themeColor}66` }
                        : undefined
                    }
                  >
                    <TraitContent
                      traits={traits}
                      traitKey={Constants.TraitTypeEnum.PERSONALITY_TRAITS}
                      label="Personality Traits"
                      fallback="+ Add Personality Traits"
                      onClick={this.handleTraitShow}
                    />
                    <TraitContent
                      traits={traits}
                      traitKey={Constants.TraitTypeEnum.IDEALS}
                      label="Ideals"
                      fallback="+ Add Ideals"
                      onClick={this.handleTraitShow}
                    />
                    <TraitContent
                      traits={traits}
                      traitKey={Constants.TraitTypeEnum.BONDS}
                      label="Bonds"
                      fallback="+ Add Bonds"
                      onClick={this.handleTraitShow}
                    />
                    <TraitContent
                      traits={traits}
                      traitKey={Constants.TraitTypeEnum.FLAWS}
                      label="Flaws"
                      fallback="+ Add Flaws"
                      onClick={this.handleTraitShow}
                    />
                  </div>
                </>
              ),
            },
            ...(traits !== null
              ? [
                  {
                    label: "Appearance",
                    content: (
                      <ContentGroup header="Appearance">
                        <TraitContent
                          traits={traits}
                          traitKey={Constants.TraitTypeEnum.APPEARANCE}
                          fallback="+ Add Appearance information"
                          onClick={this.handleTraitShow}
                        />
                      </ContentGroup>
                    ),
                  },
                ]
              : []),
          ]}
        />
      </section>
    );
  }
}

function mapStateToProps(state: SheetAppState) {
  return {
    background: rulesEngineSelectors.getBackgroundInfo(state),
    alignment: rulesEngineSelectors.getAlignment(state),
    height: rulesEngineSelectors.getHeight(state),
    weight: rulesEngineSelectors.getWeight(state),
    size: rulesEngineSelectors.getSize(state),
    faith: rulesEngineSelectors.getFaith(state),
    skin: rulesEngineSelectors.getSkin(state),
    eyes: rulesEngineSelectors.getEyes(state),
    hair: rulesEngineSelectors.getHair(state),
    age: rulesEngineSelectors.getAge(state),
    gender: rulesEngineSelectors.getGender(state),
    traits: rulesEngineSelectors.getCharacterTraits(state),
    isReadonly: appEnvSelectors.getIsReadonly(state),
    snippetData: rulesEngineSelectors.getSnippetData(state),
    ruleData: rulesEngineSelectors.getRuleData(state),
    abilityLookup: rulesEngineSelectors.getAbilityLookup(state),
    dataOriginRefData: rulesEngineSelectors.getDataOriginRefData(state),
    proficiencyBonus: rulesEngineSelectors.getProficiencyBonus(state),
  };
}

const DescriptionWrapper = (props) => {
  const { characterFeaturesManager } = useContext(
    CharacterFeaturesManagerContext
  );
  const { pane } = useSidebar();

  return (
    <Description
      paneContext={pane}
      characterFeaturesManager={characterFeaturesManager}
      {...props}
    />
  );
};

export default connect(mapStateToProps)(DescriptionWrapper);
