/**
 * Copyright (c) 2022 - present TinyVue Authors.
 * Copyright (c) 2022 - present Huawei Cloud Computing Technologies Co., Ltd.
 *
 * Use of this source code is governed by an MIT-style license.
 *
 * THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL,
 * BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR
 * A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS.
 *
 */

import { SORT } from '../common'
import { isSame } from '../type'
import { getObj } from '../object'

/**
 * 返回在数组中可以找到一个给定元素的第一个索引,如果不存在,则返回-1。 TINY_NO_NEED 现在数组有 findIndex
 * 修复数组原生的 indexOf 方法不能判断 NaN 的问题
 *
 *     let arr1 = [1, 2, 3, 4]
 *     let arr2 = [1, 2, NaN, 4]
 *     indexOf(arr1, 2)   // 1
 *     indexOf(arr2, NaN) // 2
 *
 * @param {Array<T>} arr 要查找的数组
 * @param {T} data  需要查找的数据
 * @param {Function} [predicate] 断言函数,缺省为 isSame, 两个参数为数组的元素和查找的数据
 * @returns {number} 元素在数组中的索引,如果不存在则返回-1
 */
export const indexOf = <T>(arr: T[], data: T, predicate: (item: T, target: T) => boolean = isSame): number => {
  if (Array.isArray(arr) && typeof predicate === 'function') {
    for (let i = 0, len = arr.length; i < len; i++) {
      if (predicate(arr[i], data)) {
        return i
      }
    }
  }

  return -1
}

/**
 * 在数组里查找对象,调用自定义的断言函数。
 *
 *     let arr = [1, 2, 3, 4]
 *     find(arr, function (value) { return value > 2 })   // 3
 *
 * @param {Array<T>} arr 要查找的数组
 * @param {Function} predicate 断言函数
 * @returns {T | undefined} 找到的元素,如果没有找到则返回undefined
 */
export const find = <T>(arr: T[], predicate: (item: T, index: number) => boolean): T | undefined => {
  const index = indexOf(arr, undefined as any, predicate as any)
  return index !== -1 ? arr[index] : undefined
}

/**
 * 从数组中删除指定元素,并返回该数组。
 *
 *     let arr1 = [1, 2, 3, 4]
 *     let arr2 = [1, 2, NaN, 4]
 *     remove(arr1, 2, 2) // [1, 4]
 *     remove(arr2, NaN)  // [1, 2, 4]
 *
 * @param {Array<T>} arr 源数组
 * @param {T} data  需要删除的数据
 * @param {number} count 删除元素个数,默认为 1
 * @returns {Array<T>} 处理后的数组
 */
export const remove = <T>(arr: T[], data: T, count = 1): T[] => {
  if (Array.isArray(arr) && arr.length) {
    const index = indexOf(arr, data)
    if (index > -1) {
      arr.splice(index, count)
    }
  }

  return arr
}

/**
 * 对象数组自定义排序,并返回该数组。
 *
 *     sort([ {a:100}, {a:1}, {a:NaN}, {a:10} ], 'a')        // [ {a:1}, {a:10}, {a:100}, {a:NaN} ]
 *     sort([ {a:100}, {a:1}, {a:NaN}, {a:10} ], 'a','desc') // [ {a:100}, {a:10}, {a:1}, {a:NaN} ]
 *
 * @param {Array<T>} arr 需要排序的对象数组
 * @param {string} field 要排序的对象字段
 * @param {string} sort 排序方向,取值为 "asc" 或 "desc"
 * @returns {Array<T>} 排好序的对象数组
 */
export const sort = <T extends Record<string, any>>(arr: T[], field: string, sort: string = SORT.Asc): T[] => {
  if (Array.isArray(arr) && arr.length > 1) {
    arr.sort((x, y) => {
      const compare = sort === SORT.Asc ? [1, -1] : [-1, 1]
      const xField = getObj(x, field)
      const yField = getObj(y, field)

      if (isNaN(xField)) {
        return sort === SORT.Asc ? 1 : -1
      } else if (isNaN(yField)) {
        return -1
      }

      return xField > yField ? compare[0] : compare[1]
    })
  }

  return arr
}

/**
 * 向数组中添加不重复的数据,并返回该数组。
 *
 *     let arr = [ 1, 2, NaN, 4]
 *     push(arr, 1)   // [ 1, 2, NaN, 4]
 *     push(arr, NaN) // [ 1, 2, NaN, 4]
 *     push(arr, 5)   // [ 1, 2, NaN, 4, 5]
 *
 * @param {Array<T>} arr 源数组
 * @param {T} data 需要增加的数据
 * @returns {Array<T>} 处理后的数组
 */
export const push = <T>(arr: T[], data: T): T[] => {
  if (Array.isArray(arr) && !arr.some((value) => isSame(value, data))) {
    arr.push(data)
  }

  return arr
}

/**
 * 去除数组中的重复的值,并返回新数组。
 *
 *     let arr = [ 1, NaN, 2, NaN, 2, 3, 4]
 *     unique(arr) // [ 1, NaN, 2, 3, 4]
 *
 * @param {Array<T>} arr 源数组
 * @returns {Array<T>} 去重后的新数组
 */
export const unique = <T>(arr: T[]): T[] => {
  if (Array.isArray(arr)) {
    const array: T[] = []

    for (let i = 0, len = arr.length; i < len; i++) {
      const value = arr[i]
      if (indexOf(array, value) === -1) {
        array.push(value)
      }
    }

    return array
  }

  return arr
}

/**
 * 将一个对象的属性复制到另一个对象
 *
 * @param {Record<string, any>} to 目标对象
 * @param {Record<string, any>} _from 源对象
 * @returns {Record<string, any>} 目标对象
 */
const extend = (to: Record<string, any>, _from: Record<string, any>): Record<string, any> => {
  Object.keys(_from).forEach((key) => (to[key] = _from[key]))

  return to
}

/**
 * 数组转对象
 *
 *     let arr = [ { key1: value1 }, { key2: value2 } ]
 *     toObject(arr) // { key1: value1, key2: value2 }
 *
 * @param {Array<Record<string, any>>} arr 包含对象的数组
 * @returns {Record<string, any>} 合并后的对象
 */
export const toObject = (arr: Record<string, any>[]): Record<string, any> => {
  const res: Record<string, any> = {}

  for (let i = 0; i < arr.length; i++) {
    if (arr[i]) {
      extend(res, arr[i])
    }
  }

  return res
}

/**
 * 将 id 与 pid 构成的扁平数据转换成 children 的树状数据
 *
 *     let data = [{ id: 100, pId: 0, label: '首页'}, { id: 101, pId: 100, label: '指南'}]
 *     transformPidToChildren(data) // [ 0: { id: 100, label: "首页", children: [ 0: { id: 101, label: "指南" } ] } ]
 *
 * @param {Array<Record<string, any>>} data id 与 pid 构成的扁平数据的数组
 * @param {string} [pidName] pid 的属性名,缺省为 pId
 * @param {string} [childrenName] children 的属性名,缺省为 children
 * @param {string} [idName] id 的属性名,缺省为 id
 * @returns {Array<Record<string, any>>} 树状结构数据
 */
export const transformPidToChildren = (
  data: Record<string, any>[],
  pidName = 'pId',
  childrenName = 'children',
  idName = 'id'
): Record<string, any>[] => {
  const result: Record<string, any>[] = []

  Array.isArray(data) &&
    data.forEach((item) => {
      if (!item[pidName] || item[pidName] === '0') {
        result.push(item)
      } else {
        const parent = find(data, (i) => i[idName] === item[pidName])

        if (!parent) {
          return
        }

        if (!parent[childrenName]) {
          parent[childrenName] = []
        }

        parent[childrenName].push(item)
      }

      delete item[pidName]
    })

  return result
}

/**
 * 将pid标识的普通数组转换树结构数据
 *
 * @param {Record<string, any> | Record<string, any>[]} data 需要转换的数据
 * @param {string} key id的属性名,默认为'id'
 * @param {string} parentKey 父id的属性名,默认为'pId'
 * @returns {Record<string, any>[]} 树状结构数据
 */
export const transformTreeData = (
  data: Record<string, any> | Record<string, any>[],
  key = 'id',
  parentKey = 'pId'
): Record<string, any>[] => {
  let dataArray: Record<string, any>[]

  if (!Array.isArray(data)) {
    dataArray = [data]
  } else {
    dataArray = data
  }

  dataArray = dataArray.map((item) => ({ ...item }))

  const treeData = transformPidToChildren(dataArray, parentKey, 'children', key)
  return treeData
}