import BigNumber from 'bignumber.js'

const THOUSAND_SEPARATORS = {
  SPACE: ' ',
  COMMA: ',',
  PERIOD: '.',
  NONE: '',
}

const DECIMAL_SEPARATORS = {
  COMMA: ',',
  PERIOD: '.',
}

const NUMBER_MAX_DECIMAL_PLACES = 10

const DEFAULT_THOUSAND_SEPARATOR = THOUSAND_SEPARATORS.NONE
const DEFAULT_DECIMAL_SEPARATOR = DECIMAL_SEPARATORS.PERIOD

export const NUMBER_FORMATS = {
  NO_FORMATTING: {
    thousandSeparator: DEFAULT_THOUSAND_SEPARATOR,
    decimalSeparator: DEFAULT_DECIMAL_SEPARATOR,
    description: 'fieldNumberSubForm.noFormatting',
    value: '',
  },
  SPACE_COMMA: {
    thousandSeparator: THOUSAND_SEPARATORS.SPACE,
    decimalSeparator: DECIMAL_SEPARATORS.COMMA,
    description: 'fieldNumberSubForm.spaceComma',
    value: 'SPACE_COMMA',
  },
  SPACE_PERIOD: {
    thousandSeparator: THOUSAND_SEPARATORS.SPACE,
    decimalSeparator: DECIMAL_SEPARATORS.PERIOD,
    description: 'fieldNumberSubForm.spacePeriod',
    value: 'SPACE_PERIOD',
  },
  COMMA_PERIOD: {
    thousandSeparator: THOUSAND_SEPARATORS.COMMA,
    decimalSeparator: DECIMAL_SEPARATORS.PERIOD,
    description: 'fieldNumberSubForm.commaPeriod',
    value: 'COMMA_PERIOD',
  },
  PERIOD_COMMA: {
    thousandSeparator: THOUSAND_SEPARATORS.PERIOD,
    decimalSeparator: DECIMAL_SEPARATORS.COMMA,
    description: 'fieldNumberSubForm.periodComma',
    value: 'PERIOD_COMMA',
  },
}

/**
 * Returns all number format options for a given field
 */
export const getNumberFormatOptions = (field) => {
  const { thousandSeparator, decimalSeparator } =
    NUMBER_FORMATS[field.number_separator] ?? NUMBER_FORMATS.NO_FORMATTING

  const numberPrefix = field.number_prefix ?? ''
  const numberSuffix = field.number_suffix ?? ''
  const decimalPlaces = field.number_decimal_places ?? undefined
  const allowNegative = field.number_negative ?? false

  return {
    thousandSeparator,
    decimalSeparator,
    numberPrefix,
    numberSuffix,
    decimalPlaces,
    allowNegative,
  }
}

/*
 * FIXME: This function formats a number value according to the field's number format.
 * Value can be a number or a string. If it's a string, it must be in the format
 * of the field's number format. If it's a number, it will be formatted according
 * to the field's number format.
 */
export const formatNumberValue = (
  field,
  value,
  withThousandSeparator = true,
  roundDecimals = true
) => {
  if (value === null || value === undefined || value === '') {
    return ''
  }

  const {
    thousandSeparator,
    decimalSeparator,
    numberPrefix,
    numberSuffix,
    decimalPlaces,
  } = getNumberFormatOptions(field)

  // Parse the input value if it's a string
  let numericValue =
    typeof value === 'string'
      ? parseNumberValue(field, value, roundDecimals)
      : value

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

  numericValue = new BigNumber(numericValue)
  if (numericValue.isNaN()) {
    return String(NaN)
  }
  const isNegative = numericValue.isNegative()
  numericValue = numericValue.absoluteValue().toString()

  let locale, localeThousandsSeparator
  if (decimalSeparator === DECIMAL_SEPARATORS.COMMA) {
    locale = 'it-IT'
    localeThousandsSeparator = DECIMAL_SEPARATORS.PERIOD
  } else {
    locale = 'en-US'
    localeThousandsSeparator = DECIMAL_SEPARATORS.COMMA
  }
  // Format the number, but keep all decimal places if roundDecimals is false.
  // For example, filter values are not rounded since the backend doesn't round them.
  const formatter = new Intl.NumberFormat(locale, {
    minimumFractionDigits: decimalPlaces,
    maximumFractionDigits: roundDecimals
      ? decimalPlaces
      : NUMBER_MAX_DECIMAL_PLACES,
    useGrouping: true,
  })
  let formatted = formatter.format(numericValue)

  if (!withThousandSeparator) {
    formatted = formatted.replace(
      new RegExp(`\\${localeThousandsSeparator}`, 'g'),
      ''
    )
  } else if (thousandSeparator !== localeThousandsSeparator) {
    formatted = formatted.replace(
      new RegExp(`\\${localeThousandsSeparator}`, 'g'),
      thousandSeparator
    )
  }

  const sign = isNegative ? '-' : ''
  return `${sign}${numberPrefix}${formatted}${numberSuffix}`.trim()
}

export const parseNumberValue = (field, value, roundDecimals = true) => {
  const { numberPrefix, numberSuffix, decimalSeparator } =
    getNumberFormatOptions(field)

  if (value == null || value === '') {
    return null
  }

  const toBigNumber = (val) => {
    let rounded = val
    if (roundDecimals) {
      rounded = new BigNumber(val).decimalPlaces(
        field.number_decimal_places ?? 0
      )
    }
    return new BigNumber(rounded)
  }

  if (typeof value === 'number' || BigNumber.isBigNumber(value)) {
    return toBigNumber(value)
  }

  let result = value

  // 1. check if the number is negative
  let isNegative = false
  if (result.startsWith('-')) {
    isNegative = field.number_negative
    result = result.substring(1)
  }
  // 2. remove the prefix
  if (numberPrefix && result.startsWith(numberPrefix)) {
    result = result.substring(numberPrefix.length)
  }
  // 3. remove the suffix
  if (numberSuffix && result.endsWith(numberSuffix)) {
    result = result.substring(0, result.length - numberSuffix.length)
  }

  // 5. Match and keep the first decimal separator
  // 6. Keep all digits
  // 7. Remove any additional decimal separators and non-numeric characters
  const firstSep = result.indexOf(decimalSeparator)
  const regex = /[^0-9]/g
  if (firstSep === -1) {
    result = result.replace(regex, '')
  } else {
    const integerPart = result.substring(0, firstSep).replace(regex, '')
    const decimalPart = result.substring(firstSep + 1).replace(regex, '')
    result = `${integerPart}${decimalSeparator}${decimalPart || 0}`
  }
  // Ensure we use a period as the decimal separator before the parsing
  if (decimalSeparator !== '.') {
    result = result.replace(new RegExp(decimalSeparator, 'g'), '.')
  }

  const parsedNumber = toBigNumber(result)
  return parsedNumber.isNaN() ? null : isNegative ? -parsedNumber : parsedNumber
}