import BigNumber from 'bignumber.js'

import { ValidationError } from '../errors'

const DROPS_PER_XAH = 1000000.0
const MAX_FRACTION_LENGTH = 6
const BASE_TEN = 10
const SANITY_CHECK = /^-?[0-9.]+$/u

/**
 * Convert Drops to XAH.
 *
 * @param dropsToConvert - Drops to convert to XAH. This can be a string, number, or BigNumber.
 * @returns Amount in XAH.
 * @throws When drops amount is invalid.
 * @category Utilities
 */
export function dropsToXah(dropsToConvert: BigNumber.Value): number {
  /*
   * Converting to BigNumber and then back to string should remove any
   * decimal point followed by zeros, e.g. '1.00'.
   * Important: specify base BASE_10 to avoid exponential notation, e.g. '1e-7'.
   */
  const drops = new BigNumber(dropsToConvert).toString(BASE_TEN)

  // check that the value is valid and actually a number
  if (typeof dropsToConvert === 'string' && drops === 'NaN') {
    throw new ValidationError(
      `dropsToXah: invalid value '${dropsToConvert}', should be a BigNumber or string-encoded number.`,
    )
  }

  // drops are only whole units
  if (drops.includes('.')) {
    throw new ValidationError(
      `dropsToXah: value '${drops}' has too many decimal places.`,
    )
  }

  /*
   * This should never happen; the value has already been
   * validated above. This just ensures BigNumber did not do
   * something unexpected.
   */
  if (!SANITY_CHECK.exec(drops)) {
    throw new ValidationError(
      `dropsToXah: failed sanity check -` +
        ` value '${drops}',` +
        ` does not match (^-?[0-9]+$).`,
    )
  }

  return new BigNumber(drops).dividedBy(DROPS_PER_XAH).toNumber()
}

/**
 * Convert an amount in XAH to an amount in drops.
 *
 * @param xrpToConvert - Amount in XAH.
 * @returns Amount in drops.
 * @throws When amount in xrp is invalid.
 * @category Utilities
 */
export function xahToDrops(xrpToConvert: BigNumber.Value): string {
  // Important: specify base BASE_TEN to avoid exponential notation, e.g. '1e-7'.
  const xrp = new BigNumber(xrpToConvert).toString(BASE_TEN)

  // check that the value is valid and actually a number
  if (typeof xrpToConvert === 'string' && xrp === 'NaN') {
    throw new ValidationError(
      `xahToDrops: invalid value '${xrpToConvert}', should be a BigNumber or string-encoded number.`,
    )
  }

  /*
   * This should never happen; the value has already been
   * validated above. This just ensures BigNumber did not do
   * something unexpected.
   */
  if (!SANITY_CHECK.exec(xrp)) {
    throw new ValidationError(
      `xahToDrops: failed sanity check - value '${xrp}', does not match (^-?[0-9.]+$).`,
    )
  }

  const components = xrp.split('.')
  if (components.length > 2) {
    throw new ValidationError(
      `xahToDrops: failed sanity check - value '${xrp}' has too many decimal points.`,
    )
  }

  const fraction = components[1] || '0'
  if (fraction.length > MAX_FRACTION_LENGTH) {
    throw new ValidationError(
      `xahToDrops: value '${xrp}' has too many decimal places.`,
    )
  }

  return new BigNumber(xrp)
    .times(DROPS_PER_XAH)
    .integerValue(BigNumber.ROUND_FLOOR)
    .toString(BASE_TEN)
}
