import { isObject } from './verify'

export interface GroupKey<T extends Record<string, any>> {
  /** 子节点的key */
  children: string
  /** 根据那个字段分组 */
  groupKey: keyof T
  /** 组的id，默认于groupKey相同 */
  value?: keyof T
  /** 组名的key，默认于groupKey相同 */
  label: keyof T
}
export interface GroupProps { valueKey?: string, labelKey?: string, childrenKey?: string }

/**
 * 数组对象去重 (根据某个字段去重)
 * @param arr - 需要去重的数组
 * @param key - 需要根据那个字段去重
 */
export function filterRepeat<T extends Record<number | string | symbol, unknown>, K extends keyof T>(arr: T[], key: K): T[] {
  const res = new Map<unknown, 1>()
  return arr.filter(item => !res.has(item[key]) && res.set(item[key], 1))
}

/**
 * 根据key合并数组去掉重复
 * 有重复的就保留arr2的
 */
export function mergeArrFun<T extends Record<string, unknown>, K extends keyof T>(arr1: T[], arr2: T[], key: K): T[] {
  const res = new Map<unknown, T>()
  arr1.forEach((item) => {
    res.set(item[key], item)
  })
  arr2.forEach((item) => {
    res.set(item[key], item)
  })
  const arr: T[] = []

  res.forEach((val) => {
    arr.push(val)
  })
  return arr
}

/**
 * 根据路径获取对象的值
 * @param obj - 对象
 * @param path - 如 'a.c'只支持点路径,不支持 'a[0]'
 */
export function getValueByPath(obj: Record<string, unknown>, path: string): unknown {
  const paths = path.split('.')
  let res: Record<string, unknown> = obj
  let result: unknown = ''
  paths.forEach((item: string) => {
    if (item in res && typeof res[item] === 'object') {
      res = res[item] as Record<string, unknown>
    }
    else {
      result = res[item]
    }
  })
  return result
}

/**
 * 根据路径 或一个路径数组得到一个由该路径值组成的一个字符串  (多由于唯一字段)
 * @param obj - 对象
 * @param paths - 如 ['a.c','a' 'a.c.d']只支持点路径,不支持 ['a[0]']
 */
export function getOnlyValue(obj: Record<string, unknown>, paths: string[] | string): string {
  if (Array.isArray(paths)) {
    let onlyValue = ''
    paths.forEach((key) => {
      onlyValue += `_${String(getValueByPath(obj, key)) || ''}`
    })
    return onlyValue
  }
  else {
    return obj[paths] as string
  }
}

/**
 * 删除指定的元素并返回删除后的数组 [用于需要返回新数组而不是直接修改原数组的情况]
 */
export function mySplice<T>(array: T[], index: number): T[] {
  return array.slice(0, index).concat(array.slice(index + 1))
}

/**
 * 在数组后面添加一个元素并返回添加后的数组 [用于需要返回新数组而不是直接修改原数组的情况]
 */
export function myPush<T>(array: T[], item: T): T[] {
  return array.concat([item])
}

/**
 * 特定的值删除数组中的元素并返回删除后的数组 [用于需要返回新数组而不是直接修改原数组的情况]
 */
export function myDelItem<T>(array: T[], item: T): T[] {
  const index = array.indexOf(item)
  return index >= 0 ? mySplice(array, index) : array
}

/**
 * 得到两个数组的交集
 */
export function myGetIntersection<T>(arr1: T[], arr2: T[]): T[] {
  if (Array.isArray(arr1) && Array.isArray(arr2)) {
    return arr1.filter(item => arr2.includes(item))
  }
  else {
    return []
  }
}

/**
 * 判断两个数组是否有交集
 */
export function myHaveIntersection<T = unknown>(arr1: T[], arr2: T[]): boolean {
  if (Array.isArray(arr1) && Array.isArray(arr2)) {
    const index = arr1.findIndex(item => arr2.includes(item))
    return index >= 0
  }
  else {
    return false
  }
}

/**
 * 判断两个对象内容是否相等
 * @param obj1 - 需要判断的对象1
 * @param obj2 - 需要判断的对象2
 * @param notComparisonParam - notComparisonParam  不判断对象中的那些字段 [字符串数组]
 */
export function myEqual(obj1: unknown, obj2: unknown, notComparisonParam: string[] = []): boolean {
  // 判断两个对象是否指向同一内存或者全等,指向同一内存或全等返回true
  if (obj1 === obj2) {
    return true
  }
  // 类型相同才比较,不同则直接返回false
  const obj1Type = typeof obj1
  const obj2Type = typeof obj2
  // 注意 null 的typeof 也为 'object'
  if (obj1Type === obj2Type && obj1 !== null && obj2 !== null) {
    if (isObject(obj1) && isObject(obj2)) {
      const a = JSON.parse(JSON.stringify(obj1))
      const b = JSON.parse(JSON.stringify(obj2))
      notComparisonParam.forEach((item) => {
        delete a[item]
        delete b[item]
      })
      // 获取两个对象键值数组
      const aProps = Object.getOwnPropertyNames(a)
      const bProps = Object.getOwnPropertyNames(b)
      // 判断两个对象键值数组长度是否一致,不一致返回false
      if (aProps.length !== bProps.length) {
        return false
      }
      // 遍历对象的键值
      for (const prop in a) {
        // 判断a的键值,在b中是否存在,不存在,返回false
        if (Object.hasOwn(b, prop)) {
          // 判断a的键值是否为对象,是则递归,不是对象直接判断键值是否相等,不相等返回false
          if (isObject(a[prop])) {
            if (!myEqual(a[prop], b[prop])) {
              return false
            }
          }
          else if (a[prop] !== b[prop]) {
            return false
          }
        }
        else {
          return false
        }
      }
      return true
    }
    else {
      // 不是对象且上面已经判断了不全等则返回false
      return false
    }
  }
  else {
    return false
  }
}

/**
 * 把字符串转为Json对象
 */
export function myStrToJson<T = Record<string, unknown>>(obj: T | string): T | undefined {
  if (typeof obj === 'string') {
    try {
      return JSON.parse(obj) as T
      // oxlint-disable-next-line no-unused-vars
    }
    // eslint-disable-next-line unused-imports/no-unused-vars
    catch (err) {
      return undefined
    }
  }
  else {
    return undefined
  }
}

/**
 * 反转对象的键值对(不会改变原对象)
 */
export function invertObjKeyValues<T extends Record<string, string>>(obj: T) {
  const newObj: Record<string, string> = {}
  for (const key in obj) {
    const val = obj[key]
    if (val) {
      newObj[val] = key
    }
  }
  return newObj
}

/**
 * 判断两个对象内容是否相等
 * @param obj1 - 需要判断的对象1
 * @param obj2 - 需要判断的对象2
 * @param notComparisonParam - notComparisonParam  不判断对象中的那些字段 [字符串数组]
 */
export function isEqual(obj1: unknown, obj2: unknown, notComparisonParam: string[] = []): boolean {
  // 判断两个对象是否指向同一内存或者全等,指向同一内存或全等返回true
  if (obj1 === obj2) {
    return true
  }
  // 类型相同才比较,不同则直接返回false
  const obj1Type = typeof obj1
  const obj2Type = typeof obj2
  // 注意 null 的typeof 也为 'object'
  if (obj1Type === obj2Type && obj1 !== null && obj2 !== null) {
    if (isObject(obj1) && isObject(obj2)) {
      const a = JSON.parse(JSON.stringify(obj1))
      const b = JSON.parse(JSON.stringify(obj2))
      notComparisonParam.forEach((item) => {
        delete a[item]
        delete b[item]
      })
      // 获取两个对象键值数组
      const aProps = Object.getOwnPropertyNames(a)
      const bProps = Object.getOwnPropertyNames(b)
      // 判断两个对象键值数组长度是否一致,不一致返回false
      if (aProps.length !== bProps.length) {
        return false
      }
      // 遍历对象的键值
      for (const prop in a) {
        // 判断a的键值,在b中是否存在,不存在,返回false
        if (Object.hasOwn(b, prop)) {
          // 判断a的键值是否为对象,是则递归,不是对象直接判断键值是否相等,不相等返回false
          if (isObject(a[prop])) {
            if (!isEqual(a[prop], b[prop])) {
              return false
            }
          }
          else if (a[prop] !== b[prop]) {
            return false
          }
        }
        else {
          return false
        }
      }
      return true
    }
    else {
      // 不是对象且上面已经判断了不全等则返回false
      return false
    }
  }
  else {
    return false
  }
}
let my__OnlyId = 10000

/** 得到唯一id */
export function getOnlyId() {
  my__OnlyId++
  return my__OnlyId
}

/**
 * 递归分组函数
 * @param data 原始数据数组
 * @param keys 分组键数组
 * @returns 分组后的树形结构
 */
export function groupByKeys<T extends Record<number | string | symbol, any>>(data: T[], groupKeys: GroupKey<T>[]): any[] {
  // 递归分组函数
  const groupRecursively = (items: T[], keys: GroupKey<T>[]): any[] => {
    if (keys.length === 0) {
      return []
    }

    // 获取当前分组键和剩余键
    const [currentKey, ...remainingKeys] = keys
    const { groupKey, value, label, children = 'children' } = currentKey!
    const valueKey = value || groupKey
    const labelKey = label || groupKey

    // 使用 reduce 按当前键分组
    const groupedMap = items.reduce((acc: Map<any, T[]>, item) => {
      const keyValue = item[valueKey]
      if (!acc.has(keyValue)) {
        acc.set(keyValue, [])
      }
      acc.get(keyValue)!.push(item)
      return acc
    }, new Map())
    // 构建分组节点
    return Array.from(groupedMap.entries()).map(([keyValue, groupItems]) => {
      // 获取当前分组的标签值（取第一条数据的对应字段）
      const labelValue = groupItems[0]![labelKey]

      // 递归处理下一级分组
      const list: T[] = groupRecursively(groupItems, remainingKeys)
      // 构建当前节点
      return {
        [valueKey]: keyValue,
        [labelKey]: labelValue,
        [children]: list,
      }
    })
  }

  return groupRecursively(data, groupKeys)
}
