import { isArrayLike } from '@/common/utils/objects.ts'

/*
 集合、数组工具
 */


/**
 * 检查数组、键值对是否为 null 或长度为 0
 * @param value
 */
export function isEmpty(value: any) {
  if (value == null) {
    return true
  } else if (typeof value === 'object') {
    if (Array.isArray(value) || isArrayLike(value)) {
      return value.length === 0
    } else if (value instanceof Map || value instanceof Set) {
      return value.size === 0
    } else {
      return Object.keys(value).length === 0
    }
  } else {
    return false
  }
}

/**
 * 交集
 *
 * @param setA A 集合
 * @param setB B 集合
 * @return 返回 A、B 两个集合共有的元素
 */
export function intersection<T>(setA?: T[] | null, setB?: T[] | null) {
  if (isEmpty(setA) || isEmpty(setB)) return [] as T[]

  return setA!.filter(it => setB!.includes(it))
}

/**
 * 相对补集
 *
 * @param setA A 集合
 * @param setB B 集合
 * @return 返回属于 A 但不属于 B 的元素
 */
export function complement<T>(setA?: T[] | null, setB?: T[] | null) {
  if (isEmpty(setA)) return [] as T[]
  if (isEmpty(setB)) return setA!

  return setA!.filter(it => !setB!.includes(it))
}

/**
 * 并集
 *
 * @param setA A 集合
 * @param setB B 集合
 * @return 返回属于 A 或属于 B 的元素
 */
export function union<T>(setA?: T[] | null, setB?: T[] | null) {
  if (isEmpty(setA)) {
    if (isEmpty(setB)) return [] as T[]
    else return setB
  }
  if (isEmpty(setB)) return setA

  return [ ...setA!, ...setB! ]
}

/**
 * 对称差
 *
 * @param setA A 集合
 * @param setB B 集合
 * @return 返回非 A、B 两个集合共有的元素
 */
export function symmetricDiff<T>(setA: T[], setB: T[]) {
  if (isEmpty(setA)) {
    if (isEmpty(setB)) return [] as T[]
    else return setB
  }
  if (isEmpty(setB)) return setA

  return [ ...setA!, ...setB! ].filter(it => {
    return !(setA.includes(it) && setB.includes(it))
  })
}

export function distinct<T>(arr?: T[]) {
  return [ ...new Set(arr) ]
}

/**
 * 按指定大小分割数组
 *
 * @param arr 原数组
 * @param size 每份大小
 */
export function chunks<T>(arr: T[], size: number) {
  if (size <= 0) {
    throw new Error('块的大小必须大于0')
  }

  let temp: T[][] = []

  for (let j = 0; j < Math.ceil(arr.length / size); j++) {
    temp.push(arr.splice(j, size))
  }

  return temp
}

/**
 * 分组
 *
 * @param arr 待分组数组
 * @param keyFn 键生成函数（默认为索引）
 * @param valFn 值生成函数（默认为原始数组中的值）
 * @return 键--值
 */
export function keyObj<T, R = T>(arr?: T[] | null,
                                 keyFn?: (it: T, index: number) => string,
                                 valFn?: (it: T, index: number) => R) {
  if (isEmpty(arr)) {
    return {}
  }

  return arr!.reduce((previousValue, currentValue, currentIndex, _) => {
    let key = keyFn == null ? currentIndex : keyFn(currentValue, currentIndex)

    if (previousValue[key] !== undefined) {
      throw new Error(`有重复 KEY [${key}]`)
    }
    previousValue[key] = ((valFn == null ? currentValue : valFn(currentValue, currentIndex)) ?? null) as R

    return previousValue
  }, {} as { [k: string | number]: R })
}

/**
 * 分组
 *
 * @param arr 待分组数组
 * @param keyFn 键生成函数（默认为索引）
 * @param valFn 值生成函数（默认为原始数组中的值）
 * @return 键--值数组
 */
export function keyArr<T, V = T>(arr?: T[] | null,
                                 keyFn?: (it: T, index: number) => string,
                                 valFn?: (it: T, index: number) => V) {
  if (isEmpty(arr)) {
    return {}
  }

  return arr!.reduce((previousValue, currentValue, currentIndex, _) => {
    let key = keyFn == null ? currentIndex + '' : keyFn(currentValue, currentIndex)

    if (previousValue[key] == null) {
      previousValue[key] = []
    }

    previousValue[key].push(((valFn == null ? currentValue : valFn(currentValue, currentIndex)) ?? null) as V)

    return previousValue
  }, {} as { [k: string]: V[] })
}


/**
 * 构建树形数组
 *
 * @param arr 待处理数据（同一个值中的 id 和 pid 不能相同）
 * @param idFn id 获取函数
 * @param pidFn pid 获取函数
 * @param rootId 根节点 Id（默认字符串 0）
 */
export function toTree<N extends G.TreeNode>(arr: N[],
                                             idFn: (it: N) => string = it => it.id,
                                             pidFn: (it: N) => string = it => it.pid,
                                             rootId: string = '0') {
  let pid_vals_map = keyArr(arr, pidFn)

  for (let n of arr) {
    let treeNodes = pid_vals_map[idFn(n)]
    if (treeNodes != null && treeNodes.length > 0) {
      n.children = treeNodes
    }
  }

  let rootNodes: N[]
  let rootNode = arr.find(it => idFn(it) == rootId)
  if (rootNode == null) {
    rootNodes = arr.filter(it => pidFn(it) == rootId)
  } else {
    rootNodes = [ rootNode ]
  }

  return rootNodes
}

export default {
  isEmpty,
  complement,
  intersection,
  symmetricDiff,
  union,
  distinct,
  chunks,
  keyObj,
  keyArr,
  toTree,
}


