import clsx from "clsx";
import { FC, HTMLAttributes, ReactNode } from "react";
import { useSelector } from "react-redux";
import { useSearchParams } from "react-router-dom";

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

import { CheckboxGroup, CheckboxInfo } from "~/components/CheckboxGroup";
import { PrivacyTypeRadio } from "~/components/PrivacyTypeRadio";
import { SourceCategoryDescription } from "~/constants";
import { useCharacterEngine } from "~/hooks/useCharacterEngine";
import { useDispatch } from "~/hooks/useDispatch";
import { useModalManager } from "~/subApps/builder/contexts/ModalManager";
import { appEnvActions } from "~/tools/js/Shared/actions";
import SimpleClassSpellList from "~/tools/js/Shared/components/SimpleClassSpellList";
import { appEnvSelectors } from "~/tools/js/Shared/selectors";
import config from "~/tools/js/config";

import { SelectField } from "../../components/SelectField";
import pageStyles from "../../styles/page.module.css";
import { PremadeForm } from "./PremadeForm";
import styles from "./styles.module.css";

export interface HomeProps extends HTMLAttributes<HTMLDivElement> {}

export const Home: FC<HomeProps> = ({ className, ...props }) => {
  const dispatch = useDispatch();
  const {
    preferences,
    ruleData,
    activeSourceCategories,
    classes,
    race: species,
    classSpellListSpellsLookup,
  } = useCharacterEngine();
  const diceEnabled = useSelector(appEnvSelectors.getDiceEnabled);
  const { createModal } = useModalManager();
  const [searchParams] = useSearchParams();
  const isVttView = searchParams.get("view") === "vtt";

  const {
    useHomebrewContent,
    encumbranceType,
    hitPointType,
    progressionType,
    abilityScoreDisplayType,
    privacyType,
    ignoreCoinWeight,
    enforceFeatRules,
    enforceMulticlassRules,
    showScaledSpells,
    enableOptionalOrigins,
    enableOptionalClassFeatures,
  } = preferences;

  const handleSourceCategoryChange = (
    sourceId: number,
    isActive: boolean
  ): void => {
    let newSourceCats: Array<any> = [];
    if (isActive) {
      newSourceCats = [...activeSourceCategories, sourceId];
    } else {
      newSourceCats = activeSourceCategories.filter((id) => id !== sourceId);
    }

    dispatch(characterActions.activeSourceCategoriesSet(newSourceCats));
  };

  const handlePartneredSourceChangeAll = (
    sourceIds: number[],
    isActive: boolean
  ): void => {
    let newSourceCats: Array<any> = [];
    if (isActive) {
      newSourceCats = [...activeSourceCategories, ...sourceIds];
    } else {
      newSourceCats = activeSourceCategories.filter(
        (id) => !sourceIds.includes(id)
      );
    }

    dispatch(characterActions.activeSourceCategoriesSet(newSourceCats));
  };

  const handlePreferenceChange = (prefKey: string, value: boolean): void => {
    const typedPrefKey = CharacterUtils.getPreferenceKey(prefKey);
    if (typedPrefKey !== null) {
      dispatch(characterActions.preferenceChoose(typedPrefKey, value));
    }
  };

  const handleIntPreferenceChange = (prefKey: string, value: string): void => {
    const typedPrefKey = CharacterUtils.getPreferenceKey(prefKey);
    if (typedPrefKey !== null) {
      dispatch(
        characterActions.preferenceChoose(
          typedPrefKey,
          HelperUtils.parseInputInt(value)
        )
      );
    }
  };

  const handleDiceToggle = (): void => {
    const newDiceEnabledSetting: boolean = !diceEnabled;

    try {
      localStorage.setItem("dice-enabled", newDiceEnabledSetting.toString());
    } catch (e) {}

    dispatch(
      appEnvActions.dataSet({
        diceEnabled: newDiceEnabledSetting,
      })
    );
  };

  const handleOptionalClassFeaturesPreferenceChange = (
    newIsEnabled: boolean,
    accept: () => void,
    reject: () => void
  ): void => {
    const spellListIds: Array<number> =
      ClassUtils.getUpdateEnableOptionalClassFeaturesSpellListIdsToRemove(
        classes,
        newIsEnabled
      );

    const hasSpellsToRemove = spellListIds.some((id) =>
      classSpellListSpellsLookup.hasOwnProperty(id)
    );

    if (!hasSpellsToRemove) {
      handlePreferenceChange("enableOptionalClassFeatures", newIsEnabled);
      accept();
    } else {
      createModal({
        content: (
          <div>
            <p>
              Are you sure you want to disable{" "}
              <strong>Optional Class Features</strong> for this character?
            </p>
            <p>
              After doing so, the following spells provided by these features
              will be removed from your character:
            </p>
            <SimpleClassSpellList
              spellListIds={spellListIds}
              classSpellListSpellsLookup={classSpellListSpellsLookup}
            />
          </div>
        ),
        props: {
          heading: "Optional Class Features",
          size: "fit-content",
          variant: "remove",
          onConfirm: () => {
            handlePreferenceChange("enableOptionalClassFeatures", newIsEnabled);
            accept();
          },
          onClose: () => {
            reject();
          },
        },
      });
    }
  };

  const handleOptionalOriginsPreferenceChange = (
    newIsEnabled: boolean,
    accept: () => void,
    reject: () => void
  ): void => {
    if (!species) {
      handlePreferenceChange("enableOptionalOrigins", newIsEnabled);
      accept();

      return;
    }

    const spellListIds: Array<number> =
      RaceUtils.getUpdateEnableOptionalOriginsSpellListIdsToRemove(
        species,
        newIsEnabled
      );

    const hasSpellsToRemove = spellListIds.some((id) =>
      classSpellListSpellsLookup.hasOwnProperty(id)
    );

    if (!hasSpellsToRemove) {
      handlePreferenceChange("enableOptionalOrigins", newIsEnabled);
      accept();
    } else {
      createModal({
        content: (
          <div>
            <p>
              Are you sure you want to disable{" "}
              <strong>Customized Origins</strong> for this character?
            </p>
            <p>
              After doing so, the following spells provided by these features
              will be removed from your character:
            </p>
            <SimpleClassSpellList
              spellListIds={spellListIds}
              classSpellListSpellsLookup={classSpellListSpellsLookup}
            />
          </div>
        ),
        props: {
          heading: "Customized Origin Features",
          size: "fit-content",
          variant: "remove",
          onConfirm: () => {
            handlePreferenceChange("enableOptionalOrigins", newIsEnabled);
            accept();
          },
          onClose: () => {
            reject();
          },
        },
      });
    }
  };

  const handleProgressionPreferenceChangePromise = (
    newValue: string,
    oldValue: string,
    accept: () => void,
    reject: () => void
  ): void => {
    const prefKey = CharacterUtils.getPreferenceKey("progressionType");
    const newIdValue = HelperUtils.parseInputInt(newValue);

    let content: ReactNode;
    let heading: string | null = null;
    if (newIdValue === Constants.PreferenceProgressionTypeEnum.XP) {
      heading = "XP Advancement";
      content = (
        <div>
          <p>
            Are you sure you want to change your advancement method to XP
            progression?
          </p>
          <p>You will begin with the base XP value for your current level.</p>
        </div>
      );
    } else if (
      newIdValue === Constants.PreferenceProgressionTypeEnum.MILESTONE
    ) {
      heading = "Milestone Advancement";
      content = (
        <div>
          <p>
            Are you sure you want to change your advancement method to Milestone
            progression?
          </p>
          <p>Your current XP values will be lost.</p>
        </div>
      );
    }

    if (prefKey !== null && heading) {
      createModal({
        content,
        props: {
          heading,
          size: "fit-content",
          onConfirm: () => {
            dispatch(characterActions.preferenceChoose(prefKey, newIdValue));
            accept();
          },
          onClose: () => {
            reject();
          },
        },
      });
    }
  };

  const handleHitPointPreferenceChangePromise = (
    newValue: string,
    oldValue: string,
    accept: () => void,
    reject: () => void
  ): void => {
    const prefKey = CharacterUtils.getPreferenceKey("hitPointType");
    const newIdValue = HelperUtils.parseInputInt(newValue);

    let content: ReactNode;
    let heading: string | null = null;
    if (newIdValue === Constants.PreferenceHitPointTypeEnum.FIXED) {
      heading = "Fixed Hit Points";
      content = (
        <div>
          <p>
            Are you sure you want to change your hit points to the fixed value?
          </p>
          <p>Any rolled hit point totals will be lost.</p>
        </div>
      );
    } else if (newIdValue === Constants.PreferenceHitPointTypeEnum.MANUAL) {
      heading = "Manual Hit Points";
      content = (
        <div>
          <p>Are you sure you want to change your hit points manual entry?</p>
          <p>
            After doing so, use Manage HP in the Class section to enter your
            rolled values.
          </p>
        </div>
      );
    }

    if (prefKey !== null && heading) {
      createModal({
        content,
        props: {
          heading,
          size: "fit-content",
          onConfirm: () => {
            dispatch(characterActions.preferenceChoose(prefKey, newIdValue));
            accept();
          },
          onClose: () => {
            reject();
          },
        },
      });
    }
  };

  const renderSourceToggles = () => {
    let sourceToggles: Array<CheckboxInfo> = [];
    let partneredSourceCheckboxes: Array<CheckboxInfo> = [];
    let allPartneredSources: Array<number> = [];

    RuleDataUtils.getSourceCategories(ruleData).forEach((sourceCategory) => {
      if (!sourceCategory.isToggleable) {
        return null;
      }

      const checkbox: CheckboxInfo = {
        label: `${sourceCategory.name}`,
        initiallyEnabled: activeSourceCategories.includes(sourceCategory.id),
        onChange: (e) =>
          handleSourceCategoryChange(sourceCategory.id, e.target.checked),
        sortOrder: sourceCategory.sortOrder,
        description: sourceCategory.description ?? "",
      };

      if (sourceCategory.isPartneredContent) {
        delete checkbox.description; // remove description from partnered content
        partneredSourceCheckboxes.push(checkbox);
        allPartneredSources.push(sourceCategory.id);
      } else {
        sourceToggles.push(checkbox);
      }
    });

    return (
      <>
        <CheckboxGroup
          className={styles.checkboxGroup}
          title="Sources"
          description={SourceCategoryDescription.official}
          name="sources"
          checkboxes={[
            ...sourceToggles,
            {
              label: "Homebrew",
              description: SourceCategoryDescription.homebrew,
              initiallyEnabled: useHomebrewContent,
              onChange: (e) =>
                handlePreferenceChange("useHomebrewContent", e.target.checked),
              sortOrder: 0,
            },
          ]}
          showAccordion={false}
        />
        <CheckboxGroup
          className={styles.checkboxGroup}
          title="Partnered Content"
          description={SourceCategoryDescription.partnered}
          name="partneredContent"
          checkboxes={partneredSourceCheckboxes}
          accordionSummary="Choose Partners"
          showAccordion={true}
          toggleAllLabel="Enable All Partnered Content"
          onToggleAll={(e) =>
            handlePartneredSourceChangeAll(
              allPartneredSources,
              e.target.checked
            )
          }
        />
      </>
    );
  };

  return (
    <div className={clsx([pageStyles.page, styles.home, className])} {...props}>
      <PremadeForm />
      <h2 className={pageStyles.title}>Character Preferences</h2>
      <div className="home-manage-preferences">
        {renderSourceToggles()}
        {!isVttView && (
          <CheckboxGroup
            className={styles.checkboxGroup}
            title="Dice Rolling"
            description="Enables digital dice rolling for all characters on this browser"
            name="dice-rolling"
            checkboxes={[
              {
                label: "Enable Dice Rolling",
                initiallyEnabled: diceEnabled,
                onChange: handleDiceToggle,
              },
            ]}
          />
        )}
        <CheckboxGroup
          className={styles.checkboxGroup}
          title="Optional Features"
          name="optional-features"
          description="Allow or restrict optional features for this character"
          checkboxes={[
            {
              label: "Optional Class Features",
              initiallyEnabled: enableOptionalClassFeatures,
              onChangeConfirm: handleOptionalClassFeaturesPreferenceChange,
            },
            {
              label: "Customize Your Origin",
              initiallyEnabled: enableOptionalOrigins,
              onChangeConfirm: handleOptionalOriginsPreferenceChange,
            },
          ]}
        />
        <SelectField
          label="Advancement Type"
          description="Story-based character progression / XP-based character progression"
          onChangeConfirm={handleProgressionPreferenceChangePromise}
          hidePlaceholderOption={true}
          options={[
            {
              label: "Milestone",
              value: Constants.PreferenceProgressionTypeEnum.MILESTONE,
            },
            { label: "XP", value: Constants.PreferenceProgressionTypeEnum.XP },
          ]}
          value={"" + progressionType}
          name={"advancementType"}
        />
        <SelectField
          label="Hit Point Type"
          description="When leveling up, increase hit points by the fixed value for your chosen class or manually enter a rolled value"
          onChangeConfirm={handleHitPointPreferenceChangePromise}
          hidePlaceholderOption={true}
          options={[
            {
              label: "Fixed",
              value: Constants.PreferenceHitPointTypeEnum.FIXED,
            },
            {
              label: "Manual",
              value: Constants.PreferenceHitPointTypeEnum.MANUAL,
            },
          ]}
          value={"" + hitPointType}
          name={"hitPointType"}
        />
        <CheckboxGroup
          className={styles.checkboxGroup}
          title="Use Prerequisites"
          description="Allow or restrict choices based on rule prerequisites for the following for this character"
          name="use-prerequisites"
          checkboxes={[
            {
              label: "Feats",
              initiallyEnabled: enforceFeatRules,
              onChange: (e) =>
                handlePreferenceChange("enforceFeatRules", e.target.checked),
            },
            {
              label: "Multiclass Requirements",
              initiallyEnabled: enforceMulticlassRules,
              onChange: (e) =>
                handlePreferenceChange(
                  "enforceMulticlassRules",
                  e.target.checked
                ),
            },
          ]}
        />
        <CheckboxGroup
          className={styles.checkboxGroup}
          title="Show Level-Scaled Spells"
          description="Display and highlight available spells to cast with higher level spell slots"
          name="show-scaled-spells"
          checkboxes={[
            {
              label: "Show Level-Scaled Spells",
              initiallyEnabled: showScaledSpells,
              onChange: (e) =>
                handlePreferenceChange("showScaledSpells", e.target.checked),
            },
          ]}
        />

        <SelectField
          label="Encumbrance Type"
          description="Use the standard encumbrance rules / Disable the encumbrance display / Use the more detailed rules for encumbrance"
          onChange={(value: string) =>
            handleIntPreferenceChange("encumbranceType", value)
          }
          hidePlaceholderOption={true}
          options={[
            {
              label: "Use Encumbrance",
              value: Constants.PreferenceEncumbranceTypeEnum.ENCUMBRANCE,
            },
            {
              label: "No Encumbrance",
              value: Constants.PreferenceEncumbranceTypeEnum.NONE,
            },
            {
              label: "Variant Encumbrance",
              value: Constants.PreferenceEncumbranceTypeEnum.VARIANT,
            },
          ]}
          value={"" + encumbranceType}
          name={"encumbranceType"}
        />
        <CheckboxGroup
          className={styles.checkboxGroup}
          title="Ignore Coin Weight"
          description="Coins do not count against your total weight carried (50 coins weigh 1 lb.)"
          name="ignore-coin-weight"
          checkboxes={[
            {
              label: "Ignore Coin Weight",
              initiallyEnabled: ignoreCoinWeight,
              onChange: (e) =>
                handlePreferenceChange("ignoreCoinWeight", e.target.checked),
            },
          ]}
        />
        <SelectField
          label="Ability Score/Modifier Display"
          description="Reverse the arrangement of ability modifiers and scores"
          onChange={(value: string) =>
            handleIntPreferenceChange("abilityScoreDisplayType", value)
          }
          hidePlaceholderOption={true}
          options={[
            {
              label: "Modifiers Top",
              value:
                Constants.PreferenceAbilityScoreDisplayTypeEnum.MODIFIERS_TOP,
            },
            {
              label: "Scores Top",
              value: Constants.PreferenceAbilityScoreDisplayTypeEnum.SCORES_TOP,
            },
          ]}
          value={"" + abilityScoreDisplayType}
          name="abilityScoreDisplayType"
        />

        <PrivacyTypeRadio
          className={styles.privacyTypeRadio}
          initialValue={privacyType}
          onChange={(e) =>
            handleIntPreferenceChange("privacyType", e.target.value)
          }
        />
      </div>
      <hr className={pageStyles.divider} />
      <div className={styles.versionInfo}>
        <p>
          Version: <span>{config.version}</span>
        </p>
      </div>
    </div>
  );
};
