import React, {
  forwardRef,
  useContext,
  useCallback,
  CSSProperties,
} from "react";
import classNames from "classnames";
import { Text } from "../text";
import { TableBox } from "./TableBox";
import { TableProps } from "./TableProps";
import { useMiddleware } from "./util/use-middleware";
import { isCallable } from "../_util/is-callable";
import { useConfig } from "../_util/config-context";
import { TableVirtualizedBodyProps } from "./TableVirtualizedBody";
import {
  getFixedOffset,
  getFixedEndColumn,
  CellResizeObserver,
} from "./TableBody";
import { TableContext } from "./TableContext";
import { TableGroupableHeadItem } from "./addons/groupable";
import { DragContext } from "./addons/draggable/DraggableTable";
import { ResizeContext } from "./addons/resizable/ResizableTable";
import { getRowExactWidth } from "./util/get-row-exact-width";

export const TableHead = forwardRef(function TableHead(
  props: TableProps & {
    tableBoxStyle?: React.CSSProperties;
    // `scrollable` 开启虚拟滚动式注入该 props
    virtualizedOptions?: TableVirtualizedBodyProps;
    // `columnsDraggable` 开启时注入该 props
    columnsDraggable?: boolean;
    // `resizable` 开启时注入该 props
    columnsResizable?: boolean;
    // 固定列计算宽度
    onResize?: React.Dispatch<React.SetStateAction<number[]>>;
    headerRows?: TableGroupableHeadItem[][];
  },
  ref: React.Ref<HTMLDivElement>
) {
  const { classPrefix } = useConfig();
  const { scrollStatus, columnsWidths } = useContext(TableContext);
  const { dragEventHandlers, draggableColumns } = useContext(DragContext);
  const { isResizing } = useContext(ResizeContext);
  const {
    columns,
    records,
    addons,
    tableBoxStyle,
    disableTextOverflow,
    hideHeader,
    virtualizedOptions,
    headerRows,
    columnsDraggable,
    columnsResizable,
    onResize,
  } = props;

  const hasFixedColumn = columns.find(column => !!column.fixed);
  const renderColumn = useMiddleware(
    addons,
    "onInjectColumn"
  )((record, rowKey, recordIndex, column, columnIndex) => {
    const { key, header } = column;
    let children: React.ReactNode = header;

    // pass a render function
    if (isCallable(header)) {
      children = header(column);
    }

    // forgot to pass, use a key as a reminder
    if (header === undefined) {
      children = key;
    }

    // plain values
    if (typeof children === "string" || typeof children === "number") {
      children = (
        <Text
          overflow={!disableTextOverflow}
          title={disableTextOverflow ? undefined : String(children)}
        >
          {children}
        </Text>
      );
    }
    return {
      props: {},
      children,
    };
  });

  const tableHeaders = columns.map((column, index) => {
    const { props, children } = renderColumn(null, null, -1, column, index);
    const cKey = column.key;
    if (props.colSpan === 0) {
      return null;
    }
    if (virtualizedOptions) {
      props.style = {
        ...(props.style || {}),
        display: "inline-block",
        overflow: "hidden",
        boxSizing: "border-box",
        // 全部列都有宽度时要自适应
        flexGrow: column.width && !getRowExactWidth(columns) ? undefined : 1,
        flexBasis: column.width || 1,
      };
    }
    // 固定列
    if (
      (column.fixed === "left" || column.fixed === "right") &&
      column.fixed !== scrollStatus &&
      scrollStatus !== "no-scroll"
    ) {
      const end = getFixedEndColumn(columns);
      props.className = classNames(
        props.className,
        `${classPrefix}-table__td--fixed`,
        {
          [`${classPrefix}-table__td--fixed-${column.fixed}`]:
            end[column.fixed] === index,
        }
      );
      props.style = {
        ...(props.style || {}),
        position: "sticky",
        [column.fixed]: getFixedOffset(
          columns,
          index,
          columnsWidths,
          column.fixed
        ),
      };
    }

    // 列拖拽排序
    if (!column.fixed && columnsDraggable && !("ignore" in column)) {
      const {
        onDragEnter,
        onDragOver,
        onDrop,
        onDragStart,
        onDrag,
        onDragLeave,
        onDragEnd,
        onMouseEnter,
        onMouseLeave,
      } = dragEventHandlers;
      props["data-column"] = column.key;
      props.onDragEnter = onDragEnter;
      props.onDragOver = onDragOver;
      props.onDrop = onDrop;

      if (draggableColumns === "all" || draggableColumns.includes(cKey)) {
        props.draggable = true;
        props.onDragStart = onDragStart;
        props.onDrag = onDrag;
        props.onDragLeave = onDragLeave;
        props.onDragEnd = onDragEnd;
        props.onMouseEnter = onMouseEnter;
        props.onMouseLeave = onMouseLeave;
        props.className = classNames(props.className, "is-draggable");
      }
    }

    const childrenStyle: CSSProperties =
      columnsResizable && isResizing ? { pointerEvents: "none" } : {};

    const cell = (
      <Text key={cKey} parent="th" {...props} align={column.align}>
        <div style={childrenStyle}>{children}</div>
      </Text>
    );

    // 无数据时通过头确定列宽
    if ((records || []).length === 0 && hasFixedColumn) {
      return (
        <CellResizeObserver
          key={`${cKey}${index}`}
          onResize={width => {
            onResize(widths => {
              const columnsWidths = [...widths];
              columnsWidths[index] = width;
              return columnsWidths;
            });
          }}
        >
          {cell}
        </CellResizeObserver>
      );
    }

    return cell;
  });

  const renderGroupingHeader = useCallback(
    heads => {
      return headerRows.map(row => (
        <tr key={row.map(i => i.key).join("-")}>
          {row.map(({ key, isLeaf, title, align = "left", colSpan }) => {
            if (isLeaf) {
              const index = columns.findIndex(i => i.key === key);
              return heads[index];
            }
            return (
              <Text key={key} parent="th" colSpan={colSpan} align={align}>
                <div>{title}</div>
              </Text>
            );
          })}
        </tr>
      ));
    },
    [columns, headerRows]
  );

  return useMiddleware(
    addons,
    "onInjectHead"
  )(props => {
    // tableBoxStyle 可能包含横向滚动的 minWidth，如果有则覆盖计算的最小宽度
    const minWidth = virtualizedOptions
      ? tableBoxStyle?.minWidth || getRowExactWidth(props.columns) || undefined
      : tableBoxStyle?.minWidth;
    return (
      <div
        className={`${classPrefix}-table__header`}
        ref={ref}
        style={hideHeader ? { display: "none" } : undefined}
      >
        <TableBox
          columns={columns}
          style={{
            ...(tableBoxStyle || {}),
            display: virtualizedOptions ? "block" : undefined,
            minWidth,
          }}
          classPrefix={classPrefix}
        >
          <thead style={virtualizedOptions ? { display: "block" } : undefined}>
            {headerRows ? (
              renderGroupingHeader(tableHeaders)
            ) : (
              <tr style={virtualizedOptions ? { display: "flex" } : undefined}>
                {tableHeaders}
              </tr>
            )}
          </thead>
        </TableBox>
      </div>
    );
  })(props);
});

TableHead.displayName = "TableHead";
