import {
  IEquipmentSet,
  IItem,
  IItemContainer,
  IShortcut,
  ItemContainerType,
  ItemSubType,
  ItemType,
} from '@rpg-engine/shared';
import React, { useCallback, useMemo, useRef, useState } from 'react';
import styled from 'styled-components';
import { SlotsContainer } from '../../Abstractions/SlotsContainer';

import { useScrollOnDrag } from '../../../hooks/useScrollOnDrag';
import { IPosition } from '../../../types/eventTypes';
import { ShortcutsSetter } from '../../Shortcuts/ShortcutsSetter';
import { ItemSlotDetailsProvider } from './context/ItemSlotDetailsContext';
import { ItemSlotDraggingProvider } from './context/ItemSlotDraggingContext';
import { DraggedItem } from './DraggedItem';
import { ItemQuantitySelectorModal } from './ItemQuantitySelectorModal';
import { ItemSlot } from './ItemSlot';
import { ItemSlotToolTips } from './ItemSlotTooltips';

export interface IItemContainerProps {
  itemContainer: IItemContainer;
  onClose?: () => void;
  onItemClick?: (
    item: IItem,
    ItemType: IItem['type'],
    itemContainerType: ItemContainerType | null
  ) => void;
  onItemDragStart?: (
    item: IItem,
    slotIndex: number,
    itemContainerType: ItemContainerType | null
  ) => void;
  onItemDragEnd?: (quantity?: number) => void;
  onOutsideDrop?: (item: IItem, position: IPosition) => void;
  onItemPlaceDrop?: (
    item: IItem | null,
    slotIndex: number,
    itemContainerType: ItemContainerType | null
  ) => void;
  scale?: number;
  checkIfItemCanBeMoved: () => boolean;
  checkIfItemShouldDragEnd?: () => boolean;
  onMouseOver?: (e: any, slotIndex: number, item: IItem | null) => void;
  onSelected?: (optionId: string, item: IItem) => void;
  type: ItemContainerType;
  atlasJSON: any;
  atlasIMG: any;
  disableContextMenu?: boolean;
  initialPosition?: { x: number; y: number };
  shortcuts?: IShortcut[];
  setItemShortcut?: (key: string, index: number) => void;
  removeShortcut?: (index: number) => void;
  equipmentSet?: IEquipmentSet | null;
  isDepotSystem?: boolean;
  onPositionChangeEnd?: (position: IPosition) => void;
  onPositionChangeStart?: (position: IPosition) => void;
  isFullScreen?: boolean;
  opacity?: number;
}

const MIN_SLOTS_FOR_SCROLL = 20;

export const ItemContainer: React.FC<IItemContainerProps> = React.memo(
  ({
    itemContainer,
    onClose,
    onMouseOver,
    onSelected,
    onItemClick,
    type,
    atlasJSON,
    atlasIMG,
    disableContextMenu = false,
    onItemDragEnd,
    onItemDragStart,
    onItemPlaceDrop,
    onOutsideDrop,
    checkIfItemCanBeMoved,
    initialPosition,
    checkIfItemShouldDragEnd,
    scale,
    shortcuts,
    setItemShortcut,
    removeShortcut,
    equipmentSet,
    isDepotSystem,
    onPositionChangeEnd,
    onPositionChangeStart,
    isFullScreen,
    opacity,
  }) => {
    const [quantitySelect, setQuantitySelect] = useState({
      isOpen: false,
      maxQuantity: 1,
      callback: (_quantity: number) => {},
    });
    const [settingShortcutIndex, setSettingShortcutIndex] = useState(-1);

    const containerRef = useRef<HTMLDivElement>(null);

    const { startScrolling, stopScrolling } = useScrollOnDrag({
      containerRef,
      threshold: 50,
      scrollStep: 4,
    });

    const handleSetShortcut = useCallback(
      (item: IItem, index: number) => {
        if (
          item.type === ItemType.Consumable ||
          item.type === ItemType.Tool ||
          item.subType === ItemSubType.Seed
        ) {
          setItemShortcut?.(item.key, index);
        }
      },
      [setItemShortcut]
    );

    const handleMouseMove = useCallback(
      (e: MouseEvent) => {
        startScrolling(e.clientY);
      },
      [startScrolling]
    );

    const onDragStartScrollingEvents = useCallback(() => {
      document.addEventListener('pointermove', handleMouseMove);
      document.addEventListener(
        'pointerup',
        () => {
          stopScrolling();
          document.removeEventListener('pointermove', handleMouseMove);
        },
        { once: true }
      );
    }, [handleMouseMove, stopScrolling]);

    const onDragEndScrollingEvents = useCallback(() => {
      stopScrolling();
      document.removeEventListener('pointermove', handleMouseMove);
    }, [handleMouseMove, stopScrolling]);

    const onDragStartHandler = useCallback(
      (
        item: IItem,
        slotIndex: number,
        itemContainerType: ItemContainerType | null
      ) => {
        if (onItemDragStart) {
          onItemDragStart(item, slotIndex, itemContainerType);
        }

        onDragStartScrollingEvents();
      },
      [onItemDragStart, onDragStartScrollingEvents]
    );

    const onDragEndHandler = useCallback(
      (quantity: number | undefined) => {
        if (onItemDragEnd) {
          onItemDragEnd(quantity);
        }
        onDragEndScrollingEvents();
      },
      [onItemDragEnd, onDragEndScrollingEvents]
    );

    const handleItemClick = useCallback(
      (
        itemType: IItem['type'],
        containerType: ItemContainerType | null,
        item: IItem
      ) => {
        if (settingShortcutIndex !== -1) {
          setSettingShortcutIndex(-1);
          handleSetShortcut(item, settingShortcutIndex);
        } else if (onItemClick) {
          onItemClick(item, itemType, containerType);
        }
      },
      [settingShortcutIndex, handleSetShortcut, onItemClick]
    );

    const handlePlaceDrop = useCallback(
      (
        item: IItem | null,
        slotIndex: number,
        itemContainerType: ItemContainerType | null
      ) => {
        if (onItemPlaceDrop) {
          onItemPlaceDrop(item, slotIndex, itemContainerType);
        }
        onDragEndScrollingEvents();
      },
      [onItemPlaceDrop, onDragEndScrollingEvents]
    );

    const handleOutsideDrop = useCallback(
      (item: IItem, position: IPosition) => {
        if (onOutsideDrop) {
          onOutsideDrop(item, position);
        }
        onDragEndScrollingEvents();
      },
      [onOutsideDrop, onDragEndScrollingEvents]
    );

    const handleQuantitySelect = useCallback(
      (maxQuantity: number, callback: (quantity: number) => void) => {
        setQuantitySelect({
          isOpen: true,
          maxQuantity,
          callback,
        });
      },
      []
    );

    // Memoize slot rendering configuration
    const slotConfig = useMemo(
      () => ({
        type,
        atlasIMG,
        atlasJSON,
        scale,
        equipmentSet,
        isDepotSystem,
        isSelectingShortcut: settingShortcutIndex !== -1,
        checkIfItemCanBeMoved,
        checkIfItemShouldDragEnd,
      }),
      [
        type,
        atlasIMG,
        atlasJSON,
        scale,
        equipmentSet,
        isDepotSystem,
        settingShortcutIndex,
        checkIfItemCanBeMoved,
        checkIfItemShouldDragEnd,
      ]
    );

    // Memoize slots rendering
    const renderSlots = useMemo(() => {
      const slots = [];

      for (let i = 0; i < itemContainer.slotQty; i++) {
        const currentItem = itemContainer.slots?.[i] || null;

        slots.push(
          <ItemSlot
            key={i}
            slotIndex={i}
            item={currentItem}
            itemContainerType={slotConfig.type}
            onMouseOver={onMouseOver}
            onPointerDown={handleItemClick}
            onDragStart={onDragStartHandler}
            onDragEnd={onDragEndHandler}
            dragScale={slotConfig.scale}
            checkIfItemCanBeMoved={slotConfig.checkIfItemCanBeMoved}
            checkIfItemShouldDragEnd={slotConfig.checkIfItemShouldDragEnd}
            openQuantitySelector={handleQuantitySelect}
            onPlaceDrop={handlePlaceDrop}
            onOutsideDrop={handleOutsideDrop}
            atlasIMG={slotConfig.atlasIMG}
            atlasJSON={slotConfig.atlasJSON}
            isSelectingShortcut={slotConfig.isSelectingShortcut}
            equipmentSet={slotConfig.equipmentSet}
            setItemShortcut={
              type === ItemContainerType.Inventory
                ? handleSetShortcut
                : undefined
            }
            isDepotSystem={slotConfig.isDepotSystem}
          />
        );
      }
      return slots;
    }, [
      itemContainer.slotQty,
      itemContainer.slots,
      slotConfig,
      onMouseOver,
      handleItemClick,
      onDragStartHandler,
      onDragEndHandler,
      handleQuantitySelect,
      handlePlaceDrop,
      handleOutsideDrop,
      handleSetShortcut,
      type,
    ]);

    return (
      <ItemSlotDraggingProvider>
        <ItemSlotDetailsProvider>
          <DraggedItem
            atlasIMG={atlasIMG}
            atlasJSON={atlasJSON}
            scale={scale}
          />
          <SlotsContainer
            title={itemContainer.name || 'Container'}
            onClose={onClose}
            initialPosition={initialPosition}
            scale={scale}
            onPositionChangeEnd={onPositionChangeEnd}
            onPositionChangeStart={onPositionChangeStart}
            isFullScreen={isFullScreen}
            opacity={opacity}
          >
            {type === ItemContainerType.Inventory &&
              shortcuts &&
              removeShortcut && (
                <ShortcutsSetter
                  setSettingShortcutIndex={setSettingShortcutIndex}
                  settingShortcutIndex={settingShortcutIndex}
                  shortcuts={shortcuts}
                  removeShortcut={removeShortcut}
                  atlasIMG={atlasIMG}
                  atlasJSON={atlasJSON}
                />
              )}
            <ItemsContainer
              className="item-container-body"
              ref={containerRef}
              isScrollable={itemContainer.slotQty > MIN_SLOTS_FOR_SCROLL}
              isFullScreen={isFullScreen}
            >
              {renderSlots}
            </ItemsContainer>
          </SlotsContainer>
          {quantitySelect.isOpen && (
            <ItemQuantitySelectorModal
              quantitySelect={quantitySelect}
              setQuantitySelect={setQuantitySelect}
            />
          )}
          <ItemSlotToolTips
            dragScale={scale}
            atlasIMG={atlasIMG}
            atlasJSON={atlasJSON}
            equipmentSet={equipmentSet}
            isContextMenuDisabled={disableContextMenu}
            onSelected={onSelected}
          />
        </ItemSlotDetailsProvider>
      </ItemSlotDraggingProvider>
    );
  }
);

interface IItemsContainerProps {
  isScrollable: boolean;
  isFullScreen?: boolean;
}

const ItemsContainer = styled.div<IItemsContainerProps>`
  display: flex;
  justify-content: ${({ isFullScreen }) =>
    isFullScreen ? 'flex-start' : 'center'};
  flex-wrap: wrap;
  max-height: ${({ isFullScreen }) =>
    isFullScreen
      ? undefined
      : '270px'}; // Adjust the max-height based on the prop
  overflow-y: ${({ isScrollable }) => (isScrollable ? 'auto' : 'hidden')};
  overflow-x: hidden;
  width: ${({ isFullScreen }) =>
    isFullScreen ? '100vw' : '415px'}; // Adjust the width based on the prop

  max-height: ${({ isFullScreen }) => isFullScreen && '60%'};

  @media screen and (max-height: 350px) {
    max-height: ${({ isFullScreen }) => isFullScreen && '50%'};
  }
`;
