export { isNumber, clamp } from 'lodash-es'

/**
 * Returns the remainder of division of `n` by `m`. You should use this
 * instead of the built-in operation as the built-in operation does not
 * properly handle negative numbers.
 */
export function mod(n: number, m: number) {
  return ((n % m) + m) % m
}

export function random(lower: number, upper: number) {
  if (upper == null) {
    upper = lower == null ? 1 : lower // eslint-disable-line
    lower = 0 // eslint-disable-line
  } else if (upper < lower) {
    const tmp = lower
    lower = upper // eslint-disable-line
    upper = tmp // eslint-disable-line
  }
  return Math.floor(Math.random() * (upper - lower + 1) + lower)
}

export function isPercentage(val: any): val is string {
  return typeof val === 'string' && val.slice(-1) === '%'
}

export function normalizePercentage(
  num: number | string | null | undefined,
  ref: number,
) {
  if (num == null) {
    return 0
  }

  let raw: number

  if (typeof num === 'string') {
    raw = parseFloat(num)
    if (isPercentage(num)) {
      raw /= 100
      if (Number.isFinite(raw)) {
        return raw * ref
      }
    }
  } else {
    raw = num
  }

  if (!Number.isFinite(raw)) {
    return 0
  }

  if (raw > 0 && raw < 1) {
    return raw * ref
  }

  return raw
}

export function parseCssNumeric(val: string, units?: string | string[]) {
  function getUnit(regexp: string) {
    const matches = new RegExp(`(?:\\d+(?:\\.\\d+)*)(${regexp})$`).exec(val)
    if (!matches) {
      return null
    }

    return matches[1]
  }

  const number = parseFloat(val)

  if (Number.isNaN(number)) {
    return null
  }

  // determine the unit
  let regexp: string
  if (units == null) {
    // accept any unit, as well as no unit
    regexp = '[A-Za-z]*'
  } else if (Array.isArray(units)) {
    if (units.length === 0) {
      return null
    }

    regexp = units.join('|')
  } else if (typeof units === 'string') {
    regexp = units
  }

  const unit = getUnit(regexp!)

  if (unit === null) {
    return null
  }

  return {
    unit,
    value: number,
  }
}

export type SideOptions =
  | number
  | {
      vertical?: number
      horizontal?: number
      left?: number
      top?: number
      right?: number
      bottom?: number
    }

export function normalizeSides(box?: SideOptions) {
  if (typeof box === 'object') {
    let left = 0
    let top = 0
    let right = 0
    let bottom = 0

    if (box.vertical != null && Number.isFinite(box.vertical)) {
      top = bottom = box.vertical
    }
    if (box.horizontal != null && Number.isFinite(box.horizontal)) {
      right = left = box.horizontal
    }

    if (box.left != null && Number.isFinite(box.left)) left = box.left
    if (box.top != null && Number.isFinite(box.top)) top = box.top
    if (box.right != null && Number.isFinite(box.right)) right = box.right
    if (box.bottom != null && Number.isFinite(box.bottom)) bottom = box.bottom

    return { top, right, bottom, left }
  }

  let val = 0
  if (box != null && Number.isFinite(box)) {
    val = box
  }

  return { top: val, right: val, bottom: val, left: val }
}
