import React, { useRef, useContext, useEffect, useMemo } from "react";
import {
  VariableSizeList,
  ListOnScrollProps,
  ListChildComponentProps,
} from "react-window";
import classNames from "classnames";
import { forwardRefWithStatics } from "../_util/forward-ref-with-statics";
import { mergeRefs } from "../_util/merge-refs";
import { TreeData, TreeProps } from "./TreeProps";
import { TreeNode, TreeNodeInnerProps } from "./TreeNode";
import { TreeContext } from "./TreeContext";
import { useConfig } from "../_util/config-context";

type InnerTreeData = TreeData & TreeNodeInnerProps;

const defaultItemHeight = 32;

export interface VirtualizedTreeListProps {
  /**
   * 树节点
   */
  nodes?: InnerTreeData[];

  /**
   * 列表高
   */
  height?: TreeProps["height"];

  /**
   * 滚动到底部事件
   */
  onScrollBottom?: (props: ListOnScrollProps) => void;

  /**
   * 虚拟滚动组件 ref
   */
  virtualizedRef?: React.Ref<VariableSizeList>;
}

function Row({ data, index, style }: ListChildComponentProps<InnerTreeData[]>) {
  const { classPrefix } = useConfig();
  const { activable, activeIds } = useContext(TreeContext);
  const { pure, style: nodeStyle = {}, className, ...props } = data[index];

  return (
    <li
      className={classNames(`${classPrefix}-tree__node`, className, {
        // 高亮
        // eslint-disable-next-line react/destructuring-assignment
        "is-selected": activable && activeIds.includes(props.id),
        [`${classPrefix}-tree__node--transparent`]: pure,
      })}
      style={{ ...style, ...nodeStyle }}
    >
      <TreeNode {...props} />
    </li>
  );
}

export const VirtualizedTreeList = forwardRefWithStatics(
  function VirtualizedList(
    {
      nodes,
      height: listHeight,
      onScrollBottom,
      virtualizedRef,
    }: VirtualizedTreeListProps,
    ref: React.Ref<HTMLUListElement>
  ) {
    const vRef = useRef<VariableSizeList>(null);
    const listRef = useRef<HTMLDivElement>(null);

    // VariableSizeList 默认按 index 缓存 style，需手动触发重置
    useEffect(() => {
      if (vRef.current) {
        nodes.forEach((_, i) => vRef.current.resetAfterIndex(i, true));
      }
    }, [nodes]);

    const [minHeight, maxHeight] = Array.isArray(listHeight)
      ? listHeight
      : [listHeight, listHeight];

    const itemsSize = useMemo(
      () =>
        nodes.reduce(
          (p, _, i) => p + (nodes[i].height || defaultItemHeight),
          0
        ),
      [nodes]
    );

    const height = useMemo(() => {
      if (typeof minHeight === "number" && minHeight > itemsSize) {
        return minHeight;
      }
      if (typeof maxHeight === "number" && maxHeight < itemsSize) {
        return maxHeight;
      }
      if (
        typeof minHeight === "number" &&
        typeof maxHeight === "number" &&
        minHeight < itemsSize &&
        maxHeight > itemsSize
      ) {
        return itemsSize;
      }
      return maxHeight ?? itemsSize;
    }, [itemsSize, maxHeight, minHeight]);

    function handleScroll(props: ListOnScrollProps) {
      if (listRef.current && onScrollBottom) {
        if (typeof maxHeight !== "number" || itemsSize > maxHeight) {
          const { scrollHeight, scrollTop, clientHeight } = listRef.current;
          if (scrollHeight <= Math.ceil(clientHeight + scrollTop + 1)) {
            onScrollBottom(props);
          }
        }
      }
    }

    return (
      <VariableSizeList
        ref={mergeRefs(virtualizedRef, vRef)}
        innerRef={ref}
        outerRef={listRef}
        height={height}
        width="100%"
        itemCount={nodes.length}
        itemSize={index => {
          return nodes[index].height || defaultItemHeight;
        }}
        innerElementType="ul"
        onScroll={handleScroll}
        itemKey={index => nodes[index].pathKey || nodes[index].id}
        itemData={nodes}
      >
        {Row}
      </VariableSizeList>
    );
  },
  {
    displayName: "VirtualizedList",
  }
);
