import React from "react";

import {
  ApiAdapterPromise,
  ApiAdapterRequestConfig,
  ApiResponse,
  CharacterPreferences,
  ChoiceData,
  ChoiceUtils,
  Constants,
  CoreUtils,
  DefinitionPool,
  FeatDefinitionContract,
  FeatLookup,
  HelperUtils,
  OptionalOriginLookup,
  OptionalOriginUtils,
  PrerequisiteData,
  RacialTrait,
  RacialTraitUtils,
} from "@dndbeyond/character-rules-engine/es";

import { Accordion } from "~/components/Accordion";
import { FeatureChoices } from "~/components/FeatureChoices";
import { HtmlContent } from "~/components/HtmlContent";

interface Props {
  speciesTrait: RacialTrait;
  choiceInfo: ChoiceData;
  preferences: CharacterPreferences;
  prerequisiteData: PrerequisiteData;
  featLookup: FeatLookup;
  onChoiceChange: (
    speciesTraitId: number,
    choiceId: string,
    type: number,
    value: any
  ) => void;
  loadAvailableFeats: (
    additionalConfig?: Partial<ApiAdapterRequestConfig>
  ) => ApiAdapterPromise<ApiResponse<Array<FeatDefinitionContract>>>;
  optionalOriginLookup: OptionalOriginLookup;
  definitionPool: DefinitionPool;
  speciesName?: string | null;
}
interface State {
  collapsibleOpened: boolean;
}
export default class SpeciesManageSpeciesTrait extends React.PureComponent<
  Props,
  State
> {
  constructor(props: Props) {
    super(props);

    this.state = {
      collapsibleOpened: false,
    };
  }

  getSummary = () => {
    const { speciesTrait, speciesName } = this.props;
    // Get base name for the species trait
    let name = RacialTraitUtils.getName(speciesTrait);
    // Get the feature type for the species trait
    const featureType = RacialTraitUtils.getFeatureType(speciesTrait);
    // Get the display configuration for the species trait
    const displayConfiguration =
      RacialTraitUtils.getDisplayConfiguration(speciesTrait);
    // Determine if we should add the species name to the name
    const addName = displayConfiguration
      ? CoreUtils.getDisplayConfigurationValue(
          Constants.DisplayConfigurationTypeEnum.RACIAL_TRAIT,
          displayConfiguration
        ) === Constants.DisplayConfigurationValueEnum.ON
      : false;
    // Add the species name to the name if necessary
    if (speciesName && addName) name = `${name} (${speciesName})`;
    // Add "origin" to the name if it's an optional origin replacement feature
    if (featureType === Constants.FeatureTypeEnum.REPLACEMENT)
      name = `Origin ${name}`;
    // Return constructed name
    return name;
  };

  getMetaItems = () => {
    const { speciesTrait, optionalOriginLookup, definitionPool } = this.props;
    const displayConfiguration =
      RacialTraitUtils.getDisplayConfiguration(speciesTrait);
    const choices = RacialTraitUtils.getChoices(speciesTrait);
    let metaItems: Array<string> = [];
    if (choices.length) {
      metaItems.push(
        `${choices.length} Choice${choices.length !== 1 ? "s" : ""}`
      );
    }
    const featureType = RacialTraitUtils.getFeatureType(speciesTrait);
    const addOrigin = displayConfiguration
      ? CoreUtils.getDisplayConfigurationValue(
          Constants.DisplayConfigurationTypeEnum.RACIAL_TRAIT,
          displayConfiguration
        ) === Constants.DisplayConfigurationValueEnum.OFF
      : false;
    if (featureType !== Constants.FeatureTypeEnum.GRANTED || addOrigin) {
      metaItems.push("Origin");
    }
    if (featureType === Constants.FeatureTypeEnum.REPLACEMENT) {
      const optionalOrigin = HelperUtils.lookupDataOrFallback(
        optionalOriginLookup,
        RacialTraitUtils.getDefinitionKey(speciesTrait)
      );
      if (optionalOrigin) {
        const affectedDefinitionKey =
          OptionalOriginUtils.getAffectedRacialTraitDefinitionKey(
            optionalOrigin
          );
        if (affectedDefinitionKey) {
          const replacedSpeciesTrait = RacialTraitUtils.simulateRacialTrait(
            affectedDefinitionKey,
            definitionPool
          );
          if (replacedSpeciesTrait) {
            metaItems.push(
              `Replaces ${RacialTraitUtils.getName(replacedSpeciesTrait)}`
            );
          }
        }
      }
    }

    return metaItems;
  };

  hasFeatChoice = (props: Props): boolean => {
    const { speciesTrait } = props;

    const choices = RacialTraitUtils.getChoices(speciesTrait);
    return choices.some(
      (choice) =>
        ChoiceUtils.getType(choice) ===
        Constants.BuilderChoiceTypeEnum.FEAT_CHOICE_OPTION
    );
  };

  handleCollapsibleOpened = (id: string, state: boolean): void => {
    this.setState({
      collapsibleOpened: state,
    });
  };

  handleChoiceChange = (choiceId: string, type: number, value: any): void => {
    const { onChoiceChange, speciesTrait } = this.props;

    if (onChoiceChange) {
      onChoiceChange(
        RacialTraitUtils.getId(speciesTrait),
        choiceId,
        type,
        HelperUtils.parseInputInt(value)
      );
    }
  };

  render() {
    const { speciesTrait } = this.props;
    const { collapsibleOpened } = this.state;

    const description = RacialTraitUtils.getDescription(speciesTrait);
    const choices = RacialTraitUtils.getChoices(speciesTrait);
    const accordionId = RacialTraitUtils.getId(speciesTrait).toString();

    const hasTodoChoices: boolean = choices.some(
      (choice) => ChoiceUtils.getOptionValue(choice) === null
    );

    const conClsNames: Array<string> = ["race-detail-racial-trait-name"];
    if (hasTodoChoices) {
      conClsNames.push("collapsible-todo");
    }

    return (
      <Accordion
        id={accordionId}
        summary={this.getSummary()}
        summaryMetaItems={this.getMetaItems()}
        variant="paper"
        showAlert={hasTodoChoices}
        handleIsOpen={this.handleCollapsibleOpened}
      >
        <HtmlContent
          className="race-detail-racial-trait-description"
          html={description ? description : ""}
          withoutTooltips
        />
        <FeatureChoices
          choices={RacialTraitUtils.getChoices(speciesTrait)}
          onChoiceChange={this.handleChoiceChange}
          shouldFetch={collapsibleOpened}
        />
      </Accordion>
    );
  }
}
