<template>
  <div
    class="bx-form-item"
    :class="[labelPositionClass, contentPositionClass]"
    >
    <div class="bx-form-item__inner">
      <div
        class="bx-form-item__label"
        :class="(isLabelMoreThanThreshold || isVerticalAlignmentForced) && 'bx-form-item__labbx--lg'"
        v-if="label"
        >
        {{ label }}
      </div>
      <div class="bx-form-item__content">
        <slot />
      </div>
    </div>
    <div
      class="bx-form-item__error"
      v-if="isShowErrorMessage && validateMessage"
      >
      {{ validateMessage }}
    </div>
  </div>
</template>
<script>
import AsyncValidator from 'async-validator'
import { create } from '../utils'
import buildInType from './build-in-types'

const LABEL_THRESHOLD = 5
const onlyValues = values => str => values.indexOf(str) !== -1
const getPositionResult = (value, type) =>
  value ? `bx-form-item__${type}--${value}` : ''

export default create({
  name: 'form-item',
  provide() {
    return {
      bxsFormItem: this,
    }
  },
  inject: {
    bxsForm: {
      default: '',
    },
  },
  props: {
    prop: String,
    label: String,
    labelPosition: {
      type: String,
      default: 'left',
      validator: onlyValues(['top', 'bottom', 'left', 'right', 'middle']),
    },
    contentPosition: {
      type: String,
      default: 'left',
      validator: onlyValues(['top', 'bottom', 'left', 'right', 'middle']),
    },
    required: [Boolean, String],
    rules: Object,
    showMessage: {
      type: Boolean,
      default: undefined,
    },
    ignoreValidate: {
      type: Boolean,
      default: false,
    },
  },
  data() {
    return {
      validateMessage: '',
      // 是否强制垂直对齐
      isVerticalAlignmentForced: false,
    }
  },
  computed: {
    form() {
      return this.bxsForm
    },
    // label 文案是否超过阈值LABEL_THRESHOLD=5
    isLabelMoreThanThreshold() {
      return this.label.length >= LABEL_THRESHOLD
    },
    // 根据 labelPosition 计算class
    labelPositionClass() {
      return getPositionResult(this.labelPosition, 'label')
    },
    // 根据 contentPosition 计算class
    contentPositionClass() {
      return getPositionResult(this.contentPosition, 'content')
    },
    // 是否显示校验错误信息，覆盖form中的showMessage
    isShowErrorMessage() {
      if (this.showMessage === undefined || this.showMessage === null) {
        return this.form.showMessage
      }
      return this.showMessage
    },
  },
  mounted() {
    if (this.prop) {
      this.form.$emit('form.addField', this)
      // 可通过inject自行实现逻辑触发
      this.$on('form.item.validate', this.onValidate)
      this.$on('form.item.reset', this.onResetField)
    }
  },
  methods: {
    // 获取表单域上的所有规则，包括form上的rules，form-item上的rules和required
    getRules() {
      const formRules = this.form.rules[this.prop]
      const selfRules = this.rules
      const requiredRule = this.getRequiredRule()
      return [].concat(selfRules || formRules || []).concat(requiredRule)
    },
    getRequiredRule() {
      if (this.required !== undefined && this.required !== null) {
        const requiredRule = {}
        requiredRule.required = !!this.required
        if (typeof this.required === 'string') {
          requiredRule.message = this.required
        }
        return requiredRule
      }
      return []
    },
    // 获取内置类型校验规则
    getBuildInRule(rules) {
      const formItemRules = rules
      return formItemRules.map(rule => {
        if (rule.type && !rule.validator) {
          /* eslint no-param-reassign: ["error", { "props": false }] */
          rule.validator = function(_rule, value) {
            return buildInType[rule.type](value)
          }
        }
        return rule
      })
    },
    validate() {
      if (this.ignoreValidate) {
        return Promise.resolve()
      }
      let rules = this.getRules()
      // 没有设置规则的项，直接跳过
      if (rules.length === 0) {
        return Promise.resolve()
      }
      const descriptor = {}
      rules = this.getBuildInRule(rules)
      descriptor[this.prop] = rules
      const validator = new AsyncValidator(descriptor, {
        // 当验证到第一个错误时，立刻回调，不再执行之后的验证
        first: true,
      })
      const source = {}
      source[this.prop] = this.form.model[this.prop]
      const promise = validator.validate(source)
      promise
        .then(() => {
          this.resetField()
        })
        .catch(({ errors }) => {
          // 通知input，信息反馈
          this.validateMessage = errors[0].message
        })
      return promise
    },
    resetField() {
      this.validateMessage = ''
    },
    onResetField() {
      this.resetField()
    },
    onValidate() {
      this.validate()
    },
  },
})
</script>
<style>
@import './index.css';
</style>
