import _ from 'lodash'
import Vue from 'vue'

// v1.5

/**
 *
 * @param {*} arr (Array) The array to process.
 * @param {*} value The value to knock.
 * @param {*} f The value to first.
 * @returns (Array) Mutates the existing array, removes duplicates as well
 */
function knock (arr, value, f = false) {
  const cloneArr = _.cloneDeep(arr)
  if (cloneArr.length === _.pull(cloneArr, value).length) {
    if (f) {
      cloneArr.unshift(value)
    } else {
      cloneArr.push(value)
    }
  }
  return cloneArr
}

/**
 *
 * @param {*} arr (Array) The array to process.
 * @param {*} value The value to knock.
 * @param {*} comparator The comparator invoked per element.
 * @returns (Array) Mutates the existing array, removes duplicates as well
 */
function knockAllWith (arr, value, comparator) {
  const cloneArr = _.cloneDeep(arr)
  if (cloneArr.length === _.pullAllWith(cloneArr, [value], comparator).length) {
    cloneArr.push(value)
  }
  return cloneArr
}

/**
 *
 * @param {*} arr (Array) The array to process.
 * @param {*} value The value to search.
 * @param {*} path (Array|string): The path of the property to set.
 * @returns (Array) Returns the new filtered array.
 */
function search (arr, value = '', path = null) {
  return arr.filter((obj) => _.includes(
    _.lowerCase(
      _.get(obj, path, obj)
    ),
    _.lowerCase(value)
  ))
}

/**
 *
 * @param {*} prefixStr (string) The value to prefix.
 * @param {*} curr (object) The object to process.
 * @param {*} pick (array) The array to pick.
 * @returns (Array) Returns the new object.
 */

function prefix (prefixStr = '', curr, pick = []) {
  const object = {}
  const item = pick.length ? _.pick(curr, pick) : curr
  _.forEach(item, (value, key) => {
    const newKey = prefixStr + _.upperFirst(key)
    object[newKey] = value
  })
  return object
}

/**
 *
 * @param {*} str (string) The value to pascal.
 * @returns (string) Returns string pascal case.
 */

function pascalCase (str) {
  return _.startCase(_.camelCase(str)).replace(/ /g, '')
}

/**
 *
 * @param {*} str (string) The value to url.
 * @returns (string) Returns boolean.
 */

function isURL (str) {
  const pattern = new RegExp('^(https?:\\/\\/)?' // protocol
      + '((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|' // domain name
      + '((\\d{1,3}\\.){3}\\d{1,3}))' // OR ip (v4) address
      + '(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*' // port and path
      + '(\\?[;&a-z\\d%_.~+=-]*)?' // query string
      + '(\\#[-a-z\\d_]*)?$', 'g') // fragment locator
  return !!pattern.test(str)
}

/**
 *
 * @param {*} str (string) The value to url.
 * @returns (string) Returns boolean.
 */

function isHTTP (str) {
  return /^https?:\/\//i.test(str)
}

/**
 *
 * @param {*} str (string) The value to email.
 * @returns (string) Returns boolean.
 */

function isEmail (str) {
  return /.+@.+\..+/.test(str)
}

/**
 *
 * @param {*} str (string) The value to console.
 */

function log (str) {
  console.log('Info:', str)
}

/**
 *
 * @param {*} arr (Array) The array to process.
 * @param {*} index The index to pos.
 * @param {*} item The object to insert.
 * @returns (string)  Returns the array.
 */
function insert (arr, index, item) {
  arr.splice(index, 0, item)
  return arr
}

/**
 *
 * @param {*} str (Array) The value to process.
 * @returns (string)  Returns boolean.
 */
function hasValue (value) {
  return !_.isUndefined(value) && !_.isNull(value) && !_.isNaN(value)
}

function interpolate (str, args) {
  return str.replace(/\$(\d{1,2})/g, (match, index) => args[index] || '')
}

/**
 *
 * @param {*} str (string) The value to singularize.
 * @returns (string) Returns singularize.
 */

function singularize (word) {
  const rules = [
    [/s$/i, ''],
    [/(ss)$/i, '$1'],
    [/(wi|kni|(?:after|half|high|low|mid|non|night|[^\w]|^)li)ves$/i, '$1fe'],
    [/(ar|(?:wo|[ae])l|[eo][ao])ves$/i, '$1f'],
    [/ies$/i, 'y'],
    [/(dg|ss|ois|lk|ok|wn|mb|th|ch|ec|oal|is|ck|ix|sser|ts|wb)ies$/i, '$1ie'],
    [/\b(l|(?:neck|cross|hog|aun)?t|coll|faer|food|gen|goon|group|hipp|junk|vegg|(?:pork)?p|charl|calor|cut)ies$/i, '$1ie'],
    [/\b(mon|smil)ies$/i, '$1ey'],
    [/\b((?:tit)?m|l)ice$/i, '$1ouse'],
    [/(seraph|cherub)im$/i, '$1'],
    [/(x|ch|ss|sh|zz|tto|go|cho|alias|[^aou]us|t[lm]as|gas|(?:her|at|gr)o|[aeiou]ris)(?:es)?$/i, '$1'],
    [/(analy|diagno|parenthe|progno|synop|the|empha|cri|ne)(?:sis|ses)$/i, '$1sis'],
    [/(movie|twelve|abuse|e[mn]u)s$/i, '$1'],
    [/(test)(?:is|es)$/i, '$1is'],
    [/(alumn|syllab|vir|radi|nucle|fung|cact|stimul|termin|bacill|foc|uter|loc|strat)(?:us|i)$/i, '$1us'],
    [/(agend|addend|millenni|dat|extrem|bacteri|desiderat|strat|candelabr|errat|ov|symposi|curricul|quor)a$/i, '$1um'],
    [/(apheli|hyperbat|periheli|asyndet|noumen|phenomen|criteri|organ|prolegomen|hedr|automat)a$/i, '$1on'],
    [/(alumn|alg|vertebr)ae$/i, '$1a'],
    [/(cod|mur|sil|vert|ind)ices$/i, '$1ex'],
    [/(matr|append)ices$/i, '$1ix'],
    [/(pe)(rson|ople)$/i, '$1rson'],
    [/(child)ren$/i, '$1'],
    [/(eau)x?$/i, '$1'],
    [/men$/i, 'man']
  ]
  let len = rules.length
  while (len--) {
    const rule = rules[len]
    if (rule[0].test(word)) {
      // eslint-disable-next-line prefer-rest-params
      return word.replace(rule[0], () => interpolate(rule[1], arguments))
    }
  }
  return word
}

/**
 *
 * @param {*} str (String) The value to process.
 * @returns (boolean)  Returns boolean.
 */
function isArabic (str) {
  const regex = new RegExp('^[0-9]+$')
  return regex.test(str)
}

_.mixin({
  knock,
  knockAllWith,
  search,
  prefix,
  pascalCase,
  isURL,
  isHTTP,
  isEmail,
  log,
  insert,
  hasValue,
  interpolate,
  singularize,
  isArabic
})

Vue.prototype.$_ = _

export default _
