const DEFAULT_MAGNITUDE_STEPS = [
  { min: 1000, decimals: 2 },
  { min: 1, decimals: 4 },
  { min: 0.001, decimals: 6 },
  { min: 0, decimals: 9 }
]

function getOption(options, index) {
  return options[Number(index)] || options[0]
}

function normalizeIndex(index, options, fallback = 0) {
  const numericIndex = Number(index)
  if (!Number.isInteger(numericIndex) || numericIndex < 0 || numericIndex >= options.length) return fallback

  return numericIndex
}

function parseLooseNumber(value, allowNegative = true) {
  const text = String(value === undefined || value === null ? '' : value).trim().replace(/,/g, '.')
  if (!text) return null

  const numberValue = Number(text)
  if (!Number.isFinite(numberValue)) return NaN
  if (!allowNegative && numberValue < 0) return NaN

  return numberValue
}

function parsePositiveNumber(value) {
  const numberValue = parseLooseNumber(value, true)
  if (numberValue === null) return null

  return Number.isFinite(numberValue) && numberValue > 0 ? numberValue : NaN
}

function trimFixed(text) {
  return text.replace(/(\.\d*?)0+$/, '$1').replace(/\.$/, '')
}

function formatMagnitudeNumber(value, options = {}) {
  const {
    fallbackText = '--',
    steps = DEFAULT_MAGNITUDE_STEPS,
    zeroText = '0'
  } = options
  const numberValue = Number(value)
  if (!Number.isFinite(numberValue)) return fallbackText
  if (numberValue === 0) return zeroText

  const absValue = Math.abs(numberValue)
  const selectedStep = (steps || DEFAULT_MAGNITUDE_STEPS).find((step) => absValue >= step.min)
    || (steps || DEFAULT_MAGNITUDE_STEPS)[(steps || DEFAULT_MAGNITUDE_STEPS).length - 1]
  let text = trimFixed(numberValue.toFixed(selectedStep.decimals))

  if (text === '0' && absValue > 0) {
    text = trimFixed(numberValue.toFixed(12))
  }

  return text
}

function selectBestUnit(units, baseValue, fallbackIndex = 0) {
  const absValue = Math.abs(Number(baseValue))
  if (!Number.isFinite(absValue) || absValue <= 0) {
    return {
      index: fallbackIndex,
      unit: getOption(units, fallbackIndex)
    }
  }

  let selectedIndex = 0
  for (let index = 0; index < units.length; index += 1) {
    if (absValue / units[index].factor >= 1) selectedIndex = index
  }

  return {
    index: selectedIndex,
    unit: getOption(units, selectedIndex)
  }
}

function formatScaledValue(baseValue, unit, options = {}) {
  const {
    includeUnit = false
  } = options
  const selectedUnit = unit || { factor: 1, label: '' }
  const valueText = formatMagnitudeNumber(baseValue / selectedUnit.factor, options)

  return includeUnit && selectedUnit.label ? `${valueText} ${selectedUnit.label}` : valueText
}

module.exports = {
  DEFAULT_MAGNITUDE_STEPS,
  formatMagnitudeNumber,
  formatScaledValue,
  getOption,
  normalizeIndex,
  parseLooseNumber,
  parsePositiveNumber,
  selectBestUnit,
  trimFixed
}
