import React, {
  createContext,
  useRef,
  useState,
  forwardRef,
  useContext,
} from "react";
import { getElementOffset } from "../../../_util/get-element-offset";
import { mergeRefs } from "../../../_util/merge-refs";
import { CheckTreeRelation } from "../../../checktree";
import { now } from "../../../_util/now";
import { deepCopy } from "../../../_util/deep-copy";
import { getRowKeyFromRecordKey } from "../../util/get-row-key-from-record-key";
import { useConfig } from "../../../util";

interface HoverTime {
  id: string;
  startTime: number;
}
interface DragInfos {
  dragKey?: string;
  dropKey?: string;
  depth?: number;
}

let hoverTimeInfos: HoverTime = {
  id: "",
  startTime: 0,
};

let tdPaddingLeft: undefined | number;
let insertLineHandler = 0;
let dragElementWidthFromCursorToHead = 0;
let dragRecord: { record?: any; index?: number } = {};

/**
 * 行拖拽Context
 */
interface RowsDragContextValue {
  /**
   * 供表格内容区设置其 DOM Ref
   */
  setBodyRef?: (element: HTMLDivElement) => void;

  /**
   * 表格body区Ref
   */
  bodyRef?: React.RefObject<HTMLDivElement>;

  /**
   * 供表格body区高亮插入线设置其 DOM Ref
   */
  setBodyLineRef?: (element: HTMLDivElement) => void;

  /**
   * 高亮插入线Ref
   */
  bodyLineRef?: React.RefObject<HTMLDivElement>;

  /**
   * 事件句柄对象
   */
  dragEventHandlers?: React.DOMAttributes<HTMLTableRowElement>;
}

/**
 * 拖拽 Context 实例
 */
export const RowsDragContext = createContext<RowsDragContextValue>({});
RowsDragContext.displayName = "RowsDragContext";

/**
 * 注入到外层容器
 */
interface RowsDraggableTableProps<Record = any> {
  /**
   * 表格
   */
  table: JSX.Element;

  /**
   * 拖拽完成回调
   */
  onDragEnd: (
    records: Record[],
    dragContext?: {
      dragKey?: string;
      dragRecordIndex?: number;
      dragRecord?: Record;
      dropKey?: string;
      dropDepth?: number;
    }
  ) => void;

  /**
   * 拖拽开始时回调
   */
  onDragStart?: (context: any) => void;

  /**
   * 提供 `relations` 属性，则会按照树状选择逻辑进行
   */
  relations?: CheckTreeRelation;

  /**
   * 用户进行展开/收起操作的时候，知会最新的键值
   */
  onExpandedKeysChange?: (
    value: string[],
    context?: {
      event: React.SyntheticEvent;
      operateType: "expand" | "collapse";
      operateKey: string;
      operateRecord: Record;
    }
  ) => void;

  /**
   * 展开行的键值
   */
  expandedKeys?: string[];

  /**
   * 子级相对父级缩进量
   * @default 20
   */
  indent?: number;

  /**
   * 数据记录
   */
  recordsData?: Record[];

  /**
   *
   */
  getRowKey: ReturnType<typeof getRowKeyFromRecordKey>;

  /**
   * 子树字段名
   */
  childrenColName?: string;

  /**
   * 拖拽类型，树拖拽排序或者仅行拖拽
   */
  dragType?: "tree" | "row";

  /**
   * icon 列宽度
   */
  iconColumnWidth?: number;
}
export function RowsDraggableTable({
  table,
  onDragEnd,
  onDragStart,
  relations,
  indent,
  onExpandedKeysChange,
  expandedKeys,
  recordsData,
  getRowKey,
  dragType,
  childrenColName,
  iconColumnWidth,
  ...props
}: RowsDraggableTableProps) {
  const [draggingRowKey, setDraggingRowKey] = useState("");
  const [dragInfos, setDragInfos] = useState<DragInfos>({});
  const bodyRef = useRef<HTMLDivElement>(null);
  const bodyLineRef = useRef<HTMLDivElement>(null);

  /**
   * 获取行的树深度 & paddingLeft
   * @param rowKey 行ID
   * @returns
   */
  const getDepthAndPaddingLeft = (rowKey: string) => {
    let paddingLeft = 0;
    let depth = 0;
    if (indent > 0) {
      let node = rowKey;
      while (relations && relations[node]) {
        depth += 1;
        node = relations[node];
      }
      paddingLeft = indent * depth;
    }

    return { paddingLeft, depth };
  };

  /**
   * 绘制插入线
   */
  const drawInsertLine = (width: number, left: number, top: number) => {
    if (insertLineHandler) {
      cancelAnimationFrame(insertLineHandler);
    }

    insertLineHandler = requestAnimationFrame(() => {
      bodyLineRef.current.style.display = "block";
      bodyLineRef.current.style.width = `${width}px`;
      bodyLineRef.current.style.transform = `translate3d(${left +
        iconColumnWidth}px, ${top}px, 0px)`;
      bodyLineRef.current.style.pointerEvents = "none";
    });
  };

  /**
   * 重建树
   * @param dragKey 拖拽排序的原行key
   * @param baseKey 拖拽到基准目标行的key
   * @param depth 插入的树的深度
   * @param records 表格数据
   */
  const rebuildTree = (
    dragKey: string,
    baseKey: string,
    depth: number,
    records: any[]
  ) => {
    let subtree;
    let baseKeyIndex;
    let subtreeIndex;

    const operateSubtreeDataLoop = (
      key: string,
      records: any[],
      type: "getAndRemoveSubtree" | "getBaseKeyIndex" | "addSubtree"
    ) => {
      if (!records || !Array.isArray(records)) {
        return;
      }

      /* eslint-disable */
      for (const index in records) {
        const isMatch = !!(
          records[index] &&
          getRowKey(records[index], parseInt(index, 10)) === key
        );
        const isGetSubtreeAndMatch = isMatch && type === "getAndRemoveSubtree";

        if (isGetSubtreeAndMatch) {
          subtree = records[index];
          subtreeIndex = index;
          records.splice(parseInt(index, 10), 1);
          dragRecord = {
            record: deepCopy(subtree),
            index: parseInt(index, 10),
          };
          break;
        }

        const isGetBaseKeyIndexAndMatch = isMatch && type === "getBaseKeyIndex";
        if (isGetBaseKeyIndexAndMatch) {
          baseKeyIndex = parseInt(index, 10);
          break;
        }

        const isAddSubtree = isMatch && type === "addSubtree";
        if (
          isAddSubtree &&
          typeof baseKeyIndex !== "undefined" &&
          records[index] &&
          subtree
        ) {
          if (
            records[index][childrenColName] &&
            Array.isArray(records[index][childrenColName])
          ) {
            records[index][childrenColName].splice(
              parseInt(baseKeyIndex, 10),
              0,
              subtree
            );
          } else {
            records[index][childrenColName] = [subtree];
          }

          break;
        }

        if (
          records[index] &&
          records[index][childrenColName] &&
          Array.isArray(records[index][childrenColName])
        ) {
          operateSubtreeDataLoop(key, records[index][childrenColName], type);
        }
      }
    };

    // 获取baseKeyIndex
    if (baseKey) {
      operateSubtreeDataLoop(baseKey, records, "getBaseKeyIndex");
    }

    // 获取并从原树中删除子树
    operateSubtreeDataLoop(dragKey, records, "getAndRemoveSubtree");
    const addSubtree = (baseKey: string, depth: number, records: any[]) => {
      const { depth: baseKeyDepth } = getDepthAndPaddingLeft(baseKey);
      const parentKey =
        relations && typeof relations[baseKey] !== "undefined"
          ? relations[baseKey]
          : null;

      // 插入首行
      if (baseKey === "") {
        records.splice(0, 0, subtree);
      }

      if ((!baseKeyIndex && baseKeyIndex !== 0) || !subtree) {
        return;
      }

      // 深度为0插入根节点
      if (depth === 0) {
        let nodeKey = baseKey;
        while (relations && relations[nodeKey]) {
          nodeKey = relations[nodeKey];
        }
        const baseKeyParentIndex = records.findIndex(
          (record: any, index) => getRowKey(record, index) === nodeKey
        );

        records.splice(baseKeyParentIndex + 1, 0, subtree);
        return;
      }

      // 为基准节点的兄弟节点
      if (depth === baseKeyDepth) {
        // 判断摘除的子节点是否是和base节点是兄弟节点
        if (relations[dragKey] !== relations[baseKey]) {
          baseKeyIndex += 1;
        }

        if (
          relations[dragKey] &&
          relations[baseKey] &&
          relations[dragKey] === relations[baseKey] &&
          subtreeIndex > baseKeyIndex
        ) {
          baseKeyIndex += 1;
        }

        operateSubtreeDataLoop(parentKey, records, "addSubtree");
        return;
      }

      // 为基准节点的子节点
      if (depth > baseKeyDepth) {
        baseKeyIndex = 0;
        operateSubtreeDataLoop(baseKey, records, "addSubtree");
        return;
      }

      // 为基准节点的父节点的兄弟节点
      if (depth < baseKeyDepth) {
        let len = baseKeyDepth - depth + 1;
        let nodeKey = baseKey;
        while (len > 0) {
          nodeKey = relations[nodeKey];
          len -= 1;

          // 更新下baseKeyIndex
          if (len === 1) {
            operateSubtreeDataLoop(nodeKey, records, "getBaseKeyIndex");
            baseKeyIndex += 1;
          }
        }

        operateSubtreeDataLoop(nodeKey, records, "addSubtree");
      }
    };

    // 添加子树
    addSubtree(baseKey, depth, records);

    const cleanEmptyTreeNode = (records: any[]) => {
      for (const record of records) {
        if (
          record[childrenColName] &&
          Array.isArray(record[childrenColName]) &&
          record[childrenColName].length > 0
        ) {
          cleanEmptyTreeNode(record[childrenColName]);
        }

        if (
          record[childrenColName] &&
          Array.isArray(record[childrenColName]) &&
          record[childrenColName].length === 0
        ) {
          delete record[childrenColName];
        }
      }
    };

    // 清除空数组属性
    cleanEmptyTreeNode(records);
  };

  const rebuildRow = (
    dragKey: string,
    baseKey: string,
    depth: number,
    records: any[]
  ) => {
    if (dragKey === baseKey) {
      return;
    }

    let dragKeyIndex;
    let baseKeyIndex;

    /* eslint-disable */
    for (const index in records) {
      if (
        records[index] &&
        getRowKey(records[index], parseInt(index, 10)) === dragKey
      ) {
        dragKeyIndex = parseInt(index, 10);
      }

      if (
        records[index] &&
        getRowKey(records[index], parseInt(index, 10)) === baseKey
      ) {
        baseKeyIndex = parseInt(index, 10);
      }
    }

    if (!baseKey) {
      records.splice(0, 0, records.splice(dragKeyIndex, 1)[0]);
    } else {
      if (baseKeyIndex < dragKeyIndex) {
        baseKeyIndex += 1;
      }
      const row = records.splice(dragKeyIndex, 1)[0];
      records.splice(baseKeyIndex, 0, row);
      dragRecord = { record: deepCopy(row), index: parseInt(dragKeyIndex, 10) };
    }
  };

  const dragEventHandlers = {
    onDragStart: (event: React.DragEvent) => {
      event.stopPropagation();
      // QQ 浏览器设置 text 不触发 drop
      // event.dataTransfer.setData("text", "text");
      event.dataTransfer.setData("image/png", "img");
      event.dataTransfer.effectAllowed = "move";
      const draggingRowKey = event.currentTarget.attributes.getNamedItem(
        "data-key"
      ).value;

      // 获取光标到被拖元素头部距离
      const dragElementProperties = getElementOffset(
        event.currentTarget as HTMLElement
      );
      dragElementWidthFromCursorToHead =
        event.clientX - dragElementProperties.left;
      dragElementWidthFromCursorToHead =
        dragElementWidthFromCursorToHead < 0
          ? 0
          : dragElementWidthFromCursorToHead;

      if (typeof tdPaddingLeft === "undefined") {
        const firstTdNode = event.currentTarget.firstChild;
        tdPaddingLeft =
          typeof window.getComputedStyle !== "undefined"
            ? parseInt(
                window
                  .getComputedStyle(firstTdNode as Element, null)
                  .getPropertyValue("padding-left")
              )
            : 0;
      }

      if (dragType === "tree" && expandedKeys) {
        const newExpandedKeys = new Set(expandedKeys);
        if (newExpandedKeys.has(draggingRowKey)) {
          newExpandedKeys.delete(draggingRowKey);
          onExpandedKeysChange([...newExpandedKeys]);
        }
      }

      setDragInfos({});
      setDraggingRowKey(draggingRowKey);
      onDragStart({
        draggableId: draggingRowKey,
        mode: "FLUID",
        source: {
          droppableId: "droppable",
          index: recordsData.findIndex((row, index) => getRowKey(row, index) === draggingRowKey)
        },
        type: "DEFAULT",
      });
    },
    onDrag: (event: React.DragEvent) => {
      event.stopPropagation();
    },
    onDragEnd: (event: React.DragEvent) => {
      if (insertLineHandler) {
        cancelAnimationFrame(insertLineHandler);
      }
      bodyLineRef.current.style.display = "none";
      setDraggingRowKey("");
    },
    onDragEnter: (event: React.DragEvent) => {
      event.preventDefault();
      if (dragType === "tree") {
        hoverTimeInfos = {
          id: event.currentTarget.attributes.getNamedItem("data-key").value,
          startTime: now(),
        };
      }
    },
    onDragOver: (event: React.DragEvent) => {
      event.preventDefault();
      event.dataTransfer.dropEffect = "move";
      const bodyElementProperties = getElementOffset(bodyRef.current);
      const hoverRowKey = event.currentTarget.attributes.getNamedItem(
        "data-key"
      ).value;
      const hoverElementProperties = getElementOffset(
        event.currentTarget as HTMLElement
      );
      const hoverElementMiddleLineYValue =
        hoverElementProperties.top + hoverElementProperties.height / 2;
      const previousNode = event.currentTarget.previousElementSibling;
      const nextNode = event.currentTarget.nextElementSibling;

      let dragElementHeadClientX =
        event.clientX - dragElementWidthFromCursorToHead;
      dragElementHeadClientX =
        dragElementHeadClientX < 0 ? 0 : dragElementHeadClientX;

      let insertDepth = 0;
      let insertLineLeft = 0;
      let insertLineTop = 0;
      let insertLineWidth = hoverElementProperties.width;

      if (dragType === "row") {
        let dropKey = "";

        // hover行是拖拽的行，无法插入
        if (draggingRowKey === hoverRowKey) {
          bodyLineRef.current.style.display = "none";
          return;
        }

        if (event.clientY < hoverElementMiddleLineYValue && previousNode) {
          const previousElementProperties = getElementOffset(
            previousNode as HTMLElement
          );
          dropKey = previousNode.attributes.getNamedItem("data-key").value;
          insertLineTop =
            previousElementProperties.top -
            bodyElementProperties.top +
            previousElementProperties.height -
            1;
        }

        if (event.clientY >= hoverElementMiddleLineYValue) {
          dropKey = hoverRowKey;
          insertLineTop =
            hoverElementProperties.top -
            bodyElementProperties.top +
            hoverElementProperties.height -
            1;
        }

        drawInsertLine(insertLineWidth, 0, insertLineTop);
        setDragInfos({
          depth: 0,
          dragKey: draggingRowKey,
          dropKey,
        });
        return;
      }

      // 如果是关闭情况需要执行展开操作
      const parentKeys = [];

      /* eslint-disable */
      for (const key in relations) {
        parentKeys.push(relations[key]);
      }
      if (hoverRowKey === hoverTimeInfos.id) {
        const spendTime = now() - hoverTimeInfos.startTime;
        if (
          !expandedKeys.includes(hoverRowKey) &&
          parentKeys.includes(hoverRowKey) &&
          spendTime >= 1000 &&
          hoverRowKey !== draggingRowKey
        ) {
          const newExpandedKeys = new Set(expandedKeys);
          newExpandedKeys.add(hoverRowKey);
          onExpandedKeysChange([...newExpandedKeys]);
        }
      } else {
        hoverTimeInfos.id = hoverRowKey;
        hoverTimeInfos.startTime = now();
      }

      // 插入线绘制
      const {
        paddingLeft: hoverElementPaddingLeft,
        depth: hoverElementDepth,
      } = getDepthAndPaddingLeft(hoverRowKey);

      // 插入hover元素上方
      if (event.clientY < hoverElementMiddleLineYValue) {
        if (!previousNode) {
          insertLineLeft = hoverElementPaddingLeft;
          insertLineTop =
            hoverElementProperties.top - bodyElementProperties.top;
          insertDepth = 0;
          drawInsertLine(insertLineWidth, insertLineLeft, insertLineTop);
          setDragInfos({
            depth: insertDepth,
            dragKey: draggingRowKey,
            dropKey: "",
          });
          return;
        }

        const previousRowKey = previousNode.attributes.getNamedItem("data-key")
          .value;
        const {
          paddingLeft: previousElementPaddingLeft,
          depth: previousElementDepth,
        } = getDepthAndPaddingLeft(previousRowKey);
        const previousElementProperties = getElementOffset(
          previousNode as HTMLElement
        );
        const previousElementInsertBoundaryXValue =
          previousElementProperties.left +
          previousElementPaddingLeft +
          indent * 3;

        if (draggingRowKey === previousRowKey) {
          bodyLineRef.current.style.display = "none";
          return;
        }

        // 上层元素是父级情况，只能插入其子级
        if (previousElementDepth < hoverElementDepth) {
          insertLineLeft =
            previousElementProperties.left -
            bodyElementProperties.left +
            previousElementPaddingLeft +
            indent +
            tdPaddingLeft;
          insertLineTop =
            previousElementProperties.top -
            bodyElementProperties.top +
            previousElementProperties.height -
            1;
          insertDepth = previousElementDepth + 1;
          insertLineWidth -= insertLineLeft;
          drawInsertLine(insertLineWidth, insertLineLeft, insertLineTop);
          setDragInfos({
            depth: insertDepth,
            dragKey: draggingRowKey,
            dropKey: previousRowKey,
          });
          return;
        }

        // 上层是同级，可以插入其同级或子级
        if (
          dragElementHeadClientX > previousElementInsertBoundaryXValue &&
          previousElementDepth === hoverElementDepth
        ) {
          insertLineLeft =
            previousElementProperties.left -
            bodyElementProperties.left +
            previousElementPaddingLeft +
            indent +
            tdPaddingLeft;
          insertLineTop =
            previousElementProperties.top -
            bodyElementProperties.top +
            previousElementProperties.height -
            1;
          insertDepth = previousElementDepth + 1;
        } else if (previousElementDepth > hoverElementDepth) {
          // 上层非子级，当深度大于hover行，最终插入深度不能浅于hover行
          const distance =
            previousElementInsertBoundaryXValue - dragElementHeadClientX;
          const substractDepth = Math.floor(distance / indent);
          insertDepth = previousElementDepth - substractDepth;
          insertDepth =
            insertDepth < hoverElementDepth ? hoverElementDepth : insertDepth;
          insertDepth =
            insertDepth > previousElementDepth
              ? previousElementDepth + 1
              : insertDepth;
          insertLineLeft =
            previousElementProperties.left -
            bodyElementProperties.left +
            insertDepth * indent +
            tdPaddingLeft;
          insertLineTop =
            previousElementProperties.top -
            bodyElementProperties.top +
            previousElementProperties.height -
            1;
        } else {
          insertLineLeft =
            previousElementProperties.left -
            bodyElementProperties.left +
            previousElementPaddingLeft +
            tdPaddingLeft;
          insertLineTop =
            previousElementProperties.top -
            bodyElementProperties.top +
            previousElementProperties.height -
            1;
          insertDepth = previousElementDepth;
        }

        insertLineWidth -= insertLineLeft;
        drawInsertLine(insertLineWidth, insertLineLeft, insertLineTop);
        setDragInfos({
          depth: insertDepth,
          dragKey: draggingRowKey,
          dropKey: previousRowKey,
        });
        return;
      }

      // 插入hover元素下方
      // hover行是拖拽的行，且为第一级（深度为0）不能操作
      if (draggingRowKey === hoverRowKey && hoverElementDepth === 0) {
        bodyLineRef.current.style.display = "none";
        return;
      }

      // 判断插入层级的clientX临界点
      const hoverElementInsertBoundaryXValue =
        hoverElementProperties.left + hoverElementPaddingLeft + indent * 3;

      // hover行是拖拽的行，此处可以插入其父级
      if (
        (draggingRowKey === hoverRowKey && hoverElementDepth > 0) ||
        !nextNode
      ) {
        const distance =
          hoverElementInsertBoundaryXValue - dragElementHeadClientX;
        const substractDepth = Math.floor(distance / indent);
        insertDepth = hoverElementDepth - substractDepth;
        insertDepth = insertDepth < 0 ? 0 : insertDepth;
        insertDepth =
          insertDepth > hoverElementDepth
            ? !nextNode && draggingRowKey !== hoverRowKey
              ? hoverElementDepth + 1
              : hoverElementDepth
            : insertDepth;
        insertLineLeft =
          hoverElementProperties.left -
          bodyElementProperties.left +
          insertDepth * indent +
          tdPaddingLeft;
        insertLineTop =
          hoverElementProperties.top -
          bodyElementProperties.top +
          hoverElementProperties.height -
          1;
        insertLineWidth -= insertLineLeft;
        drawInsertLine(insertLineWidth, insertLineLeft, insertLineTop);
        setDragInfos({
          depth: insertDepth,
          dragKey: draggingRowKey,
          dropKey: hoverRowKey,
        });
        return;
      }

      const nextRowKey = nextNode.attributes.getNamedItem("data-key").value;
      const { depth: nextElementDepth } = getDepthAndPaddingLeft(nextRowKey);

      // hover行的下一个是子级，只能插入hover行后子级
      if (hoverElementDepth < nextElementDepth) {
        insertLineLeft =
          hoverElementProperties.left -
          bodyElementProperties.left +
          hoverElementPaddingLeft +
          indent +
          tdPaddingLeft;
        insertLineTop =
          hoverElementProperties.top -
          bodyElementProperties.top +
          hoverElementProperties.height -
          1;
        insertDepth = hoverElementDepth + 1;
        insertLineWidth -= insertLineLeft;
        drawInsertLine(insertLineWidth, insertLineLeft, insertLineTop);
        setDragInfos({
          depth: insertDepth,
          dragKey: draggingRowKey,
          dropKey: hoverRowKey,
        });
        return;
      }

      // hover行的下一个是同级或大级，插入hover行后不能超过下一级深度
      if (dragElementHeadClientX > hoverElementInsertBoundaryXValue) {
        insertLineLeft =
          hoverElementProperties.left -
          bodyElementProperties.left +
          (hoverElementDepth + 1) * indent +
          tdPaddingLeft;
        insertLineTop =
          hoverElementProperties.top -
          bodyElementProperties.top +
          hoverElementProperties.height -
          1;
        insertDepth = hoverElementDepth + 1;
      } else {
        const distance =
          hoverElementInsertBoundaryXValue - dragElementHeadClientX;
        const substractDepth = Math.floor(distance / indent);
        insertDepth = hoverElementDepth - substractDepth;
        insertDepth =
          insertDepth < nextElementDepth ? nextElementDepth : insertDepth;
        insertLineLeft =
          hoverElementProperties.left -
          bodyElementProperties.left +
          insertDepth * indent +
          tdPaddingLeft;
        insertLineTop =
          hoverElementProperties.top -
          bodyElementProperties.top +
          hoverElementProperties.height -
          1;
      }

      insertLineWidth -= insertLineLeft;
      drawInsertLine(insertLineWidth, insertLineLeft, insertLineTop);
      setDragInfos({
        depth: insertDepth,
        dragKey: draggingRowKey,
        dropKey: hoverRowKey,
      });
    },
    onDragLeave: (event: React.DragEvent) => {
      event.preventDefault();
    },
    onDrop: (event: React.DragEvent) => {
      event.preventDefault();
      if (!draggingRowKey) {
        return;
      }

      const records = deepCopy(recordsData);
      if (dragInfos.dragKey && dragType === "tree") {
        rebuildTree(
          dragInfos.dragKey,
          dragInfos.dropKey,
          dragInfos.depth,
          records
        );
      }

      if (dragInfos.dragKey && dragType === "row") {
        rebuildRow(
          dragInfos.dragKey,
          dragInfos.dropKey,
          dragInfos.depth,
          records
        );
      }

      if (insertLineHandler) {
        cancelAnimationFrame(insertLineHandler);
      }

      bodyLineRef.current.style.display = "none";

      onDragEnd(records, {
        dragKey: dragInfos.dragKey,
        dragRecordIndex: dragRecord.index,
        dragRecord: dragRecord.record,
        dropKey: dragInfos.dropKey,
        dropDepth: dragInfos.depth,
      });
    },
  };

  const dragContext: RowsDragContextValue = {
    bodyRef,
    setBodyRef: body => {
      bodyRef.current = body;
    },
    bodyLineRef,
    setBodyLineRef: bodyLine => {
      bodyLineRef.current = bodyLine;
    },
    dragEventHandlers,
  };

  return (
    <RowsDragContext.Provider value={dragContext}>
      {React.cloneElement(table, {
        ...props,
      })}
    </RowsDragContext.Provider>
  );
}

interface DraggableTableRowProps {
  row: JSX.Element;
  dataKey: string;
  draggableRowKeys?: string[] | "all";
}

export const DraggableTableRow = forwardRef(
  (
    { row, dataKey, draggableRowKeys, ...props }: DraggableTableRowProps,
    ref: React.Ref<HTMLDivElement>
  ) => {
    const { dragEventHandlers } = useContext(RowsDragContext);
    let eventHandlers = {};
    let isDraggable = true;
    if (
      draggableRowKeys === "all" ||
      (draggableRowKeys &&
        Array.isArray(draggableRowKeys) &&
        draggableRowKeys.includes(dataKey))
    ) {
      eventHandlers = dragEventHandlers;
    } else {
      isDraggable = false;
    }

    return React.cloneElement(row, {
      ...props,
      ...eventHandlers,
      ref,
      draggable: isDraggable,
      "data-key": dataKey,
    });
  }
);

/**
 * 包装表格内容区
 */
interface RowsDraggableTableBodyProps {
  body: React.FunctionComponentElement<{
    ref: React.Ref<HTMLDivElement>;
    style: React.CSSProperties;
    children: JSX.Element;
  }>;
}
export const RowsDraggableTableBody = forwardRef(
  (
    { body, ...props }: RowsDraggableTableBodyProps,
    ref: React.Ref<HTMLDivElement>
  ) => {
    const { classPrefix } = useConfig();
    const { setBodyRef, setBodyLineRef } = useContext(RowsDragContext);
    return (
      <div style={{ position: "relative", overflow: "visible" }}>
        <div
          ref={setBodyLineRef}
          className={`${classPrefix}-table__target-line--vertical`}
          style={{ display: "none" }}
        />
        {React.cloneElement(body, {
          ...props,
          ref: mergeRefs(setBodyRef, body.ref, ref),
        })}
      </div>
    );
  }
);

RowsDraggableTable.displayName = "RowsDraggableTable";
