/* eslint-disable no-restricted-globals,no-param-reassign,guard-for-in,no-restricted-syntax,no-continue */
const mod = {}
module.exports = mod

/**
 * check value is number
 * @param {any} v input value
 * @return {boolean}
 */
mod.isNumber = v => !(!isFinite(v) || v === true || v === false || v === null || v === '')

/**
 * value to number
 * @param {object} v source value
 * @param {number=} defVal default value
 * @param {number=} min range min
 * @param {number=} max range max
 * @return {number}
 */
mod.toNumber = (v, defVal, min, max) => {
  let defaultVal = defVal
  let inVal = v
  if (!mod.isNumber(defVal)) defVal = 0
  if (typeof defVal === 'string') defaultVal = parseFloat(defVal)
  const minVal = !mod.isNumber(min) ? null : typeof min === 'string' ? parseFloat(min) : min; // eslint-disable-line
  const maxVal = !mod.isNumber(max) ? null : typeof max === 'string' ? parseFloat(max) : max; // eslint-disable-line

  if (!mod.isNumber(v)) return defaultVal
  if (typeof v === 'string') inVal = parseFloat(v)
  if (minVal !== null && inVal < minVal) inVal = min
  if (maxVal !== null && inVal > maxVal) inVal = max

  return inVal
}

/**
 * @exports
 * @typedef pageObject
 * @prop {number} total page number
 * @prop {number} page number
 * @prop {number} count all count number
 * @prop {number} limit query limit item
 * @prop {number} offset query offset number
 *
 * calc page object
 * @param {number} argCount all count
 * @param {number} argPage page number
 * @param {number} argMaxItem per page item show
 * @return {pageObject}
 */
mod.calcPage = (argCount, argPage, argMaxItem = 10) => {
  const count = mod.toNumber(argCount, 0, 0)
  let page = mod.toNumber(argPage, 1, 1)
  const maxItem = mod.toNumber(argMaxItem, 10, 1)

  let total = Math.ceil(count / maxItem)
  if (total < 1) total = 1
  if (page > total) page = total
  let offset = (page - 1) * maxItem
  if (offset > count) offset = count
  const limit = maxItem

  const pageObject = { total, page, count, offset, limit }
  for (const key in pageObject) {
    pageObject[key] = mod.toNumber(pageObject[key])
  }

  return pageObject
}

/**
 * deep copy object
 * @param {any} src
 * @return {any}
 */
mod.copyObject = src => {
  if (typeof src !== 'object') return src

  const isArray = Array.isArray(src)
  const copy = isArray ? [] : {}
  for (let it in src) {
    // @ts-ignore
    if (isArray) it = parseInt(it, 10)
    if (typeof src[it] !== 'object') copy[it] = src[it]
    else copy[it] = mod.copyObject(src[it])
  }
  return copy
}

/**
 * @typedef {(string|number)} SelectObjectAlias
 * @description pass string and length > 0 rename key to alias, other keep key name
 */
/**
 * @exports
 * @typedef {{[key: string]: SelectObjectAlias }} SelectObjectParam
 */

/**
 * select object keys
 * @param {Object<string, any>} obj
 * @param {SelectObjectParam} param
 */
mod.selectObject = (obj, param) => {
  if (typeof obj !== 'object' || typeof param !== 'object') throw new Error('input arg wrong')

  let newObj = {}

  for (const key in param) {
    const strs = key.split('.')
    const alias = param[key]
    if (strs.length > 1) {
      if (!(strs[0] in obj)) continue

      newObj = { ...newObj, ...mod.selectObject(obj[strs[0]], { [strs.slice(1).join('.')]: alias }) }
    }
    const toAlias = param[key] && typeof param[key] === 'string'
    if (!(key in obj)) continue

    newObj[toAlias ? alias : key] = obj[key]
  }

  return newObj
}
