/* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/ban-ts-comment */
/* eslint-disable @typescript-eslint/ban-types */
/* eslint-disable prefer-spread */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
import React, { useEffect, useState, useCallback } from 'react';
import { Pagination, CnCheckbox, CnRadio } from '@alife/cn-ui';
import classNames from 'classnames';
import {
  domType,
  ColumnProps,
  TableProps,
  ItemProps,
  FunctionObjI,
} from 'src/types/op-table';
import { Code, State, Image, Name } from './render';

import './index.scss';

function getAttr(attrs: Array<any>, obj: any) {
  return attrs
    ?.filter?.((attr) => attr && typeof attr === 'string')
    ?.reduce?.((tempObj, attr) => tempObj?.[attr], obj);
}

function transformRender(columns: ColumnProps[], renders: FunctionObjI) {
  columns.forEach((col) => {
    const { key } = col;
    if (renders[key]) {
      col.render = renders[key];
    }
  });
}

function getDomProps(props: domType, rest: any[]) {
  let ret = props;
  if (props instanceof Function) {
    ret = (props as Function).apply(null, rest);
  }
  return ret;
}

export const renderMap = new Map();
export default function OPTable(props: TableProps) {
  const {
    dataSource: tableInitData = [],
    columns = [],
    className,
    renders,
    rowProps,
    cellProps,
    primaryKey,
    selectedKeys,
    hasHeader = true,
    onRowClick = () => {},
    onPageChange = () => {},
    pagination = {
      enabled: false,
      pageSize: 10,
      currentPage: 1,
      onPageSizeChange: () => {},
      onCurrentPageChange: () => {},
    },
    showSelect = false, // 开启选择行
    rowSelection = {
      type: 'multiple',
    },
    onSelectChange = () => {}, // 选择行后的回调
    autoWidth = true, // 设置自动宽度后，表格列 设的宽度失效
  } = props;

  const [currentPage, setCurrentPage] = useState<number>(
    pagination.currentPage || 1,
  );
  const [pageSize, setPageSize] = useState<number>(pagination.pageSize || 10);
  const [indeterminate, setIndeterminate] = useState<boolean>(false);
  const [headerChecked, setHeaderChecked] = useState<boolean>(false);
  const [selectedRowKeys, setSelectedRowKeys] = useState<
    string | number | Array<string | number>
  >([]);
  const [selectedRowRecord, setSelectedRowRecord] = useState<
    ItemProps | ItemProps[]
  >([]);
  const [dataSource, setDataSource] = useState<ItemProps[]>(
    // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
    JSON.parse(JSON.stringify(tableInitData)),
  );

  renders && transformRender(columns, renders);

  useEffect(() => {
    setDataSource(tableInitData);
  }, [tableInitData]);

  // 分页条数改变
  const onPageSizeChange = React.useCallback(
    (v: number) => {
      setPageSize(v);
      setCurrentPage(1);
      pagination.onPageSizeChange && pagination.onPageSizeChange(v);
      onPageChange &&
        onPageChange({
          currentPage: 1,
          pageSize: v,
        });
    },
    [pagination, onPageChange],
  );

  // 翻页改变
  const onCurrentPageChange = React.useCallback(
    (v: number) => {
      setCurrentPage(v);
      pagination.onCurrentPageChange && pagination.onCurrentPageChange(v);
      onPageChange &&
        onPageChange({
          currentPage: v,
          pageSize,
        });
    },
    [pagination, onPageChange, pageSize],
  );

  const handleRowSelectionChangeMultiple = useCallback(
    (keys: (string | number)[]) => {
      const records: ItemProps[] = [];
      const tmpDataSource = dataSource;
      // 过滤传入的 keys 不存在于 dataSource 中
      keys = keys.filter(
        (k) =>
          !!dataSource.filter(
            (item, index) => k === (primaryKey ? item[primaryKey] : index),
          ).length,
      );
      if (keys && Array.isArray(keys)) {
        tmpDataSource.forEach((recordItem, recordIndex) => {
          if (
            // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
            keys.includes(primaryKey ? recordItem[primaryKey] : recordIndex)
          ) {
            records.push(recordItem);
            recordItem.checked = true;
            recordItem.selected = true;
          } else {
            recordItem.checked = false;
            recordItem.selected = false;
          }
        });
        setHeaderChecked(keys.length === tmpDataSource.length);
        setIndeterminate(
          !!(keys.length && keys.length !== tmpDataSource.length),
        );
        setSelectedRowKeys(keys);
        setSelectedRowRecord(records);
        setDataSource(tmpDataSource);
        onSelectChange && onSelectChange(keys, records);
      }
    },
    [dataSource, onSelectChange, primaryKey],
  );

  // 行选中状态改变-多选
  const onCheckedChangeFromTd = useCallback(
    (checked: boolean, record: ItemProps, index: number) => {
      let keys: (string | number)[] = selectedRowKeys as string[];
      const checkedKey = (primaryKey ? record[primaryKey] : index) as string;
      if (checked) {
        keys.push(checkedKey);
      } else {
        keys = keys.filter((k) => k !== checkedKey);
      }
      handleRowSelectionChangeMultiple(keys);
    },
    [handleRowSelectionChangeMultiple, primaryKey, selectedRowKeys],
  );

  const handleRowSelectionChangeSingle = useCallback(
    (checkedKey: string | number, checked: boolean) => {
      const tmpDataSource = dataSource;
      let record: ItemProps = {};
      tmpDataSource.forEach((recordItem, recordIndex) => {
        if (
          checkedKey === (primaryKey ? recordItem[primaryKey] : recordIndex)
        ) {
          recordItem.checked = checked;
          recordItem.selected = true;
          record = recordItem;
        } else {
          recordItem.checked = false;
          recordItem.selected = false;
        }
      });
      setSelectedRowKeys(checkedKey);
      setSelectedRowRecord(record);
      setDataSource(tmpDataSource);
      onSelectChange && onSelectChange(checkedKey, record);
    },
    [dataSource, onSelectChange, primaryKey],
  );

  // 行选中状态改变-单选
  const onCheckedChangeFromTdSingle = useCallback(
    (checked: boolean, record: ItemProps, index: number) => {
      const checkedKey = (primaryKey ? record[primaryKey] : index) as string;
      handleRowSelectionChangeSingle(checkedKey, checked);
    },
    [handleRowSelectionChangeSingle, primaryKey],
  );

  useEffect(() => {
    // 初始默认选中行
    if (showSelect && rowSelection.selectedRowKeys) {
      rowSelection.type === 'single'
        ? handleRowSelectionChangeSingle(
            rowSelection.selectedRowKeys as string,
            !!rowSelection.selectedRowKeys,
          )
        : handleRowSelectionChangeMultiple(
            rowSelection.selectedRowKeys as string[],
          );
    }
  }, [
    handleRowSelectionChangeMultiple,
    handleRowSelectionChangeSingle,
    rowSelection.selectedRowKeys,
    rowSelection.type,
    showSelect,
  ]);

  // 表头选中状态改变
  const onCheckedChangeFromTh = useCallback(
    (checked: boolean) => {
      setHeaderChecked(checked);
      setIndeterminate(false);
      if (checked) {
        handleRowSelectionChangeMultiple(
          dataSource.map(
            (item, index) => (primaryKey ? item[primaryKey] : index) as string,
          ),
        );
      }
      if (!checked) {
        handleRowSelectionChangeMultiple([]);
      }
    },
    [dataSource, handleRowSelectionChangeMultiple, primaryKey],
  );

  return (
    <div className={`op-table-wrapper`}>
      <table
        role='table'
        className={classNames('op-table', className, {
          'auto-width': autoWidth,
        })}
      >
        <colgroup>
          {showSelect && <col style={{ minWidth: 50 }}></col>}
          {columns.map((item) => (
            <col style={{ width: item.width }} key={item.key} />
          ))}
        </colgroup>
        {hasHeader && (
          <thead className='op-table-header'>
            <tr>
              {showSelect && (
                <th className='lock-select'>
                  {rowSelection.type === 'single' ? null : (
                    <CnCheckbox
                      indeterminate={indeterminate}
                      checked={headerChecked}
                      onChange={onCheckedChangeFromTh}
                    />
                  )}
                </th>
              )}
              {columns.map((item) => {
                return (
                  <th
                    key={item.key}
                    className={classNames(
                      `${getAlignTdClassName(item.align)} th_${item.key}`,
                      item?.lock ? `lock-${item.lock}` : '',
                      {
                        'lock-left-has-select': showSelect,
                      },
                    )}
                  >
                    {item.title || item.name}
                  </th>
                );
              })}
            </tr>
          </thead>
        )}

        <tbody className='next-table-body'>
          {dataSource.map((item, index) => {
            const currentKey: string | number = primaryKey
              ? item[primaryKey]
              : index;
            return (
              <tr
                {...getDomProps(rowProps || {}, [index, item])}
                className={getRowClassName(item, props)}
                role='row'
                key={currentKey}
                onClick={() => {
                  if (showSelect && rowSelection.type === 'single') {
                    handleRowSelectionChangeSingle(currentKey, true);
                  }
                  if (showSelect && rowSelection.type !== 'single') {
                    let newKeys: (string | number)[] = [];
                    if (item.checked) {
                      if (Array.isArray(selectedRowKeys)) {
                        newKeys = selectedRowKeys.filter(
                          (k: string | number) => currentKey !== k,
                        );
                      }
                    } else {
                      if (Array.isArray(selectedRowKeys)) {
                        newKeys = selectedRowKeys.concat([currentKey]);
                      }
                    }
                    handleRowSelectionChangeMultiple(newKeys);
                  }
                  onRowClick(item, index);
                }}
              >
                {showSelect && (
                  <td className='lock-select operate-table-cell__first'>
                    {rowSelection.type === 'single' ? (
                      <CnRadio
                        checked={item.checked}
                        onChange={(checked: boolean) => {
                          onCheckedChangeFromTdSingle(checked, item, index);
                        }}
                      />
                    ) : (
                      <CnCheckbox
                        checked={item.checked}
                        onChange={(checked: boolean) => {
                          onCheckedChangeFromTd(checked, item, index);
                        }}
                      />
                    )}
                  </td>
                )}
                {columns.map((column, colIndex) => {
                  return (
                    <td
                      {...getDomProps(cellProps || {}, [
                        item[column.key],
                        index,
                        colIndex,
                        item,
                      ])}
                      key={column.key}
                      className={classNames(
                        getAlignTdClassName(column.align),
                        getTdSerialClassName(
                          colIndex,
                          columns.length - 1,
                          showSelect,
                        ),
                        column?.lock ? `lock-${column.lock}` : '',
                        {
                          'lock-left-has-select': showSelect,
                        },
                      )}
                    >
                      {renderTD(item, column, index)}
                    </td>
                  );
                })}
              </tr>
            );
          })}
        </tbody>
      </table>
      {pagination && pagination?.enabled && (
        <div className='op-pagination-con'>
          <Pagination
            size='large'
            type='simple'
            pageSizeSelector='dropdown'
            {...pagination}
            current={currentPage}
            pageSize={pageSize}
            onPageSizeChange={onPageSizeChange}
            onChange={onCurrentPageChange}
          />
        </div>
      )}
    </div>
  );
}

function getRowClassName(data: ItemProps, props: TableProps) {
  const { primaryKey, selectedKeys, completeKeys } = props;
  const keyValue: string = data[primaryKey as string];
  return classNames({
    selected:
      data.selected || Array.from(selectedKeys ?? '').includes(keyValue),
    complete:
      data.complete ||
      data.completed ||
      Array.from(completeKeys ?? '').includes(keyValue),
  });
}

function getAlignTdClassName(align: string) {
  return (
    {
      right: 'td-align-right',
      left: 'td-align-left',
      center: 'td-align-center',
    }[align] || 'td-align-left'
  );
}

function getTdSerialClassName(
  index: number,
  total: number,
  showSelect: boolean,
) {
  return classNames('operate-table-cell', {
    'operate-table-cell__first': !showSelect && index === 0,
    'operate-table-cell__last': index === total,
  });
}

// 2023.2.27 render优化
function renderTD(record: ItemProps, column: ColumnProps, index: number) {
  const { key, render, options } = column;
  const value: string = getAttr([...(key?.split?.('.') || [])], record);
  const renderProps = { value, index, record, options };
  if (render instanceof Function) {
    // 自定义render
    return render(value, index, record, options) as FunctionObjI;
  }
  if (typeof render === 'string') {
    // 内置render
    const renderName = render.toLowerCase();
    switch (renderName) {
      case 'code':
        return <Code {...renderProps} />;
      case 'pkstatus':
        return <State {...renderProps} />;
      case 'image':
        return <Image {...renderProps} />;
      case 'name':
        return <Name {...renderProps} />;
      default:
        return value;
    }
  }
  return value;
}
