<template>
  <FormGroup
    :error="(parentFields && parentFields.hasError(currentName)) || errorCondition"
    :loading="loading"
    :loading-icon="!!(loading && icon)">
    <FormInput
      ref="formInput"
      :disabled="disabled">
      <label
        v-if="secondaryLabel || !!$slots['secondary-label']"
        :for="currentLabelId"
        :class="{
          'cursor-not-allowed': disabled,
        }"
        class="form__secondary-label inline-block font-medium text-03 user-select-none">
        {{ secondaryLabel }}
        <slot name="secondary-label" />
      </label>
      <input
        ref="input"
        v-if="type !== 'textarea'"
        :id="currentLabelId"
        v-mask="config"
        v-focus="focus"
        v-bind="$attrs"
        :type="type"
        :disabled="disabled"
        :placeholder="placeholder"
        :autocomplete="autocomplete ? 'on' : 'off'"
        :readonly="readonly"
        :value="currentValue"
        :aria-label="label"
        :name="name"
        :style="icon && 'padding-right:0;'"
        v-on="listeners"
        class="tao__input">
      <textarea
        ref="input"
        v-else
        :id="currentLabelId"
        v-focus="focus"
        v-bind="$attrs"
        :disabled="disabled"
        :placeholder="placeholder"
        :autocomplete="autocomplete ? 'on' : 'off'"
        :readonly="readonly"
        :value="currentValue"
        :aria-label="label"
        :name="name"
        :style="icon && 'padding-right:0;'"
        v-on="listeners"
        class="tao__input" />
      <button
        v-if="icon"
        :class="{
          'cursor-pointer': !disabled,
          'opacity-md-max cursor-not-allowed': disabled
        }"
        :disabled="disabled"
        :tabindex="tabindex"
        @click.prevent="!disabled && $emit('icon-action')"
        class="icon__form inline-block p-0 cursor-pointer text-03
        bg-transparent text-center rounded m-1-4 rounded-circle">
        <TaoMiniIcon
          :name="icon" />
      </button>
    </FormInput>
    <FormLabel
      v-if="label"
      :for="currentLabelId">
      {{ label }}
      <slot name="label" />
    </FormLabel>
    <span
      :class="errorPositionClass"
      class="tao__form-error text-action-error absolute font-medium mt-1-2 text-smd">
      {{ showErrorMessage() }}
    </span>
  </FormGroup>
</template>

<script>
// @flow
import mask from 'assets/masker/masker.directive';
import masker from 'assets/masker/masker';
import tokens from 'assets/masker/tokens';

import focus from 'assets/focus/focus.directive';

import FormInput from './GenericFormInput.vue';
import FormGroup from './GenericFormGroup.vue';
import FormLabel from './GenericLabel.vue';

export default {
  name: 'TaoInput',
  components: {
    FormLabel,
    FormInput,
    FormGroup,
    TaoMiniIcon: () => import('packages/TaoMiniIcon'),
  },
  directives: { mask, focus },
  props: {
    type: {
      type: String,
      default: 'text',
    },
    name: {
      type: String,
      default: () => null,
    },
    placeholder: {
      type: String,
      default: '',
    },
    label: {
      type: String,
      default: '',
    },
    autocomplete: {
      type: Boolean,
      default: false,
    },
    readonly: {
      type: Boolean,
      default: false,
    },
    disabled: {
      type: Boolean,
      default: false,
    },
    value: {}, // eslint-disable-line
    secondaryLabel: {
      type: String,
      default: '',
    },
    mask: {
      type: [String, Array],
      default: '',
    },
    masked: {
      type: Boolean,
      default: false,
    },
    tokens: {
      type: Object,
      default: () => tokens,
    },
    icon: {
      type: String,
      default: '',
    },
    formKey: {
      type: Number,
      default: 1,
    },
    loading: {
      type: Boolean,
      default: false,
    },
    errorMessage: {
      type: String,
      default: '',
    },
    errorCondition: {
      type: Boolean,
      default: false,
    },
    errorPosition: {
      type: String,
      default: 'right',
    },
    focus: {
      type: Boolean,
      default: false,
    },
    id: {
      type: String,
      default: () => null,
    },
  },
  data() {
    return {
      currentValue: this.value,
      lastValue: null,
      currentName: null,
    };
  },
  computed: {
    parentFields(): {} {
      return this.$parent.fields || null;
    },
    currentLabelId(): string {
      if (this.id !== null) {
        return this.id;
      }
      if (this.label) {
        return this.transformID(this.label);
      }
      if (this.placeholder) {
        return this.transformID(this.placeholder);
      }
      return this.formKey;
    },
    config() {
      return {
        mask: this.mask,
        masked: this.masked,
        tokens: this.tokens,
      };
    },
    errorPositionClass(): string {
      return this.errorPosition === 'left'
        ? 'left-position'
        : 'right-position';
    },
    listeners(): {} {
      return Object.assign({}, this.$listeners, {
        input: this.handleInput,
        focus: this.handleFocus,
        blur: this.handleBlur,
      });
    },
    tabindex(): number {
      return this.currentValue ? -1 : 0;
    },
  },
  watch: {
    value(newValue: any) {
      if (newValue !== this.lastValue) {
        this.refresh(newValue);
      }
    },
    masked() {
      this.refresh(this.currentValue);
    },
  },
  mounted() {
    this.prepareInputForTest();

    this.currentName = this.name || (this.$el && this.$el.getAttribute('data-tao-name'));
  },
  methods: {
    prepareInputForTest() {
      const { input } = this.$refs;
      const inputProperties = Object.getPrototypeOf(input);
      const valueDescriptor = Object.getOwnPropertyDescriptor(inputProperties, 'value');

      if (input && valueDescriptor) {
        const { get, set } = valueDescriptor;

        this.defineValueProperty(input, { get, set });
      }
    },
    defineValueProperty(nodeElement: InputEvent, { get, set }: Object) {
      const vm = this;

      Object.defineProperty(nodeElement, 'value', { // eslint-disable-line
        get(...args) {
          return get.apply(this, args);
        },
        set(...args) {
          if (vm.disabled) return '';

          vm.refresh(...args);
          return set.apply(this, args);
        },
      });
    },
    showErrorMessage(): string {
      if (this.parentFields && this.parentFields.error(this.currentName)) {
        return this.parentFields.error(this.currentName);
      }
      if (this.errorCondition) {
        return this.errorMessage;
      }
      return '';
    },
    handleInput(event: InputEvent) {
      this.refresh(event.target.value);
    },
    refresh(value: any) {
      if (this.disabled) { return; }

      this.currentValue = value;
      value = masker(value, this.mask, this.masked, this.tokens);
      if (value !== this.lastValue) {
        this.lastValue = value;
        this.$emit('input', value);
      }
    },
    handleFocus(event: InputEvent) {
      this.$emit('focus', event);

      if (!this.disabled) {
        const { target } = event;

        if (target.parentNode instanceof HTMLElement) {
          target.parentNode.classList.add('form__focus');
        }
      }
    },
    handleBlur(event: InputEvent) {
      this.$emit('blur', event);

      const { target } = event;

      if (target.parentNode instanceof HTMLElement) {
        target.parentNode.classList.remove('form__focus');
      }
    },
    transformID(data: string): string {
      const lowerCaseLabel = data.toLowerCase();
      return `${lowerCaseLabel.replace(/[^a-zA-Z0-9]/g, '')}-${this.formKey}`;
    },
  },
};
</script>
