import omit from 'omit.js';

export function isObject(x: unknown): x is Record<string, any> {
  return x != null && typeof x === 'object';
}

export function isString(x: unknown): x is string {
  return typeof x === 'string';
}

export function capitalize(string: string) {
  return string[0].toUpperCase() + string.slice(1);
}

export type DataIndex = string | number | (string | number)[];

export function isDataIndexEqual(first: DataIndex, second: DataIndex) {
  return JSON.stringify(first) === JSON.stringify(second);
}

export function findColumn(columns: any[], dataIndex: DataIndex) {
  return columns.find((item) => isDataIndexEqual(item.dataIndex, dataIndex));
}

export function findColumns(columns: any[], dataIndexes: DataIndex[]) {
  return dataIndexes.map((dataIndex) => findColumn(columns, dataIndex)).filter(Boolean);
}

export function findColumnIndex(columns: any[], dataIndex: DataIndex) {
  return columns.findIndex((item) => isDataIndexEqual(item.dataIndex, dataIndex));
}

export const defaultOmitProps = [
  'hideInTable',
  'hideInForm',
  'hideInDescriptions',
  'order',
  'listOrder',
];
export function cloneColumn(columns: any[], dataIndex: DataIndex, omitProps = defaultOmitProps) {
  const column = findColumn(columns, dataIndex);
  return column ? omit(column, omitProps) : undefined;
}

export function cloneColumns(columns: any[], dataIndexes: DataIndex[]) {
  return dataIndexes.map((dataIndex) => cloneColumn(columns, dataIndex)).filter(Boolean);
}

export function cloneDescriptionColumns(columns: any[]) {
  return columns
    .filter((item) => !item.hideInDescriptions)
    .map((item) => omit(item, [...defaultOmitProps, 'ellipsis'])); // ProDescriptions不支持ellipsis
}

export function insertColumns(columns: any[], beforeDataIndexes: DataIndex[], inserts: any[]) {
  beforeDataIndexes.forEach((dataIndex) => {
    const index = findColumnIndex(columns, dataIndex);
    columns.splice(index, 0, ...inserts);
  });
}

export function insertDividerColumns(columns: any[], beforeDataIndexes: DataIndex[]) {
  beforeDataIndexes.forEach((dataIndex) => {
    const index = findColumnIndex(columns, dataIndex);
    columns.splice(index, 0, {
      valueType: 'divider',
      fieldProps: { key: `divider-${index}` }, // FIXME: SchemaForm没有处理Divider的key，目前只能通过fieldProps规避unique key error
    });
  });
}

export function extendColumnRules(column: Record<string, any>, rules: any[]) {
  const { formItemProps = {} } = column;
  formItemProps.rules = [...(formItemProps.rules || []), ...rules];
  Object.assign(column, { formItemProps });
}

export function extendColumnsRules(columns: any[], dataIndexes: DataIndex[], rules: any[]) {
  findColumns(columns, dataIndexes).forEach((column) => extendColumnRules(column, rules));
}

export function mergeFieldProps(srcPros, targetProps) {
  const props = {
    fieldProps: {},
    formItemProps: {
      rules: [],
    },
  };
  Object.assign(props.fieldProps, srcPros.fieldProps || {}, targetProps.fieldProps || {});
  Object.assign(props.formItemProps, srcPros.formItemProps || {}, targetProps.formItemProps || {});
  Object.assign(
    props,
    omit(srcPros, ['fieldProps', 'formItemProps']) || {},
    omit(targetProps, ['fieldProps', 'formItemProps']) || {},
  );

  props.formItemProps.rules = srcPros?.formItemProps?.rules || [];

  const srcRequiredObj = props.formItemProps.rules.find((item) => item.hasOwnProperty('required'));
  const targetRequiredObj = targetProps?.formItemProps?.rules?.find((item) =>
    item.hasOwnProperty('required'),
  );
  if (targetRequiredObj) {
    if (srcRequiredObj && srcRequiredObj.required !== targetRequiredObj.required) {
      srcRequiredObj.required = targetRequiredObj.required;
    }
    if (!srcRequiredObj) {
      props.formItemProps.rules.push(targetRequiredObj);
    }
  }

  return props;
}

export function insertDynamicValue(attrs, value) {
  if (!isObject(attrs)) {
    return {};
  }
  Object.entries(attrs).forEach(([key, props]) => {
    if (isObject(props)) {
      return insertDynamicValue(props, value);
    }
    if (props === '$this') {
      // eslint-disable-next-line no-param-reassign
      attrs[key] = value === '' ? undefined : value;
    }
    return null;
  });
  return attrs;
}

export function filterEnums(enums, attrs) {
  if (attrs.enumIncludes && enums.valueEnum) {
    Object.entries(enums.valueEnum).forEach(([v, opt]) => {
      if (attrs.enumIncludes.includes(v)) {
        if (opt.disabled) {
          // eslint-disable-next-line no-param-reassign
          opt.disabled = false;
        }
      } else if (!opt.disabled) {
        // eslint-disable-next-line no-param-reassign
        opt.disabled = true;
      }
    });
  }
  if (attrs.enumExcludes && enums.valueEnum) {
    Object.entries(enums.valueEnum).forEach(([v, opt]) => {
      if (!attrs.enumExcludes.includes(v)) {
        if (opt.disabled) {
          // eslint-disable-next-line no-param-reassign
          opt.disabled = false;
        }
      } else if (!opt.disabled) {
        // eslint-disable-next-line no-param-reassign
        opt.disabled = true;
      }
    });
  }
  return enums;
}

export function getFileName(url: string) {
  return url.substring(url.lastIndexOf('/') + 1);
}

export function calcFixedSize(size: number) {
  if (Number.isInteger(size)) {
    return String(size);
  }
  return size.toFixed(2);
}

export function parseFileSize(unTrimStr: string | number) {
  // should be sorted by bytes desc
  const readableSizes = [
    { title: 'GB', unitBytes: 1024 * 1024 * 1024 },
    { title: 'MB', unitBytes: 1024 * 1024 },
    { title: 'KB', unitBytes: 1024 },
  ];

  const size = String(unTrimStr).trim();
  // eslint-disable-next-line no-restricted-syntax
  for (const { title, unitBytes } of readableSizes) {
    if (size.endsWith(title)) {
      const bytes = Number(size.split(title)[0]) * unitBytes;
      return { humanFormat: size, bytes };
    }
  }

  const bytes = Number(size);
  // eslint-disable-next-line no-restricted-syntax
  for (const { title, unitBytes } of readableSizes) {
    if (bytes >= unitBytes) {
      return { humanFormat: `${calcFixedSize(bytes / unitBytes)}${title}`, bytes };
    }
  }
  return { humanFormat: `${bytes}Bytes`, bytes };
}

export function mergeDeep(target: Record<string, any>, source: Record<string, any>) {
  const output = { ...target };
  if (isObject(target) && isObject(source)) {
    Object.keys(source).forEach((key) => {
      if (isObject(source[key])) {
        if (!(key in target)) Object.assign(output, { [key]: source[key] });
        else output[key] = mergeDeep(target[key], source[key]);
      } else {
        Object.assign(output, { [key]: source[key] });
      }
    });
  }
  return output;
}

export function deepClone(obj) {
  const newObj = Array.isArray(obj) ? [] : {};
  if (obj && typeof obj === 'object') {
    Object.entries(obj).forEach(([key, value]) => {
      newObj[key] = typeof value === 'object' ? deepClone(value) : value;
    });
  }
  return newObj;
}

/**
 * 根据key去重数组
 * @param arr
 * @param key
 */
export function uniqueByKey(array: Record<string | number, any>[], key: string) {
  return [...new Map(array.map((item) => [item[key], item])).values()];
}

/**
 * 合并数据，并根据uniqueKey进行去重
 * @param source any[]
 * @param dest any
 * @param uniqueKey string 去重的字段名，默认为id
 * @return any[] 合并后的数据
 */
export function mergeData(source: any[], dest: any, uniqueKey = 'id') {
  if (!dest) return source;
  const concatList = source.concat(dest);
  return uniqueByKey(concatList, uniqueKey);
}

/**
 * 根据dataIndex提取对应数据
 * @param initialValues
 * @param dataIndex
 * @returns
 */
export function parseInitialData(initialValues: any, dataIndex: DataIndex) {
  if (!initialValues) return undefined;
  if (!Array.isArray(dataIndex)) return initialValues[dataIndex];
  return dataIndex.reduce((acc, key) => {
    if (isString(key) && ['@connect', '@set'].includes(key)) return acc;
    return acc[key];
  }, initialValues);
}

/**
 * 添加initialData到column，以便在request合并初始选项
 * @param column
 * @param initialData
 */
export function appendColumnInitialData(column: any, initialData: any) {
  const { proFieldProps = {} } = column;
  Object.assign(proFieldProps, { '@initialData': initialData });
  Object.assign(column, { proFieldProps });
}

/**
 * 根据columns配置自动批量注入initialData到column，以便在request合并初始选项
 * @param columns
 * @param initialValues
 */
export function appendColumnsInitialData(columns: any[], initialValues: any) {
  columns.forEach((column) => {
    if (Array.isArray(column.columns)) {
      appendColumnsInitialData(column.columns, initialValues);
    } else if (typeof column.request === 'function') {
      const initialData = parseInitialData(initialValues, column.dataIndex);
      appendColumnInitialData(column, initialData);
    } else if (
      column.valueType === 'dependency' &&
      column._columns?.length === 1 &&
      typeof column.columns === 'function'
    ) {
      // 兼容editFormFields配置，假定每个dependency只返回最多一个column且与_column字段一致
      const initialData = parseInitialData(initialValues, column._columns[0].dataIndex);
      Object.assign(column.columns, { '@initialData': initialData });
    }
  });
}