import BigNumber from 'bignumber.js'
import {
  formatNumberValue,
  parseNumberValue,
  getNumberFormatOptions,
} from '@baserow/modules/database/utils/number'

/**
 * This mixin contains some method overrides for validating and formatting the
 * number field. This mixin is used in both the GridViewFieldNumber and
 * RowEditFieldNumber components. All methods requires the field to be passed as
 * they can be used in functional components.
 */
export default {
  data() {
    return {
      formattedValue: '',
      focused: false,
      roundDecimals: true,
    }
  },
  methods: {
    updateFormattedValue(field, value) {
      this.formattedValue = this.formatNumberValue(field, value)
    },
    formatNumberValue(field, value) {
      return formatNumberValue(field, value, true, this.roundDecimals)
    },
    /*
     * This method is similar to formatNumberValue, but it returns the value as a
     * string without the number prefix, suffix and thousand separator. This is
     * useful when the value is being edited and the user should only see the
     * decimal separator.
     */
    formatNumberValueForEdit(field, value) {
      const withThousandSeparator = false
      return formatNumberValue(
        field,
        value,
        withThousandSeparator,
        this.roundDecimals
      )
    },
    parseNumberValue(field, value) {
      return parseNumberValue(field, value, this.roundDecimals)
    },
    getNumberFormatOptions(field) {
      return getNumberFormatOptions(field)
    },
    onInput(field, event) {
      this.updateCopy(field, event.target.value)
    },
    onFocus() {
      this.focused = true
      this.copy = this.formatNumberValueForEdit(this.field, this.copy)
    },
    onBlur() {
      this.focused = false
      this.updateFormattedValue(this.field, this.copy)
    },
    onKeyPress(event) {
      if (!this.isValidChar(event.key)) {
        return event.preventDefault()
      }
    },
    isValidChar(char) {
      const validChars = []
      if (this.field.number_negative) {
        validChars.push('-')
      }
      // Allow any non-digit character in the number prefix and suffix.
      const {
        numberPrefix,
        numberSuffix,
        decimalSeparator,
        thousandSeparator,
      } = this.getNumberFormatOptions(this.field)
      validChars.push(...numberPrefix.split(''), ...numberSuffix.split(''))
      // Allow the decimal separator and the thousands separator.
      validChars.push(decimalSeparator, thousandSeparator)
      return /\d/.test(char) || validChars.includes(char)
    },
    prepareCopy(value) {
      // FIXME: This function is called with value that can be either a number or the string
      // representation of a number. It if's a string, it's not in the field format, so
      // we need to parse it first
      if (value == null || value === '') {
        return ''
      }
      return this.formatNumberValueForEdit(this.field, new BigNumber(value))
    },
    prepareValue(copy) {
      return this.parseNumberValue(this.field, copy)
    },
    beforeSave(copy) {
      return this.parseNumberValue(this.field, copy)
    },
    afterSave(value) {
      this.updateFormattedValue(this.field, this.prepareCopy(value))
    },
    getStartEditIndex(field, value) {
      if (value == null || value === '') {
        return 0
      }

      const { decimalSeparator, numberSuffix } =
        this.getNumberFormatOptions(field)
      const decimalSeparatorIndex = value.indexOf(decimalSeparator)
      const suffixIndex = numberSuffix ? value.indexOf(numberSuffix) : -1
      return decimalSeparatorIndex !== -1
        ? decimalSeparatorIndex
        : suffixIndex !== -1
        ? suffixIndex
        : value.length
    },
    updateCopy(field, newCopy) {
      if (newCopy == null || newCopy === '') {
        this.copy = ''
        return
      }
      const newNumber = this.parseNumberValue(field, newCopy)
      const prevNumber = this.parseNumberValue(field, this.copy)
      if (newNumber !== prevNumber) {
        this.copy = newCopy
      }
    },
  },
}