<template>
  <div :class="_class">
    <label
      :class="_labelClass"
      :style="_labelStyle"
      :for="labelFor"
      v-if="label || labelWidth">{{ label }}</label>

    <div :class="_contentClass">
      <slot></slot>
      <div :class="_errorMsgClass">
        <Icon icon="warning" />
        <span>{{ validateMessage }}</span>
      </div>
    </div>
    <div :class="_iconClass" v-show="statusIcon" >
      <Tooltip :message="tips" v-show="tips">
        <Icon icon="line_circle_help" />
      </Tooltip>
      <i class="hive-icon--loading" v-show="status === 'loading'"></i>
      <Icon icon="circle_tick" v-show="status === 'success'" />
      <Icon icon="circle_error" v-show="status === 'warning'" />
      <Icon icon="circle_clear" v-show="status === 'error'" />
    </div>
  </div>
</template>

<script>
  import AsyncValidator from 'async-validator';
  import Emitter from 'utils-lib/dist/emitter';
  import Icon from '../icon';
  import Tooltip from '../tooltip';

  const name = 'hive-form-item';

  function getPropByPath(obj, path) {
    let tempObj = obj;
    path = path.replace(/\[(\w+)\]/g, '.$1');
    path = path.replace(/^\./, '');

    let keyArr = path.split('.');
    let i = 0;

    for (let len = keyArr.length; i < len - 1; ++i) {
      let key = keyArr[i];
      if (key in tempObj) {
        tempObj = tempObj[key];
      } else {
        throw new Error('prop传值不正确');
      }
    }
    return {
      o: tempObj,
      k: keyArr[i],
      v: tempObj[keyArr[i]],
    };
  }

  export default {
    name,
    components: {
      Icon,
      Tooltip,
    },
    mixins: [
      Emitter,
    ],
    inject: [
      'form',
    ],
    props: {
      label: String,
      prop: String,
      required: Boolean,
      rules: [Object, Array],
      error: String,
      validateStatus: Boolean,
      labelFor: String,
      /**
       *  标签宽度
       */
      labelWidth: String,
      /**
       * 提示
       */
      tips: String,
      /**
       * 状态
       * @member error | loading | success | warning
       */
      status: String,
      /**
       * 是否显示状态图标
       */
      statusIcon: {
        type: Boolean,
        default: true,
      },
    },
    data() {
      return {
        isRequired: false,
        validateState: this.status,
        validateMessage: this.error,
        validateDisabled: false,
        validator: {},
        errorLocation: this.form.errorLocation,
      };
    },
    computed: {
      _class() {
        return [
          `${name}`,
          {
            [`${name}--required`]: this.required || this.isRequired,
            [`${name}--error`]: this.validateState === 'error',
            [`${name}--warning`]: this.validateState === 'warning',
            [`${name}-validating`]: this.validateState === 'validating',
          },
        ];
      },
      _labelClass() {
        return [
          `${name}-label`,
        ];
      },
      _labelStyle() {
        return {
          width: this.labelWidth,
        };
      },
      _contentClass() {
        return [
          `${name}-control`,
        ];
      },
      _errorMsgClass() {
        return [
          `${name}-err`,
        ];
      },
      _iconClass() {
        return [
          `${name}-icon`,
        ];
      },

      fieldValue: {
        cache: false,
        get() {
          const model = this.form.model;
          if (!model || !this.prop) {
            return;
          }

          let path = this.prop;
          if (path.indexOf(':') !== -1) {
            path = path.replace(/:/, '.');
          }

          return getPropByPath(model, path).v;
        },
      },
    },
    watch: {
      error(val) {
        this.validateMessage = val;
        this.validateState = val === '' ? '' : 'error';
      },
      validateStatus(val) {
        this.validateState = val;
      },
      rules() {
        this.validate();
      },
    },

    mounted() {
      if (this.prop) {
        this.dispatch('hive-form', 'form-item.add', this);

        Object.defineProperty(this, 'initialValue', {
          value: this.fieldValue,
        });

        let rules = this.getRules();

        if (rules.length) {
          rules.every(rule => {
            if (rule.required) {
              this.isRequired = true;
              return false;
            }
          });
          this.$on('form.blur', this.onFieldBlur);
          this.$on('form.change', this.onFieldChange);
        }
      }
    },
    beforeDestroy() {
      this.dispatch('hive-form', 'form-item.remove', this);
    },

    methods: {
      getRules() {
        let formRules = this.form.rules;
        const selfRules = this.rules;

        formRules = formRules ? formRules[this.prop] : [];

        return [].concat(selfRules || formRules || []);
      },
      getFilteredRule(trigger) {
        const rules = this.getRules();

        return rules.filter(rule => !rule.trigger || rule.trigger.indexOf(trigger) !== -1);
      },
      validate(trigger, callback = () => {}) {
        const rules = this.getFilteredRule(trigger);
        if (!rules || rules.length === 0) {
          callback();
          return true;
        }

        this.validateState = 'validating';

        let descriptor = {};
        descriptor[this.prop] = rules;

        const validator = new AsyncValidator(descriptor);
        let model = {};

        model[this.prop] = this.fieldValue;

        validator.validate(model, { firstFields: true }, errors => {
          this.validateState = !errors ? 'success' : 'error';
          this.validateMessage = errors ? errors[0].message : '';

          // 需要错误定位
          if (this.validateMessage && this.errorLocation) {
            this.$nextTick(() => {
              const errorEl = document.querySelector('.hive-form-item-error');
              if (errorEl) {
                // 预留空白
                const top = window.innerHeight / 5;
                const scrollY = errorEl.getBoundingClientRect().top + window.pageYOffset - top;
                window.scrollTo(0, scrollY);
              }
            });
          }

          callback(this.validateMessage);
        });
        this.validateDisabled = false;
      },
      onFieldBlur() {
        this.validate('blur');
      },
      onFieldChange() {
        if (this.validateDisabled) {
          this.validateDisabled = false;
          return;
        }

        this.validate('change');
      },
    },
  };
</script>
