// 对象相关处理
const clearNullProps = (obj: any) => {
  for (const key in obj) {
    if (obj[key] === null || obj[key] === undefined) {
      delete obj[key]
    }
  }
  return obj
}

// 计算父节点和子节点的总数
const getNodesSize = (nodes: any[], childrenKey = 'children') => {
  let size = 0
  for (const node of nodes) {
    size += 1
    if (node[childrenKey]?.length > 0) {
      size += getNodesSize(node.children, childrenKey)
    }
  }
  return size
}

// 只赋值dst中存在的属性
const assignExits = (dst: any, src: any) => {
  for (const key in dst) {
    dst[key] = src[key]
  }
}

// 只提取指定的属性
const filterKeys = (obj: any, keys: string[]) => {
  const newObj: any = {}
  for (const key of keys) {
    newObj[key] = obj[key]
  }
  return newObj
}

// 是否没有值
const isNullOrUndefined = (obj: any) => {
  return obj === null || obj === undefined
}

const isEqualIgnoreNumberString = (obj1: any, obj2: any) => {
  return isEqual(obj1, obj2, { ignoreNumberString: true })
}

const isEqual = (
  obj1: any,
  obj2: any,
  options?: {
    ignoreNumberString?: boolean
  },
) => {
  if (obj1 === obj2) {
    return true
  }

  // 忽略数字、字符串的比较
  if (
    options?.ignoreNumberString &&
    (typeof obj1 === 'number' || typeof obj1 === 'string') &&
    (typeof obj2 === 'number' || typeof obj2 === 'string')
  ) {
    return String(obj1) === String(obj2)
  }

  if (typeof obj1 !== 'object' || obj1 === null || typeof obj2 !== 'object' || obj2 === null) {
    return false
  }

  const keys1 = Object.keys(obj1)
  const keys2 = Object.keys(obj2)

  if (keys1.length !== keys2.length) return false

  for (const key of keys1) {
    if (!keys2.includes(key)) return false
    if (!isEqual(obj1[key], obj2[key], options)) {
      return false
    }
  }
  return true
}

const isKeyValueObject = (obj: any) => {
  return (
    obj !== null &&
    typeof obj === 'object' &&
    !Array.isArray(obj) &&
    !(obj instanceof Date) &&
    !(obj instanceof RegExp) &&
    !(obj instanceof Map) &&
    !(obj instanceof Set)
  )
}

const coverNullProps = (dst: any, src: any) => {
  return cover(dst, src, {
    onlyNullProps: true,
  })
}

function moveElement<T>(arr: T[], fromIndex: number, toIndex: number): T[] {
  // 处理边界情况
  if (fromIndex < 0 || fromIndex >= arr.length || toIndex < 0 || toIndex >= arr.length) {
    return arr
  }

  // 移除fromIndex位置的元素
  const [element] = arr.splice(fromIndex, 1)
  // 插入到toIndex位置
  arr.splice(toIndex, 0, element)

  return arr
}

// 覆盖属性
const cover = (
  dst: any,
  src: any,
  options?: {
    onlyNullProps?: boolean
  },
) => {
  if (!isKeyValueObject(dst) || !isKeyValueObject(src)) {
    return dst
  }
  const onlyNull = options?.onlyNullProps
  // 合并所有的key
  const keys = Array.from(new Set([...Object.keys(dst), ...Object.keys(src)]))
  for (const key of keys) {
    // 跳过
    if (!(key in src)) continue
    // 来源值
    const srcVal = src[key]
    // 目标值
    const dstVal = dst[key]
    if (onlyNull && dstVal !== undefined && dstVal !== null) {
      continue
    }
    if (Array.isArray(srcVal)) {
      // 如果是数组
      if (Array.isArray(dstVal)) {
        dst[key] = [...dstVal, ...srcVal]
      } else {
        dst[key] = [...srcVal]
      }
    } else if (isKeyValueObject(srcVal) && isKeyValueObject(dstVal)) {
      // 如果是对象类型
      dst[key] = cover(dstVal, srcVal)
    } else {
      // 如果是非对象类型
      dst[key] = srcVal
    }
  }
  return dst
}

const getAllMethods = (obj: any): Record<string, any> => {
  const methods: Record<string, any> = {}
  let current = obj

  while (current && current !== Object.prototype) {
    const proto = Object.getPrototypeOf(current)
    Object.getOwnPropertyNames(proto)
      .filter(name => name !== 'constructor' && typeof proto[name] === 'function')
      .forEach(name => {
        // 这个判断很重要，优先返回自己的属性，不希望被父类的属性覆盖
        if (methods[name]) return
        methods[name] = proto[name]
      })
    current = proto
  }

  return methods
}

const snowflakeId = (workerId = 1): string => {
  // 自定义起始时间
  const timestamp = BigInt(Date.now())
  const workerBits = BigInt(workerId) << 12n
  const sequenceBits = BigInt(Math.floor(Math.random() * 4096))
  return String((timestamp << 22n) | workerBits | sequenceBits)
}

const list2map = (list: any[], key: string, value?: string): any => {
  const map: Record<string, any> = {}
  for (const item of list) {
    map[item[key]] = value ? item[value] : item
  }
  return map
}

const options2map = (list: any[]): any => {
  return list2map(list, 'value', 'label')
}

const map2options = (map: Record<number, string>) => {
  // 将map转换成options
  const arr = []
  for (const value in map) {
    arr.push({
      value,
      label: map[value],
    })
  }
  return arr
}

const runTabTask = async <T>(task: Promise<T>, passFn: any) => {
  return new Promise<T>((resolve, reject) => {
    task
      .then((res: T) => {
        if (passFn()) {
          resolve(res)
        }
      })
      .catch((err: any) => {
        if (passFn()) {
          reject(err)
        }
      })
  })
}

export default {
  clearNullProps,
  getNodesSize,
  assignExits,
  filterKeys,
  isEqual,
  isEqualIgnoreNumberString,
  cover,
  map2options,
  list2map,
  coverNullProps,
  isKeyValueObject,
  isNullOrUndefined,
  getAllMethods,
  snowflakeId,
  options2map,
  moveElement,
  runTabTask,
}
