<template>
  <div
    class="vc-number-input"
    :style="setMaxWidth"
  >
    <div
      v-if="label || labelIcon"
      class="flex"
    >
      <div class="vc-number-input__label mb-2">
        <label class="vc-number-input__label-wrapper flex items-center">
          <span
            v-if="labelIcon"
            class="vc-number-input__label-wrapper--left-item"
          >
            <i
              :class="labelIcon"
              class="icon-lg vc-number-input__label-wrapper--left-item-icon text-icons"
            />
          </span>
          <span
            v-if="label"
            class="vc-number-input__label-wrapper-label"
          >
            {{ label }} <span
              v-if="required"
              class="vc-number-input__label-wrapper-label__required-icon"
            >*</span>
          </span>
          <VcPopover
            v-if="infoText || $slots['info-text']"
            :popover-width="infoTooltipWidth"
            arrow-position="bottom-center"
          >
            <template #reference>
              <span class="vc-input__label-info px-1 flex items-center">
                <i class="vc-icon-info icon-base" />
              </span>
            </template>
            <div
              class="label-info-text p-2 text-xs"
            >
              <slot name="info-text">
                {{ infoText }}
              </slot>
            </div>
          </VcPopover>
        </label>
      </div>
    </div>
    <div
      class="vc-number-input__container relative"
      data-test="number-input-container"
      :style="setMaxWidth"
      :class="{ 'disabled': disabled }"
    >
      <div
        class="vc-btn__decrease absolute left-0"
        data-test="decrease-button"
        :class="disabledDecreaseBtn"
        @click="decrease"
      >
        <i class="vc-icon-minus-linear icon-base icon" />
      </div>
      <input
        v-model.number="model"
        :class="[disabled ? 'disabled': 'bg-white']"
        class="vc-input text-main-text w-full"
        type="text"
        data-test="input-modelValue"
        :disabled="disabled"
        :placeholder="placeholder"
        @change="onChange"
        @blur="handleBlur"
        @keydown.up.prevent="increase"
        @keydown.down.prevent="decrease"
      >
      <div
        class="vc-btn__increase absolute right-0"
        data-test="increase-button"
        :class="disabledIncreaseBtn"
        @click="increase"
      >
        <i class="vc-icon-plus-linear icon-base icon" />
      </div>
    </div>
    <div
      v-if="!dynamicErrorSpace || (dynamicErrorSpace && error)"
      class="vc-number-input__error-wrapper"
    >
      <span
        class="vc-number-input__error-wrapper--text"
      >
        {{ error }}
      </span>
    </div>
  </div>
</template>

<script lang="ts">
import { defineComponent } from 'vue'
import inputMixin from '@/mixins/input.mixin'

export default defineComponent({
  name: 'VcInputNumber',
  mixins: [inputMixin],
  props: {
    precision: {
      type: Number,
      default: 0
    },
    step: {
      type: Number,
      default: 1
    },
    minValue: {
      type: Number,
      default: -Infinity
    },
    maxValue: {
      type: Number,
      default: Infinity
    }
  },
  data () {
    return {
      model: '' as string | number
    }
  },
  computed: {
    valueWithStepMoreThanMax (): boolean {
      return this.parsedModelValue(+this.modelValue + this.step) > this.maxValue
    },
    valueWithStepLessThanMin (): boolean {
      return this.parsedModelValue(+this.modelValue - this.step) < this.minValue
    },
    disabledDecreaseBtn (): string {
      return +this.modelValue <= this.minValue || this.disabled || this.valueWithStepLessThanMin ? 'disabled' : ''
    },
    disabledIncreaseBtn (): string {
      return +this.modelValue >= this.maxValue || this.disabled || this.valueWithStepMoreThanMax ? 'disabled' : ''
    },
    placeholder () {
      return this.emptyText ? this.emptyText : '0'
    }
  },
  watch: {
    modelValue: {
      immediate: true,
      handler (newV) {
        if (!newV && this.emptyText) {
          this.model = ''
          return
        }
        this.model = newV
      }
    },
    model (newV) {
      this.model = this.parsedModelValue(newV)
      if ((!newV && this.emptyText) || isNaN(this.model)) {
        this.model = ''
        return
      }
      this.setValue(this.model)
    }
  },
  methods: {
    parsedModelValue (value: number | string, isAction = false): number {
      if (this.precision) {
        return Number.parseFloat(Number(value).toFixed(this.precision))
      } else if (this.isFloat(value)) {
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        const valueN = isAction ? this.modelValue.toString().split('.') as any[] : value.toString().split('.') as any[]
        const _count = valueN ? valueN.pop().length || 0 : 0
        return Number.parseFloat(Number(value).toFixed(_count))
      }
      return Number(value)
    },
    handleBlur (): void {
      if (!this.model) {
        this.model = 0
      } else {
        this.model = this.validateValue(Number(this.model))
      }
    },
    validateValue (modelValue: number): number {
      let val = +modelValue === 0 ? 0 : +modelValue

      if (val < this.minValue) {
        val = this.minValue
      }
      if (val > this.maxValue) {
        val = this.maxValue
      }

      return val
    },
    decrease (): void {
      const hasEmptyText = typeof this.model === 'string'
      const valueLessThanMin = +this.modelValue <= this.minValue
      if (this.disabled || hasEmptyText || valueLessThanMin || this.valueWithStepLessThanMin) {
        return
      }
      this.model = this.parsedModelValue(+this.model - this.step, true)
      this.setValue(this.model)
    },
    increase (): void {
      const valueMoreThanMax = +this.modelValue >= this.maxValue
      if (valueMoreThanMax || this.disabled || this.valueWithStepMoreThanMax) {
        return
      }
      this.model = this.parsedModelValue(+this.model + this.step, true)
      this.setValue(+this.model)
    },
    onChange (event: Event): void {
      if (this.disabled) {
        return
      }
      const target = (event.target as HTMLInputElement).value
      this.setValue(+target)
    },
    isFloat (n: number | string): boolean {
      return Number(n) === n && n % 1 !== 0
    }
  }
})
</script>

<style lang="scss" scoped>
@import 'src/assets/sass/main/mixins';

.vc-number-input {
  @include inputWithNumber;
  @apply text-sm;
}
.vc-input {
  @include input
}
.vc-number-input__container {
  @apply rounded inline-flex overflow-hidden h-8;
}
.vc-input {
  @apply text-center text-sm px-9 rounded border border-secondary-actions outline-none box-content h-auto;
}
.vc-number-input__container:focus-within, .vc-number-input__container:hover {
  @apply transition duration-300 ease-out shadow-focus;
}
.vc-number-input__container.disabled:hover {
  @apply shadow-none;
}
</style>
