import React from 'react';
import { Renderer } from '../../../factory';
import Table, { TableProps } from '../../../renderers/Table';
import { LionBiTableStore, IColumn, IRow } from '../../../store/lionBiTable';
import {
  isPureVariable,
  resolveVariable,
  resolveVariableAndFilter,
  tokenize
} from '../../../utils/tpl-builtin';
import { filter } from '../../../utils/tpl';
import {
  anyChanged,
  noop,
  isArrayChildrenModified,
} from '../../../utils/helper';
import Checkbox from '../../../components/Checkbox';
import { Icon } from '../../../components/icons';
import Spinner from '../../../components/Spinner';
import { filterFn, sortFn } from '../../../utils/utils';
import { DATAKEYID } from '../../../store/crud';
import { handleFilter, handleSort } from '../../../store/utils/commonTableFunction';
import { isMobile } from '../../../utils/helper';
export default class LionBiTable extends Table {

  static defaultProps: Partial<TableProps> = {
    className: '',
    placeholder: 'placeholder.noData',
    tableClassName: '',
    source: '$items',
    selectable: false,
    columnsTogglable: false,
    affixHeader: true,
    headerClassName: '',
    footerClassName: '',
    toolbarClassName: '',
    headerToolbarClassName: '',
    footerToolbarClassName: '',
    primaryField: 'id',
    itemCheckableOn: '',
    itemDraggableOn: '',
    hideCheckToggler: isMobile(),
    canAccessSuperData: false,
    resizable: true
  };
  tableWindowRef: React.RefObject<any>
  constructor(props: TableProps) {
    super(props);
    this.tableWindowRef = React.createRef()
    this.syncRows(this.props, undefined) && this.syncSelected();
  }

  syncRows(
    props: TableProps,
    prevProps?: TableProps
  ) {
    const { store } = props
    const source = props.source;
    const value = props.value || props.items;
    let rows: Array<object> = [];
    let updateRows = false;

    if (
      Array.isArray(value) &&
      (!prevProps || (prevProps.value || prevProps.items) !== value)
    ) {
      updateRows = true;
      rows = value;
    } else if (typeof source === 'string') {
      const resolved = resolveVariableAndFilter(source, props.data, '| raw');
      const prev = prevProps
        ? resolveVariableAndFilter(source, prevProps.data, '| raw')
        : null;

      if (prev && prev === resolved) {
        updateRows = false;
      } else if (Array.isArray(resolved)) {
        updateRows = true;
        rows = resolved;
      }
    }

    if (updateRows) {
      if (rows && rows.length > 0) {
        this.updateColumns(rows)
        rows = this.groupRowDatas(rows)
      }
      store.initRows(rows, props.getEntryId, props.reUseRow);
    }
    typeof props.selected !== 'undefined' &&
      store.updateSelected(props.selected, props.valueField);
    return updateRows;
  }

  updateColumns(rowDatas: any[]) {
    const { columns = [], treeFields = '', groupFields = '', groupBy: { groupByField = '', tpl = '', order = 'asc' }, store } = this.props
    const groupValues = rowDatas.reduce<any[]>((previous, current) => {
      const groupByValue = current[groupByField]
      if (!previous.includes(groupByValue)) {
        previous.push(groupByValue)
      }
      return previous
    }, []).sort((a, b) => sortFn(a, b, order.toLocaleLowerCase()))

    const newColumns: any[] = []

    columns.forEach(column => {
      const treeFiledArr = treeFields.split(',').map((field: string) => field.trim())
      if (treeFiledArr.includes(column.name)) {
        newColumns.push(column)
      }
    })

    groupValues.forEach(value => {
      columns.forEach(column => {
        if (groupFields.includes(column.name)) {
          const obj = { [groupByField]: value }
          const groupColumnName = `${value}_${column.name}`
          const tempColumn = {
            ...column,
            groupName: tokenize(tpl, obj),
            label: tokenize(column.label, obj),
            name: groupColumnName,
            rawName: column.name
          }
          if (column.quickEdit) {
            tempColumn.quickEdit = { ...column.quickEdit, name: groupColumnName }
          }
          if (typeof column.tpl == 'string') {
            tempColumn.tpl = column.tpl.replaceAll(column.name, groupColumnName)
          }
          newColumns.push(tempColumn)
        }
      })
    })
    if (store.columnsInfo && Object.keys(store.columnsInfo).length) {
      const colsArr = Object.entries(store.columnsInfo).sort(
        (a: [string, { hidden?: 0 | 1, index: number, fixed: string }],
          b: [string, { hidden?: 0 | 1, index: number, fixed: string }]) => a[1]?.index - b[1]?.index);
      let newCols: any[] = []
      colsArr.forEach(([k, v]) => {
        const target = newColumns.find(colItem => colItem.name === k)
        if (target) {
          newCols.push({ ...target, ...(v as object) })
        }
      })
      const restColumns = newColumns.filter(item => !newCols.find((col: any) => col.name === item.name))
      store.updateColumns([...newCols, ...restColumns])
    } else {
      store.updateColumns(newColumns)
    }
  }

  groupRowDatas(rowDatas: any[]) {
    const { levelField, groupFields, groupBy: { groupByField }, primaryField } = this.props
    const fields = groupFields.split(',')
    const levelfs = levelField.split(',')

    let temps = rowDatas.reduce<any[]>((previous, current) => {
      const index = previous.findIndex(item => {
        for (let i = 0; i < levelfs.length; i++) {
          const fs = levelfs[i]
          if (item[fs] !== current[fs]) {
            return false
          }
        }
        return true
      })
      if (index == -1) {
        const target = { ...current }
        const groupName = current[groupByField]
        fields.forEach((field: string) => {
          target[`${groupName}_${field}`] = target[field]
        })
        previous.push(target)
      } else {
        const target = previous[index]
        const groupName = current[groupByField]
        if (primaryField) {
          target[primaryField] = String(target[primaryField]) + `,${current[primaryField]}`
        }
        fields.forEach((field: string) => {
          target[`${groupName}_${field}`] = current[field]
          target[field] = String(target[field]) + `,${current[field]}`
        })
        previous[index] = target
      }
      return previous
    }, [])

    const filterColumns: Map<string, any> = this.props.store.filterColumns
    if (filterColumns.size > 0) {
      temps = handleFilter(temps.slice(), Array.from(filterColumns.keys()), filterColumns)
    }
    const orderColumns: Map<string, any> = this.props.store.orderColumns
    if (orderColumns.size > 0) {
      temps = handleSort(temps.slice(), Array.from(orderColumns.keys()), orderColumns, this.props.store.modefiedMap.modifiedDataSet)
    }
    return temps
  }

  handleToggleExpanded(item: IRow) {
    // 异步加载--暂不用
    // if (item.defer && !item.loaded) {
    //   this.handleDeferLoad?.(item);
    //   return;
    // }
    item.toggleExpanded()
  }

  // @ts-ignore
  renderCell(
    region: string,
    column: IColumn,
    item: IRow,
    props: any,
    ignoreDrag = false
  ) {
    const {
      render,
      store,
      multiple,
      classPrefix: ns,
      classnames: cx,
      checkOnItemClick,
      popOverContainer,
      canAccessSuperData,
      itemBadge,
      groupBy,
      primaryField,
      levelField,
      treeFields
    } = this.props;

    if (column.name && item.rowSpans[column.name] === 0) {
      return null;
    }

    // Jay
    const { rowClassName, rowClassNameExpr } = this.props
    const stickyWidths = store.stickyWidths
    const leftFixedColumns = store.leftFixedColumns
    const lastLeftFixedIndex = leftFixedColumns[leftFixedColumns.length - 1]?.index
    const rightFixedColumns = store.rightFixedColumns
    const firstRightFixedIndex = rightFixedColumns[0]?.index

    const style: any = {}
    column.fixed && (style.position = "sticky")
    column.fixed === 'left' && (style.left = stickyWidths?.[`${column.index}`])
    column.fixed === 'right' && (style.right = stickyWidths?.[`${column.index}`])
    column.fixed && (style.zIndex = 1)

    if (column.type === '__checkme') {
      return (
        <td key={props.key} className={cx(column.pristine.className,
          // Jay
          rowClassNameExpr
            ? filter(rowClassNameExpr, item.data)
            : rowClassName, {
          'fixed-left-last': column.index === lastLeftFixedIndex,
          'fixed-right-first': column.index === firstRightFixedIndex,
        })} style={style}>
          <Checkbox
            classPrefix={ns}
            type={multiple ? 'checkbox' : 'radio'}
            checked={item.checked}
            disabled={item.checkdisable}
            onChange={
              checkOnItemClick ? noop : this.handleCheck.bind(this, item)
            }
          />
        </td>
      );
    } else if (column.type === '__dragme') {
      return (
        <td key={props.key} className={cx(column.pristine.className,
          // Jay
          rowClassNameExpr
            ? filter(rowClassNameExpr, item.data)
            : rowClassName, {
          'fixed-left-last': column.index === lastLeftFixedIndex,
        })} style={style}>
          {item.draggable ? <Icon icon="drag-bar" className="icon" /> : null}
        </td>
      );
    } else if (column.type === '__expandme') {
      return (
        <td key={props.key} className={cx(column.pristine.className,
          // Jay
          rowClassNameExpr
            ? filter(rowClassNameExpr, item.data)
            : rowClassName, {
          'fixed-left-last': column.index === lastLeftFixedIndex,
        })} style={style}
        >
          {item.depth > 2
            ? Array.from({ length: item.depth - 2 }).map((_, index) => (
              <i key={index} className={cx('Table-divider-' + (index + 1))} />
            ))
            : null}

          {item.expandable ? (
            item.loading ? (
              <Spinner
                size="sm"
                show
                icon="reload"
                spinnerClassName={cx('Table-expandBtn')}
              />
            ) :
              <a
                className={cx(
                  'Table-expandBtn',
                  item.expanded ? 'is-active' : ''
                )}
                onClick={() => this.handleToggleExpanded(item)}
              >
                <Icon icon="right-arrow-bold" className="icon" />
              </a>
          ) : null}
        </td>
      );
    } else if (column.type === '__pseudoColumn') {
      return <td {...props} className={cx(column.pristine.className, rowClassNameExpr
        ? filter(rowClassNameExpr, item.data)
        : rowClassName, 'fixed-left-last')} >
        {props.rowIndex + 1}
      </td>;
    }

    let prefix: React.ReactNode = null;

    if (
      !ignoreDrag &&
      column.isPrimary &&
      store.isNested &&
      store.draggable &&
      item.draggable
    ) {
      prefix = (
        <a
          draggable
          onDragStart={this.handleDragStart}
          className={cx('Table-dragBtn')}
        >
          <Icon icon="drag-bar" className="icon" />
        </a>
      );
    }

    // Aug第一列渲染展开按钮
    if (props.colIndex === 0 && item.expandable) {
      prefix = item.loading ? (
        <Spinner
          size="sm"
          show
          icon="reload"
          spinnerClassName={cx('LionBiTable-expandBtn')}
        />
      ) :
        <a
          className={cx(
            'LionBiTable-expandBtn',
            item.expanded ? 'is-active' : ''
          )}
          onClick={() => this.handleToggleExpanded(item)}
        >
          <Icon icon="right-arrow-bold" className="icon" />
        </a>

    }

    // Jay
    // 对于input-table-dynamic组件的原始数据做判断
    // 禁用
    const { $path } = this.props
    const isInputTableDynamic = /(input-table-dynamic)/.test($path)
    const isOrigin = item.pristine.isOrigin && isInputTableDynamic

    const value = column.name ? resolveVariable(column.name, canAccessSuperData ? item.locals : item.data) : column.value
    // if (value == undefined && column.pristine.quickEdit != undefined) {
    //   return <td></td>
    // }

    const subProps: any = {
      ...props,
      btnDisabled: store.dragging,
      data: item.locals,
      value: value,
      popOverContainer: popOverContainer || this.getPopOverContainer,
      rowSpan: item.rowSpans[column.name as string],
      quickEditFormRef: this.subFormRef,
      prefix,
      onImageEnlarge: this.handleImageEnlarge,
      canAccessSuperData,
      row: item,
      itemBadge,
      showBadge:
        !props.isHead &&
        itemBadge &&
        store.firstToggledColumnIndex === props.colIndex,
      // Jay
      disabled: isOrigin,
      biGroupIndex: column?.name?.includes(groupBy.groupIndex) && column.type === 'tpl' ? groupBy.groupIndex : undefined,
      primaryField: primaryField,
      groupByField: groupBy?.groupByField,
      treeFields: treeFields,
      levelField: levelField,
      crudColumn: column.type == 'mapping' ? column : undefined,
    };
    delete subProps.label;

    return render(
      region,
      {
        ...column.pristine,
        column: column.pristine,
        type: 'cell'
      },
      subProps
    );
  }

  componentDidUpdate(prevProps: TableProps) {
    const props = this.props;
    const store = props.store;

    if (
      anyChanged(
        [
          'selectable',
          'columnsTogglable',
          'draggable',
          'orderBy',
          'orderDir',
          'multiple',
          'footable',
          'primaryField',
          'itemCheckableOn',
          'itemDraggableOn',
          'hideCheckToggler',
          'combineNum',
          'combineFromIndex',
          'expandConfig'
        ],
        prevProps,
        props
      )
    ) {
      let combineNum = props.combineNum;
      if (typeof combineNum === 'string') {
        combineNum = parseInt(
          resolveVariableAndFilter(combineNum, props.data, '| raw'),
          10
        );
      }
      store.update({
        selectable: props.selectable,
        columnsTogglable: props.columnsTogglable,
        draggable: props.draggable,
        orderBy: props.orderBy,
        orderDir: props.orderDir,
        multiple: props.multiple,
        primaryField: props.primaryField,
        footable: props.footable,
        itemCheckableOn: props.itemCheckableOn,
        itemDraggableOn: props.itemDraggableOn,
        hideCheckToggler: props.hideCheckToggler,
        combineNum: combineNum,
        combineFromIndex: props.combineFromIndex,
        expandConfig: props.expandConfig
      });
    }

    if (prevProps.columns !== props.columns) {
      store.update({
        columns: props.columns
      });
    }
    // 数据源id更新
    if (prevProps.data.dataSetId !== props.data.dataSetId) {
      store.updateDataSetId(props.data.dataSetId)
    }

    if (
      anyChanged(['source', 'value', 'items'], prevProps, props) ||
      (!props.value &&
        !props.items &&
        (props.data !== prevProps.data ||
          (typeof props.source === 'string' && isPureVariable(props.source))))
    ) {
      this.syncRows(props, prevProps) && this.syncSelected();
    } else if (isArrayChildrenModified(prevProps.selected!, props.selected!)) {
      const prevSelectedRows = store.selectedRows
        .map(item => item.id)
        .join(',');
      store.updateSelected(props.selected || [], props.valueField);
      const selectedRows = store.selectedRows.map(item => item.id).join(',');
      prevSelectedRows !== selectedRows && this.syncSelected();
    }

    this.updateTableInfoLazy();
  }

  render() {
    const {
      className,
      store,
      classnames: cx,
      affixColumns,
      autoFillHeight = true,
      autoGenerateFilter
    } = this.props;

    this.renderedToolbars = []; // 用来记录哪些 toolbar 已经渲染了，已经渲染了就不重复渲染了。
    const heading = this.renderHeading();
    const header = this.renderHeader();
    const footer = this.renderFooter();
    const tableClassName = cx(
      'Table-table',
      store.combineNum > 0 ? 'Table-table--withCombine' : '',
      this.props.tableClassName
    );

    return (
      <div
        className={cx('Table', 'LionBiTable', className, {
          'Table--unsaved': !!store.modified || !!store.moved,
          'Table--autoFillHeight': autoFillHeight
        })}
      >
        {autoGenerateFilter ? this.renderAutoFilterForm() : null}
        {header}
        {heading}
        <div
          ref={this.tableWindowRef}
          className={cx('Table-contentWrap')}
          onMouseLeave={this.handleMouseLeave}
        >
          {this.renderTableContent()}
        </div>
        {footer}
      </div>
    );
  }

}

@Renderer({
  type: 'lion-bi-table',
  storeType: LionBiTableStore.name,
  name: 'lion-bi-table'
})
export class LionBiTableRenderer extends LionBiTable { }