import type { ComputedRef, Ref } from 'vue';

import type { PaginationProps } from '../types/pagination';
import type { BasicColumn, BasicTableProps, CellFormat, GetColumnsParams } from '../types/table';

import { computed, h, reactive, ref, toRaw, unref, watch } from 'vue';

import { usePermission } from '@jnpf/hooks';
import { BasicHelp } from '@jnpf/ui';
import { formatToDate, isArray, isBoolean, isFunction, isMap, isString } from '@jnpf/utils';

import { $t } from '@vben/locales';

import { cloneDeep, isEqual } from 'lodash-es';

import { ACTION_COLUMN_FLAG, DEFAULT_ALIGN, EXPAND_COLUMN_FLAG, INDEX_COLUMN_FLAG } from '../const';

function handleItem(item: BasicColumn, ellipsis: boolean) {
  const { dataIndex, key, children } = item;
  item.align = item.align || DEFAULT_ALIGN;
  if (!key) {
    item.key = dataIndex as string;
  }
  if (ellipsis && !isBoolean(item.ellipsis)) {
    Object.assign(item, {
      ellipsis,
    });
  }
  if (children && children.length > 0) {
    handleChildren(children, !!ellipsis);
  }
}

function handleChildren(children: BasicColumn[] | undefined, ellipsis: boolean) {
  if (!children) return;
  children.forEach((item) => {
    const { children } = item;
    handleItem(item, ellipsis);
    handleChildren(children, ellipsis);
  });
}

function handleIndexColumn(propsRef: ComputedRef<BasicTableProps>, getPaginationRef: ComputedRef<boolean | PaginationProps>, columns: BasicColumn[]) {
  const { indexColumnProps, showIndexColumn } = unref(propsRef);

  let pushIndexColumns = false;
  columns.forEach(() => {
    const indIndex = columns.findIndex((column) => column.flag === INDEX_COLUMN_FLAG);
    if (showIndexColumn) {
      pushIndexColumns = indIndex === -1;
    } else if (!showIndexColumn && indIndex !== -1) {
      columns.splice(indIndex, 1);
    }
  });
  if (columns.length === 0) pushIndexColumns = true;

  if (!pushIndexColumns) return;

  const isFixedLeft = columns.some((item) => item.fixed === 'left');

  columns.unshift({
    align: 'center',
    flag: INDEX_COLUMN_FLAG,
    title: $t('component.table.index'),
    type: INDEX_COLUMN_FLAG,
    width: 50,
    ...(isFixedLeft ? { fixed: 'left' } : {}),
    ...indexColumnProps,
  });
}
function handleSelectionColumn(propsRef: ComputedRef<BasicTableProps>, columns: BasicColumn[]) {
  const { rowSelection } = unref(propsRef);
  if (!rowSelection) return;
  const isFixedLeft = columns.some((item) => item.fixed === 'left');
  columns.unshift({
    align: 'center',
    flag: rowSelection.type,
    title: '',
    type: rowSelection.type,
    width: 50,
    ...(isFixedLeft ? { fixed: 'left' } : {}),
    slots: rowSelection.type === 'checkbox' ? { checkbox: 'checkboxDefault', header: 'checkboxHeader' } : { radio: 'radioDefault' },
  });
}
function handleExpandColumn(propsRef: ComputedRef<BasicTableProps>, columns: BasicColumn[]) {
  const { showExpandColumn } = unref(propsRef);
  if (!showExpandColumn) return;
  const isFixedLeft = columns.some((item) => item.fixed === 'left');
  columns.unshift({
    align: 'center',
    flag: EXPAND_COLUMN_FLAG,
    title: '',
    type: EXPAND_COLUMN_FLAG,
    width: 50,
    ...(isFixedLeft ? { fixed: 'left' } : {}),
    slots: { content: 'expandedRowRender' },
  });
}
function handleActionColumn(propsRef: ComputedRef<BasicTableProps>, columns: BasicColumn[]) {
  const { actionColumn } = unref(propsRef);
  if (!actionColumn) return;

  const hasIndex = columns.findIndex((column) => column.flag === ACTION_COLUMN_FLAG);
  if (hasIndex === -1) {
    columns.push({
      ...columns[hasIndex],
      fixed: 'right',
      ...actionColumn,
      flag: ACTION_COLUMN_FLAG,
      slots: { default: ACTION_COLUMN_FLAG },
    });
  }
}

export function useColumns(propsRef: ComputedRef<BasicTableProps>, getPaginationRef: ComputedRef<boolean | PaginationProps>) {
  const columnsRef = ref(unref(propsRef).columns) as unknown as Ref<BasicColumn[]>;
  let cacheColumns = unref(propsRef).columns;

  const getColumnsRef = computed(() => {
    const columns = cloneDeep(unref(columnsRef)).filter((o) => o.type !== EXPAND_COLUMN_FLAG);
    if (unref(propsRef).isTreeTable && columns[0]) columns[0].treeNode = true;
    handleIndexColumn(propsRef, getPaginationRef, columns);
    handleSelectionColumn(propsRef, columns);
    handleExpandColumn(propsRef, columns);
    handleActionColumn(propsRef, columns);
    if (!columns) return [];
    return columns;
  });

  function isIfShow(column: BasicColumn): boolean {
    const ifShow = column.ifShow;

    let isIfShow = true;

    if (isBoolean(ifShow)) {
      isIfShow = ifShow;
    }
    if (isFunction(ifShow)) {
      isIfShow = ifShow(column);
    }
    return isIfShow;
  }
  const { hasColumnP } = usePermission();

  const getViewColumns = computed(() => {
    const viewColumns = sortFixedColumn(unref(getColumnsRef));
    const mapFn = (column) => {
      const { customRender, dataIndex, field, format, slots } = column;
      if (field) column.dataIndex = field;
      if (!field && dataIndex) column.field = dataIndex;
      if ((!slots || !slots?.default) && (customRender || format) && column.flag != INDEX_COLUMN_FLAG) {
        column.slots = {
          default: (params) => {
            params.record = params.row;
            if (customRender) return customRender(params);
            const { record, rowIndex } = params;
            return formatCell(record[column.field], format, record, rowIndex);
          },
          ...slots,
        };
      }
      if (column.helpMessage) {
        column.slots = {
          ...column.slots,
          header: () => {
            const help = h(BasicHelp, { text: column.helpMessage });
            return h('span', {}, [column.title, help]);
          },
        };
      }

      return reactive(column);
    };
    const columns: any = cloneDeep(viewColumns);
    // 当序号在最后一个时，序号的宽度增加
    if (columns.length > 0 && columns.length === 1 && columns[columns.length - 1].flag === INDEX_COLUMN_FLAG) {
      columns[columns.length - 1].width = 54;
    }

    return columns
      .filter((column) => hasColumnP(column.auth) && isIfShow(column))
      .map((column) => {
        // Support table multiple header
        if (column.children?.length) {
          column.children = column.children.map((e) => mapFn(e));
        }
        return mapFn(column);
      });
  });

  watch(
    () => unref(propsRef).columns,
    (columns) => {
      columnsRef.value = columns;
      cacheColumns = columns?.filter((item) => !item.type) ?? [];
    },
  );

  function setCacheColumnsByField(dataIndex: string | undefined, value: Partial<BasicColumn>) {
    if (!dataIndex || !value) {
      return;
    }
    cacheColumns.forEach((item) => {
      if (item.dataIndex === dataIndex) {
        Object.assign(item, value);
      }
    });
  }
  /**
   * set columns
   * @param columnList key｜column
   */
  function setColumns(columnList: (string | string[])[] | Partial<BasicColumn>[]) {
    const columns = cloneDeep(columnList);
    if (!isArray(columns)) return;

    if (columns.length <= 0) {
      columnsRef.value = [];
      return;
    }
    const firstColumn = columns[0];

    const cacheKeys = cacheColumns.map((item) => item.dataIndex);

    if (!isString(firstColumn) && !isArray(firstColumn)) {
      columnsRef.value = columns as BasicColumn[];
    } else {
      const columnKeys = (columns as (string | string[])[]).map((m) => m.toString());
      const newColumns: BasicColumn[] = [];
      cacheColumns.forEach((item) => {
        newColumns.push({
          ...item,
          defaultHidden: !columnKeys.includes(item.dataIndex?.toString() || (item.key as string)),
        });
      });
      // Sort according to another array
      if (!isEqual(cacheKeys, columns)) {
        newColumns.sort((prev, next) => {
          return columnKeys.indexOf(prev.dataIndex?.toString() as string) - columnKeys.indexOf(next.dataIndex?.toString() as string);
        });
      }
      columnsRef.value = newColumns;
    }
  }

  function getColumns(opt?: GetColumnsParams) {
    const { ignoreAction, ignoreAllType, ignoreIndex, sort } = opt || {};
    let columns = toRaw(unref(getColumnsRef));

    if (ignoreAllType) {
      columns = columns.filter((item) => !item.flag && !item.type);
    } else {
      if (ignoreIndex) {
        columns = columns.filter((item) => item.flag !== INDEX_COLUMN_FLAG);
      }
      if (ignoreAction) {
        columns = columns.filter((item) => item.flag !== ACTION_COLUMN_FLAG);
      }
    }

    if (sort) {
      columns = sortFixedColumn(columns);
    }

    return columns;
  }
  function getCacheColumns() {
    return cacheColumns;
  }
  function setCacheColumns(columns: BasicColumn[]) {
    if (!isArray(columns)) return;
    cacheColumns = columns.filter((item) => !item.flag);
  }

  return {
    getCacheColumns,
    getColumns,
    getColumnsRef,
    getViewColumns,
    setCacheColumns,
    setCacheColumnsByField,
    setColumns,
  };
}

function sortFixedColumn(columns: BasicColumn[]) {
  const fixedLeftColumns: BasicColumn[] = [];
  const fixedRightColumns: BasicColumn[] = [];
  const defColumns: BasicColumn[] = [];
  for (const column of columns) {
    if (column.fixed === 'left') {
      fixedLeftColumns.push(column);
      continue;
    }
    if (column.fixed === 'right') {
      fixedRightColumns.push(column);
      continue;
    }
    defColumns.push(column);
  }
  return [...fixedLeftColumns, ...defColumns, ...fixedRightColumns].filter((item) => !item.defaultHidden).map((o) => ({ ...o, visible: !o.defaultHidden }));
}

// format cell
export function formatCell(text: string, format: CellFormat, record: Recordable, index: number) {
  if (!format) {
    return text;
  }

  // custom function
  if (isFunction(format)) {
    return format(text, record, index);
  }

  try {
    // date type
    const DATE_FORMAT_PREFIX = 'date|';
    if (isString(format) && format.startsWith(DATE_FORMAT_PREFIX) && text) {
      const dateFormat = format.replace(DATE_FORMAT_PREFIX, '');

      if (!dateFormat) {
        return text;
      }
      return formatToDate(text, dateFormat);
    }

    // Map
    if (isMap(format)) {
      return format.get(text);
    }
  } catch {
    return text;
  }
}
