<template>
  <div class="ds-form-item" :class="stateClasses">
    <label class="ds-input-label" v-show="!!label" :for="id">
      {{ label }}
    </label>
    <div class="ds-input-wrap">
      <div v-if="icon" class="ds-input-icon">
        <ds-icon :name="icon" />
      </div>
      <component
        class="ds-input"
        :class="[icon && 'ds-input-has-icon', iconRight && 'ds-input-has-icon-right']"
        :id="id"
        :name="name ? name : model"
        :type="type"
        :autofocus="autofocus"
        :placeholder="placeholder"
        :tabindex="tabindex"
        :disabled="disabled"
        :readonly="readonly"
        :is="tag"
        :value.prop="innerValue"
        @input="handleInput"
        @focus="handleFocus"
        @blur="handleBlur"
        :rows="type === 'textarea' ? rows : null"
      />
      <div v-if="iconRight" class="ds-input-icon-right">
        <ds-icon :name="iconRight" />
      </div>
    </div>
    <transition name="ds-input-error">
      <div class="ds-input-error" v-show="!!error">
        {{ error }}
      </div>
    </transition>
  </div>
</template>

<script>
import Schema from 'async-validator'

Schema.warning = function () {}

function getNestedValue(obj, path) {
  return path.split('.').reduce((acc, key) => (acc != null ? acc[key] : undefined), obj)
}

export default {
  name: 'OcelotInput',
  inject: {
    $parentForm: {
      default: null,
    },
  },
  props: {
    value: {
      type: [String, Object, Number, Array],
      default: null,
    },
    model: {
      type: String,
      default: null,
    },
    name: {
      type: String,
      default: null,
    },
    label: {
      type: String,
      default: null,
    },
    id: {
      type: String,
      default: null,
    },
    disabled: {
      type: Boolean,
      default: false,
    },
    readonly: {
      type: Boolean,
      default: false,
    },
    schema: {
      type: Object,
      default: () => null,
    },
    size: {
      type: String,
      default: 'base',
      validator: (value) => /^(small|base|large)$/.test(value),
    },
    tabindex: {
      type: Number,
      default: 0,
    },
    type: {
      type: String,
      default: 'text',
      validator: (value) => /^(url|text|password|email|search|textarea)$/.test(value),
    },
    placeholder: {
      type: String,
      default: null,
    },
    autofocus: {
      type: Boolean,
      default: false,
    },
    rows: {
      type: [String, Number],
      default: 1,
    },
    icon: {
      type: String,
      default: null,
    },
    iconRight: {
      type: String,
      default: null,
    },
  },
  data() {
    return {
      innerValue: null,
      error: null,
      focus: false,
    }
  },
  computed: {
    tag() {
      if (this.type === 'textarea') {
        return 'textarea'
      }
      return 'input'
    },
    stateClasses() {
      return [
        this.size && `ds-input-size-${this.size}`,
        this.disabled && 'ds-input-is-disabled',
        this.readonly && 'ds-input-is-readonly',
        this.error && 'ds-input-has-error',
        this.focus && 'ds-input-has-focus',
      ]
    },
  },
  watch: {
    value: {
      handler(value) {
        this.innerValue = value
      },
      deep: true,
      immediate: true,
    },
  },
  created() {
    if (this.$parentForm && this.model) {
      this.$parentForm.subscribe(this.handleFormUpdate)
    }
  },
  beforeDestroy() {
    if (this.$parentForm && this.model) {
      this.$parentForm.unsubscribe(this.handleFormUpdate)
    }
  },
  methods: {
    handleInput(event) {
      this.input(event.target.value)
    },
    input(value) {
      this.innerValue = value
      if (this.$parentForm && this.model) {
        this.$parentForm.update(this.model, value)
      } else {
        this.$emit('input', value)
        this.validate(value)
      }
    },
    handleFormUpdate(data, errors) {
      this.innerValue = getNestedValue(data, this.model)
      this.error = errors ? errors[this.model] : null
    },
    validate(value) {
      if (!this.schema) {
        return
      }
      const validator = new Schema({ input: this.schema })
      validator.validate({ input: value }, (errors) => {
        if (errors) {
          this.error = errors[0].message
        } else {
          this.error = null
        }
      })
    },
    handleFocus() {
      this.focus = true
    },
    handleBlur() {
      this.focus = false
    },
  },
}
</script>

<style lang="scss">
/* Styles inherited from global styleguide CSS (ds-input, ds-form-item classes).
 * Once ds-input is fully removed from the styleguide, move the styles here. */
</style>
