import axios, { Canceler } from "axios";
import React from "react";

import { LoadingPlaceholder, Button } from "@dndbeyond/character-components/es";
import {
  HelperUtils,
  Item,
  ItemUtils,
  Modifier,
  RuleData,
  RuleDataUtils,
  TypeValueLookup,
  CharacterTheme,
  InventoryManager,
  ContainerManager,
  ItemManager,
} from "@dndbeyond/character-rules-engine/es";

import { Accordion } from "~/components/Accordion";
import { AccordionSummary } from "~/components/AccordionSummary";
import { ItemFilter } from "~/components/ItemFilter";
import { ItemName } from "~/components/ItemName";

import { AppLoggerUtils } from "../../../utils";
import ItemDetail from "../../ItemDetail";
import styles from "./styles.module.css";

//TODO: This component should be removed and replaced with the EquipmentShop component
export class EquipmentManagerShopItem extends React.PureComponent<
  {
    item: Item;
    onItemAdd: (
      item: Item,
      amount: number,
      containerDefinitionKey: string
    ) => void;
    minAddAmount: number;
    maxAddAmount: number;
    ruleData: RuleData;
    containerDefinitionKey: string;
    theme: CharacterTheme;
  },
  {
    amount: number | null;
  }
> {
  static defaultProps = {
    enableAdd: true,
    minAddAmount: 1,
    maxAddAmount: 10,
  };

  constructor(props) {
    super(props);

    this.state = {
      amount: 1,
    };
  }

  handleAmountChange = (evt) => {
    this.setState({
      amount: evt.target.value,
    });
  };

  handleAmountBlur = (evt) => {
    const { minAddAmount, maxAddAmount } = this.props;

    const parsedValue = HelperUtils.parseInputInt(evt.target.value);
    let clampedValue: number | null = null;
    if (parsedValue) {
      clampedValue = HelperUtils.clampInt(
        parsedValue,
        minAddAmount,
        maxAddAmount
      );
    }

    this.setState({
      amount: clampedValue,
    });
  };

  handleDecrementCountClick = () => {
    const { minAddAmount } = this.props;

    this.setState((prevState, props) => ({
      amount: Math.max(
        (prevState.amount === null ? 0 : prevState.amount) - 1,
        minAddAmount
      ),
    }));
  };

  handleIncrementCountClick = () => {
    this.setState((prevState, props) => ({
      amount: (prevState.amount === null ? 0 : prevState.amount) + 1,
    }));
  };

  handleAdd = () => {
    const { amount } = this.state;
    const { item, onItemAdd, minAddAmount, containerDefinitionKey } =
      this.props;

    onItemAdd(
      item,
      amount === null ? minAddAmount : amount,
      containerDefinitionKey
    );
  };

  render() {
    const { amount } = this.state;
    const { item, minAddAmount, maxAddAmount, ruleData, theme } = this.props;
    const avatarUrl = ItemUtils.getDefinitionAvatarUrl(item);

    let metaItems = [ItemUtils.getType(item)];
    let defaultImageUrl: string | null = null;
    // Set default avatar for weapon
    if (ItemUtils.isWeaponContract(item)) {
      defaultImageUrl = RuleDataUtils.getDefaultWeaponImageUrl(ruleData);
    }
    // Set default avatar for gear
    else if (ItemUtils.isGearContract(item)) {
      const subType = ItemUtils.getSubType(item);
      if (subType) {
        metaItems.push(subType);
      }

      defaultImageUrl = RuleDataUtils.getDefaultGearImageUrl(ruleData);
    }
    // Set default avatar for armor
    else if (ItemUtils.isArmorContract(item)) {
      const baseArmorName = ItemUtils.getBaseArmorName(item);
      if (baseArmorName) {
        metaItems.push(baseArmorName);
      }

      defaultImageUrl = RuleDataUtils.getDefaultArmorImageUrl(ruleData);
    }

    const totalAmount: number =
      (amount === null ? 1 : amount) * ItemUtils.getBundleSize(item);

    return (
      <Accordion
        className={styles.accordion}
        variant="paper"
        summary={
          <AccordionSummary
            className={styles.accordionSummary}
            title={<ItemName item={item} showLegacyBadge={true} />}
            description={metaItems as string[]}
            imageUrl={avatarUrl ?? defaultImageUrl ?? undefined}
            callout={
              <Button size="small" onClick={this.handleAdd}>
                Add
              </Button>
            }
          />
        }
      >
        <ItemDetail
          theme={theme}
          item={item}
          ruleData={ruleData}
          showCustomize={false}
          showActions={false}
        />
        <div className="equipment-list-item-actions">
          <div className="equipment-list-item-action">
            <div className="equipment-list-item-amount">
              <div className="equipment-list-item-amount-label">
                Amount to add
              </div>
              <div className="equipment-list-item-amount-controls">
                <div className="equipment-list-item-amount-decrease">
                  <Button
                    clsNames={["button-action-decrease"]}
                    onClick={this.handleDecrementCountClick}
                    disabled={amount !== null && amount <= minAddAmount}
                  />
                </div>
                <div className="equipment-list-item-amount-value">
                  <input
                    type="number"
                    value={amount === null ? "" : amount}
                    className="character-input equipment-list-item-amount-input"
                    onChange={this.handleAmountChange}
                    onBlur={this.handleAmountBlur}
                    min={minAddAmount}
                    max={maxAddAmount}
                  />
                </div>
                <div className="equipment-list-item-amount-increase">
                  <Button
                    clsNames={["button-action-increase"]}
                    onClick={this.handleIncrementCountClick}
                    disabled={amount !== null && amount >= maxAddAmount}
                  />
                </div>
              </div>
            </div>
          </div>
          <div className="equipment-list-item-action">
            <Button onClick={this.handleAdd}>
              Add {totalAmount === 1 ? " Item" : ` ${totalAmount} Items`}
            </Button>
          </div>
        </div>
      </Accordion>
    );
  }
}

interface EquipmentManagerShopProps {
  globalModifiers: Array<Modifier>;
  valueLookupByType: TypeValueLookup;
  pageSize: number;
  onItemAdd: (
    item: Item,
    amount: number,
    containerDefinitionKey: string
  ) => void;
  ruleData: RuleData;
  containerDefinitionKey: string;
  theme: CharacterTheme;
  activeSourceCategories: Array<number>;
  inventoryManager: InventoryManager;
}
interface EquipmentManagerShopState {
  query: string;
  filteredItems: Array<Item>;
  currentPage: number;
  loading: boolean;
  loaded: boolean;
  filterTypes: Array<string>;
  filterQuery: string;
  filterProficient: boolean;
  filterBasic: boolean;
  filterMagic: boolean;
  filterContainer: boolean;
  filterSourceCategories: Array<number>;
  shoppeContainer: ContainerManager | null;
}
export default class EquipmentManagerShop extends React.PureComponent<
  EquipmentManagerShopProps,
  EquipmentManagerShopState
> {
  static defaultProps = {
    pageSize: 30,
  };

  loadItemsCanceler: null | Canceler = null;

  constructor(props) {
    super(props);

    const { items } = props;

    this.state = {
      query: "",
      filteredItems: items,
      currentPage: 0,
      loading: false,
      loaded: false,
      filterTypes: [],
      filterQuery: "",
      filterProficient: false,
      filterBasic: false,
      filterMagic: false,
      filterContainer: false,
      filterSourceCategories: [],
      shoppeContainer: null,
    };
  }

  componentDidMount() {
    const { inventoryManager } = this.props;

    if (inventoryManager) {
      this.setState({
        loading: true,
      });

      inventoryManager
        .getInventoryShoppe({
          onSuccess: (shoppeContainer: ContainerManager) => {
            this.setState({
              shoppeContainer,
              loading: false,
              loaded: true,
            });
          },
          additionalApiConfig: {
            cancelToken: new axios.CancelToken((c) => {
              this.loadItemsCanceler = c;
            }),
          },
        })
        .then((res) => {
          this.loadItemsCanceler = null;
          return res;
        })
        .catch(AppLoggerUtils.handleAdhocApiError);
    } else {
      this.setState({
        loaded: true,
      });
    }
  }

  componentWillUnmount(): void {
    if (this.loadItemsCanceler !== null) {
      this.loadItemsCanceler();
    }
  }

  getLastPageIdx = (filteredItems) => {
    const { pageSize } = this.props;

    return Math.ceil(filteredItems.length / pageSize) - 1;
  };

  handlePageMore = () => {
    this.setState((prevState) => ({
      currentPage: prevState.currentPage + 1,
    }));
  };

  handleQueryChange = (evt) => {
    const query = evt.target.value;

    this.setState({
      filterQuery: query,
      currentPage: 0,
    });
  };

  handleFilterItemType = (type) => {
    this.setState((prevState) => ({
      filterTypes: prevState.filterTypes.includes(type)
        ? prevState.filterTypes.filter((t) => t !== type)
        : [...prevState.filterTypes, type],
      currentPage: 0,
    }));
  };

  handleSourceCategoryClick = (categoryId: number) => {
    this.setState((prevState) => ({
      filterSourceCategories: prevState.filterSourceCategories.includes(
        categoryId
      )
        ? prevState.filterSourceCategories.filter((cat) => cat !== categoryId)
        : [...prevState.filterSourceCategories, categoryId],
    }));
  };

  handleToggleFilter = (type: string): void => {
    switch (type) {
      case "Proficient":
        this.handleProficientToggle();
        break;
      case "Common":
        this.handleBasicToggle();
        break;
      case "Magical":
        this.handleMagicToggle();
        break;
      case "Container":
        this.handleContainerToggle();
        break;
      default:
    }
  };

  handleProficientToggle = () => {
    this.setState((prevState) => ({
      filterProficient: !prevState.filterProficient,
      currentPage: 0,
    }));
  };

  handleBasicToggle = () => {
    this.setState((prevState) => ({
      filterBasic: !prevState.filterBasic,
      currentPage: 0,
    }));
  };

  handleMagicToggle = () => {
    this.setState((prevState) => ({
      filterMagic: !prevState.filterMagic,
      currentPage: 0,
    }));
  };

  handleContainerToggle = (): void => {
    this.setState((prevState) => ({
      filterContainer: !prevState.filterContainer,
      currentPage: 0,
    }));
  };

  renderPager = (
    filteredItems: Array<ItemManager>,
    totalItems: number
  ): React.ReactNode => {
    if (filteredItems.length >= totalItems) {
      return null;
    }

    return (
      <div className="equipment-manager-shop-pager">
        <Button block={true} onClick={this.handlePageMore}>
          Load More
        </Button>
      </div>
    );
  };

  renderPagedListing = (filteredItems: Array<ItemManager>): React.ReactNode => {
    const { currentPage } = this.state;
    const { pageSize, onItemAdd, ruleData, containerDefinitionKey, theme } =
      this.props;

    const pagedFilteredItems: Array<ItemManager> = filteredItems.slice(
      0,
      (currentPage + 1) * pageSize
    );

    return (
      <div className="equipment-manager-shop-items">
        {pagedFilteredItems.length ? (
          pagedFilteredItems.map((item, idx) => (
            <EquipmentManagerShopItem
              theme={theme}
              item={item.getItem()}
              key={`${item.getMappingId()}-${idx}`}
              onItemAdd={onItemAdd}
              ruleData={ruleData}
              containerDefinitionKey={containerDefinitionKey}
            />
          ))
        ) : (
          <div className="equipment-manager-shop-no-results">
            No Results Found
          </div>
        )}
      </div>
    );
  };

  renderUi = () => {
    const {
      filterTypes,
      filterQuery,
      filterProficient,
      filterBasic,
      filterMagic,
      filterContainer,
      filterSourceCategories,
      currentPage,
    } = this.state;
    const { pageSize } = this.props;

    const itemData = this.state.shoppeContainer?.getInventoryItems({
      filterOptions: {
        filterTypes,
        filterQuery,
        filterProficient,
        filterBasic,
        filterMagic,
        filterContainer,
        filterSourceCategories,
      },
      paginationOptions: {
        currentPage,
        pageSize,
      },
      isShoppe: true,
    });

    const items = itemData?.items ?? [];
    const totalItems = itemData?.totalItems ?? 0;
    const sourceCategories = itemData?.sourceCategories || [];

    return (
      <div>
        <ItemFilter
          sourceCategories={sourceCategories}
          filterQuery={filterQuery}
          onQueryChange={(value) =>
            this.setState({
              filterQuery: value,
            })
          }
          filterTypes={filterTypes}
          onFilterButtonClick={this.handleFilterItemType}
          onCheckboxChange={this.handleToggleFilter}
          onSourceCategoryClick={this.handleSourceCategoryClick}
          filterSourceCategories={filterSourceCategories}
          filterProficient={filterProficient}
          filterBasic={filterBasic}
          filterMagic={filterMagic}
          filterContainer={filterContainer}
          buttonSize="x-small"
          filterStyle="builder"
        />
        {this.renderPagedListing(items)}
        {this.renderPager(items, totalItems)}
      </div>
    );
  };

  renderLoading = () => {
    return <LoadingPlaceholder />;
  };

  render() {
    const { loaded, loading } = this.state;

    return (
      <div className="equipment-manager-shop">
        {loading && this.renderLoading()}
        {!loading && loaded && this.renderUi()}
      </div>
    );
  }
}
