import React from "react";
import { useContext } from "react";
import { connect } from "react-redux";

import {
  Collapsible,
  CollapsibleHeaderContent,
  CollapsibleHeading,
} from "@dndbeyond/character-components/es";
import {
  ApiAdapterPromise,
  ApiAdapterRequestConfig,
  ApiResponse,
  rulesEngineSelectors,
  BaseItemDefinitionContract,
  RuleData,
  CharacterTheme,
  Container,
  ContainerUtils,
  InventoryManager,
  serviceDataSelectors,
  PartyInfo,
  ItemManager,
  ContainerManager,
} from "@dndbeyond/character-rules-engine/es";

import { Button } from "~/components/Button";
import { ItemName } from "~/components/ItemName";
import { Link } from "~/components/Link";
import { NumberDisplay } from "~/components/NumberDisplay";
import { Popover } from "~/components/Popover";
import { PopoverContent } from "~/components/PopoverContent";
import { useSidebar } from "~/contexts/Sidebar";
import { PaneInfo } from "~/contexts/Sidebar/Sidebar";
import { ButtonWithMenu } from "~/subApps/sheet/components/Sidebar/components/ButtonWithMenu";
import { Header } from "~/subApps/sheet/components/Sidebar/components/Header";
import { Heading } from "~/subApps/sheet/components/Sidebar/components/Heading";
import { PaneComponentEnum } from "~/subApps/sheet/components/Sidebar/types";

import { CustomItemCreator } from "../../../components/CustomItemCreator";
import EquipmentShop from "../../../components/EquipmentShop";
import ItemDetail from "../../../components/ItemDetail";
import { ItemSlotManager } from "../../../components/ItemSlotManager";
import { InventoryManagerContext } from "../../../managers/InventoryManagerContext";
import { SharedAppState } from "../../../stores/typings";
import styles from "./styles.module.css";

interface Props {
  loadAvailableItems: (
    additionalConfig?: Partial<ApiAdapterRequestConfig>
  ) => ApiAdapterPromise<ApiResponse<Array<BaseItemDefinitionContract>>>;
  ruleData: RuleData;
  containers: Array<Container>;
  theme: CharacterTheme;
  partyInfo: PartyInfo | null;
  inventoryManager: InventoryManager;
  paneHistoryPush: PaneInfo["paneHistoryPush"];
}

class EquipmentManagePane extends React.PureComponent<Props> {
  renderInventory = (inventory: Array<ItemManager>): React.ReactNode => {
    const { theme, ruleData, inventoryManager, partyInfo, containers } =
      this.props;

    return inventory.map((item, i) => {
      const isContainer = item.isContainer();
      const container = inventoryManager.getContainer(
        item.generateContainerDefinitionKey()
      );

      // to ensure key is unique even if two items are the same, append index
      // the index helps in re-renders when items are added/removed/moved around
      const itemKey = item.getUniqueKey() + i;
      const canMove = inventoryManager.canMoveItem(item.item);
      const canDelete = inventoryManager.canRemoveItem(item.item);

      //if the item is a container - render the delete button withPopover only if it "canDelete"
      //if the item is not a container - render the move button if it "canMove" otherwise render the delete button if it "canDelete"
      //if neither - render null;
      let calloutButton: React.ReactNode = null;
      if (isContainer && canDelete) {
        calloutButton = (
          <Popover
            trigger={
              <Button size="xx-small" variant="outline" themed>
                Delete
              </Button>
            }
            position="bottomRight"
            data-testid="remove-container-button"
            maxWidth={250}
          >
            <PopoverContent
              title={`Remove ${item.getName()}?`}
              content={`Removing the ${item.getName()} will also remove all of its ${
                container && container.hasInfusions() ? "infusions and " : " "
              } contents.`}
              confirmText="Delete"
              onConfirm={() => item.handleRemove()}
              withCancel
            />
          </Popover>
        );
      } else if (!isContainer && canMove) {
        calloutButton = (
          <ButtonWithMenu
            showSingleOption={true}
            placement="bottom-end"
            variant="outline"
            groupedOptions={ContainerUtils.getGroupedOptions(
              item.getContainerDefinitionKey(),
              containers,
              "Move To:"
            )}
            onSelect={(containerDefinitionKey) =>
              item.handleMove({ containerDefinitionKey })
            }
          >
            Move
          </ButtonWithMenu>
        );
      } else if (!isContainer && canDelete) {
        calloutButton = (
          <Button
            onClick={() => item.handleRemove()}
            size="xx-small"
            variant="outline"
            themed
          >
            Delete
          </Button>
        );
      }

      return (
        <Collapsible
          key={itemKey}
          className={`ct-equipment-manage-pane__item${
            item.isContainer()
              ? " ct-equipment-manage-pane__item--is-container"
              : ""
          }`}
          layoutType={"minimal"}
          header={
            <CollapsibleHeaderContent
              heading={
                <div className="ct-equipment-manage-pane__item-header">
                  <div className="ct-equipment-manage-pane__item-header-action">
                    <ItemSlotManager
                      isUsed={!!item.isEquipped()}
                      theme={theme}
                      canUse={inventoryManager.canEquipUnequipItem(item.item)}
                      onSet={(uses) => {
                        //TODO need different component than SlotManager for item equipped/unequipped
                        if (uses === 0) {
                          item.handleUnequip();
                        }

                        if (uses === 1) {
                          item.handleEquip();
                        }
                      }}
                      useTooltip={false}
                    />
                  </div>
                  <div
                    className={`ct-equipment-manage-pane__item-header-name${
                      isContainer
                        ? " ct-equipment-manage-pane__item-header-name--is-container"
                        : ""
                    }`}
                  >
                    {isContainer ? (
                      "Container details"
                    ) : (
                      <ItemName item={item.item} showLegacy={true} />
                    )}
                  </div>
                </div>
              }
              callout={calloutButton}
            />
          }
        >
          <ItemDetail
            theme={theme}
            item={item.item}
            ruleData={ruleData}
            showCustomize={false}
            showImage={false}
            partyInfo={partyInfo}
          />
        </Collapsible>
      );
    });
  };

  renderContainers = (containers: ContainerManager[]): React.ReactNode => {
    return containers.map((container) => {
      const isEquipmentContainer =
        container.isCharacterContainer() || container.isPartyContainer();

      const containerInventory = container.getInventoryItems({
        includeContainer: true,
      }).items;

      const inventoryLength =
        containerInventory.length - (isEquipmentContainer ? 0 : 1); //don't count the container item itself if its an item and not the character/party container
      let isEmpty: boolean = inventoryLength === 0;

      let headerNode: React.ReactNode = (
        <CollapsibleHeaderContent
          heading={
            <CollapsibleHeading>{`${container.getName()} (${inventoryLength})`}</CollapsibleHeading>
          }
          callout={
            <div className="ct-equipment-manage-pane__callout">
              <NumberDisplay
                type="weightInLb"
                number={container.getWeightInfo().total}
              />
            </div>
          }
        />
      );

      return (
        <Collapsible header={headerNode} key={container.getDefinitionKey()}>
          <div className="ct-equipment-manage-pane__inventory">
            {this.renderInventory(containerInventory)}

            {isEmpty && (
              <div className="ct-equipment-manage-pane__empty-container">
                There are no items in your {container.getName()}
              </div>
            )}
          </div>
        </Collapsible>
      );
    });
  };

  render() {
    const {
      ruleData,
      containers,
      theme,
      partyInfo,
      paneHistoryPush,
      inventoryManager,
    } = this.props;

    return (
      <div className="ct-equipment-manage-pane">
        <Header>Manage Inventory</Header>
        <Collapsible header="Add Items" initiallyCollapsed={false}>
          <EquipmentShop
            partyInfo={partyInfo}
            containers={containers}
            theme={theme}
            ruleData={ruleData}
          />
          <CustomItemCreator containers={containers} />
        </Collapsible>

        {/*container collapsibles*/}

        <Heading className={styles.containerHeading}>My Inventory</Heading>
        {this.renderContainers(inventoryManager.getCharacterContainers())}
        {inventoryManager.getPartyContainers().length > 0 && (
          <Heading className={styles.containerHeading}>Party Inventory</Heading>
        )}
        {this.renderContainers(inventoryManager.getPartyContainers())}

        <div className="ct-equipment-manage-pane__links">
          <div className="ct-equipment-manage-pane__link">
            <Link
              useTheme={true}
              onClick={() =>
                paneHistoryPush(PaneComponentEnum.STARTING_EQUIPMENT)
              }
            >
              Starting Equipment
            </Link>
          </div>
          <div className="ct-equipment-manage-pane__link">
            <Link
              useTheme={true}
              onClick={() => paneHistoryPush(PaneComponentEnum.CURRENCY)}
            >
              Currency
            </Link>
          </div>
          <div className="ct-equipment-manage-pane__link">
            <Link
              useTheme={true}
              onClick={() => paneHistoryPush(PaneComponentEnum.ENCUMBRANCE)}
            >
              Encumbrance
            </Link>
          </div>
        </div>
      </div>
    );
  }
}

function mapStateToProps(state: SharedAppState) {
  return {
    ruleData: rulesEngineSelectors.getRuleData(state),
    containers: rulesEngineSelectors.getInventoryContainers(state),
    theme: rulesEngineSelectors.getCharacterTheme(state),
    partyInfo: serviceDataSelectors.getPartyInfo(state),
  };
}

function EquipmentManagePaneContainer(props) {
  const { inventoryManager } = useContext(InventoryManagerContext);
  const {
    pane: { paneHistoryPush },
  } = useSidebar();
  return (
    <EquipmentManagePane
      inventoryManager={inventoryManager}
      paneHistoryPush={paneHistoryPush}
      {...props}
    />
  );
}

export default connect(mapStateToProps)(EquipmentManagePaneContainer);
