import React from 'react';
import { useKeyPress } from 'react-use';
import { ComponentTypeEnum, isMac } from '@easytwin/core';

import TreeNode, { TreeNodeDataSource } from './TreeNode';

export interface DraggableTreeRenderNodeState {
  isSelected: boolean;
  isExpanded: boolean;
}

export interface DraggableTreeDropEventInfo {
  source: TreeNodeDataSource;
  sourceParent?: TreeNodeDataSource;
  target: TreeNodeDataSource;
  targetParent?: TreeNodeDataSource;
  targetNext?: TreeNodeDataSource;
}

export interface DraggableTreeProp {
  dataSource: TreeNodeDataSource[];
  editingKey?: string;
  keyPropName?: string;
  pidPropName?: string;
  titlePropName?: string;
  isGroupPropName?: string;
  expandPropName?: string;
  selectedKeys?: string[];
  dragType: string;
  allowVisible?: (item: TreeNodeDataSource) => boolean;
  iconRender?: (item: any, state: DraggableTreeRenderNodeState) => React.ReactNode;
  extendRender?: (item: any, state: DraggableTreeRenderNodeState) => React.ReactNode;
  onSelect?: (keys: string[], infos: any[]) => void;
  onDragStart?: (e: React.DragEvent<HTMLElement>, item: TreeNodeDataSource) => void;
  onDragEnd?: (e: React.DragEvent<HTMLElement>, item: TreeNodeDataSource) => void;
  onDrop?: (info: DraggableTreeDropEventInfo) => void;
  onRightClick?: (e: React.MouseEvent<HTMLElement>, item: TreeNodeDataSource) => void;
  onExpandChange?: (e: React.MouseEvent<HTMLElement>, item: TreeNodeDataSource) => void;
  onNameEditChange?: (id: string, name: string) => void;
}

const DraggableTree: React.FC<DraggableTreeProp> = function ({
  dataSource,
  editingKey,
  keyPropName = 'id',
  pidPropName = 'pid',
  titlePropName = 'name',
  isGroupPropName = 'isGroup',
  expandPropName = 'collapsed',
  selectedKeys,
  dragType,
  allowVisible,
  iconRender,
  extendRender,
  onSelect,
  onDragStart,
  onDragEnd,
  onDrop,
  onRightClick,
  onExpandChange,
  onNameEditChange,
}) {
  const [isCtrlPressed] = useKeyPress((event) => event.key === (isMac ? 'Meta' : 'Control'));
  const [isShiftPressed] = useKeyPress('Shift');

  const [innerSelectedKeys, setInnerSelectedKeys] = React.useState<string[]>([]);
  React.useEffect(() => {
    setInnerSelectedKeys(selectedKeys || []);
  }, [selectedKeys]);

  // TODO: 需要添加 “如果 组/母版 被选中了，那么 子要素 就无法被选中了” 逻辑
  const handleItemClick = React.useCallback(
    (key: string) => {
      const newSelectedKeys: string[] = [];

      if (isCtrlPressed) {
        const index = innerSelectedKeys.indexOf(key);
        if (index === -1) newSelectedKeys.push(...innerSelectedKeys, key);
        else newSelectedKeys.push(...innerSelectedKeys.filter((k) => key !== k));
      } else if (isShiftPressed) {
        if (innerSelectedKeys.length === 0) newSelectedKeys.push(key);
        else {
          const [startSelectedKey] = innerSelectedKeys;
          const endSelectedKey = innerSelectedKeys[innerSelectedKeys.length - 1];

          const startIndex = dataSource.findIndex((k) => k[keyPropName] === startSelectedKey);
          const endIndex = dataSource.findIndex((k) => k[keyPropName] === endSelectedKey);
          const currentIndex = dataSource.findIndex((k) => k[keyPropName] === key);

          const pushList: any[] = [];
          if (startIndex > currentIndex) {
            pushList.push(...dataSource.slice(currentIndex, endIndex + 1));
          } else {
            pushList.push(...dataSource.slice(startIndex, currentIndex + 1));
          }
          newSelectedKeys.push(...pushList.map((k) => k[keyPropName]));
        }
      } else {
        newSelectedKeys.push(key);
      }

      setInnerSelectedKeys(newSelectedKeys);
      onSelect?.(
        newSelectedKeys,
        newSelectedKeys.map((key) => dataSource.find((i) => i[keyPropName] === key)),
      );
    },
    [dataSource, innerSelectedKeys, isCtrlPressed, isShiftPressed, keyPropName, onSelect],
  );

  const handleDrop = React.useCallback(
    (sourceKey: string, targetKey: string) => {
      const source = dataSource.find((i) => i[keyPropName] === sourceKey);
      const target = dataSource.find((i) => i[keyPropName] === targetKey);
      if (!source || !target) return;

      const targetRefList = dataSource.filter((i) => i[pidPropName] === target[pidPropName]);
      const targetIndex = targetRefList.findIndex((i) => i[keyPropName] === target[keyPropName]);

      //  判断是否是子要素
      const isChild = (data: TreeNodeDataSource, list: TreeNodeDataSource[]) => {
        const pid = data[pidPropName];
        const parent = list.find((item) => item.id === pid);
        return !!(pid && parent.type !== ComponentTypeEnum.BIZGROUP);
      };

      //  判断是否为母版
      const isTemplate = (data: TreeNodeDataSource) =>
        !!(data[isGroupPropName] && data.type !== ComponentTypeEnum.BIZGROUP);

      // const isGroup = (data: TreeNodeDataSource) =>
      //   !!(data[isGroupPropName] && data.type === ComponentTypeEnum.BIZGROUP);

      const sourceIsChild = isChild(source, dataSource);
      const targetIsChild = isChild(target, dataSource);

      // const sourceIsTemplate = isTemplate(source);
      const targetIsTemplate = isTemplate(target);

      // const sourceIsGroup = isGroup(source);
      // const targetIsGroup = isGroup(target);

      if (
        //  子要素只允许在同一母版内移动
        ((sourceIsChild || targetIsChild) && source[pidPropName] !== target[pidPropName]) ||
        //  如果 target 是template
        (targetIsTemplate && !sourceIsChild)
      )
        return;

      onDrop?.({
        source,
        sourceParent: dataSource.find((i) => i[keyPropName] === source[pidPropName]),
        target,
        targetParent: dataSource.find((i) => i[keyPropName] === target[pidPropName]),
        targetNext: targetRefList[targetIndex + 1],
      });
    },
    [dataSource, keyPropName, onDrop, pidPropName],
  );

  return (
    <div>
      {dataSource.map((item) => {
        const key = item[keyPropName];

        const isSelected = innerSelectedKeys.includes(key);
        const isExpanded = item[expandPropName];

        const state: DraggableTreeRenderNodeState = {
          isSelected,
          isExpanded,
        };

        return (
          <TreeNode
            key={key}
            dataSource={item}
            keyPropName={keyPropName}
            titlePropName={titlePropName}
            isGroupPropName={isGroupPropName}
            expandPropName={expandPropName}
            visible={allowVisible ? allowVisible(item) : true}
            isSelected={isSelected}
            isEditing={editingKey === key}
            iconRender={iconRender?.(item, state)}
            extendRender={extendRender?.(item, state)}
            dragType={dragType}
            onClick={(e) => {
              e.stopPropagation();
              handleItemClick(key);
            }}
            onRightClick={(e) => onRightClick?.(e, item)}
            onExpandClick={(e) => {
              e.stopPropagation();
              onExpandChange?.(e, item);
            }}
            onDrop={handleDrop}
            onDragStart={(e) => onDragStart?.(e, item)}
            onDragEnd={(e) => onDragEnd?.(e, item)}
            onNameEditChange={(name) => onNameEditChange?.(key, name)}
          />
        );
      })}
    </div>
  );
};

export default DraggableTree;
