<template>
  <div class="fbg-form-item" ref="mainRef">
    <div class="fbg-form-item-label" :style="{width: itemLabelWidth, display: inline?'inline-block':'table-cell'}">
      <slot name="label"><span v-if="label&&requiredStatus" class="fbg-form-item-required" />{{label}}</slot>
    </div>
    <div :class="{'fbg-form-item-content': true, 'fbg-form-item-content-error': !!errStatus}" :style="{display: inline?'inline-block':'table-cell'}">
      <div class="fbg-form-item-content-box">
        <fbg-tooltip :disabled="!errStatus" :content="errorMsg" :tabindex="-1" placement="bottom" effect="light">
          <span>
            <slot></slot>
          </span>
        </fbg-tooltip>
        <!-- <div v-if="formData.formType === 'table'" class="fbg-form-item-content-box-error">
          <div v-show="errorMsg">
            <span class="fbg-form-item-content-box-error-icon">
              <i class="fbgiconfont icon-zhuangtai-yichang" />
            </span>
            <span>{{errorMsg}}</span>
          </div>
        </div> -->
      </div>
    </div>
    <!-- <div class="fbg-form-item-error" v-if="formData.formType === 'normal'">
      <div v-show="errorMsg">
        <span class="fbg-form-item-error-icon">
          <i class="fbgiconfont icon-zhuangtai-yichang" />
        </span>
        <span>{{errorMsg}}</span>
      </div>
    </div> -->
  </div>
</template>
<script>
import util from '../../utils';
import commonExtends from '../common/commonExtend';
import {Tooltip} from 'element-ui';
export default {
  name: 'FbgFormItem',
  componentName: 'FbgFormItem',
  components: {
    'fbg-tooltip': Tooltip
  },
  mixins: [commonExtends],
  props: {
    labelWidth: {
      type: String,
      default: ''
    },
    label: {
      type: String,
      default: ''
    },
    prop: {
      type: String,
      default: ''
    },
    required: {
      type: Boolean,
      default: false
    },
    inline: {
      type: Boolean,
      default: false
    }
  },
  data() {
    return {
      initialValue: '',
      errorMsg: '',
      errStatus: false,
      requiredStatus: false,
    }
  },

  inject: ["formData", "deleteValidateField", "addValidateField", "clearValidate"],
  watch: {
    required: {
      immediate: true,
      handler(value) {
        this.requiredStatus = value;
      }
    },
    prop: {
      immediate: true,
      handler(newValue, oldValue) {
        this.deleteValidateField(oldValue);
        this.addValidateField(newValue, (callback) => {
          this.validate(newValue, callback);
        }, this);
        this.clearValidate(newValue, () => {
          this.triggle(false);
        });
        this.setRequired(newValue);
      }
    },
    "formData.rules": {
      immediate: true,
      deep: true,
      handler() {
        this.setRequired(this.prop);
      }
    }
  },
  destroyed() {
    this.deleteValidateField(this.prop);
  },
  computed: {
    itemLabelWidth() {
      return this.labelWidth || this.formData.labelWidth;
    },
    form() {
      let parent = this.$parent;
      let parentName = parent.$options.componentName;
      while (parentName !== 'FbgForm') {
        parent = parent.$parent;
        parentName = parent.$options.componentName;
      }
      return parent;
    }
  },
  mounted() {
    if (this.prop) {
      this.dispatch('FbgForm', 'fbg.form.addField', [this]);
      const value = util.getObjectValue(this.form.model, this.prop);
      let initialValue = value;
      if (initialValue instanceof Array) {
        initialValue = [].concat(initialValue);
      }
      this.initialValue = initialValue;
      this.$on('fbg.formItem.validate', (option, event = 'change') => {
        this.validate(this.prop, () => {}, event);
      });
    }
  },
  methods: {
    initError() {
      this.errStatus = false;
      this.errorMsg = '';
    },
    resetField() {
      let model = this.form.model;
      let value = this.fieldValue;
      
      const lastProp = util.getLastProp(this.prop);
      const data = util.getObjectValue(model, this.prop, false);
      data[lastProp] = util.deepCopy(this.initialValue);
      this.$nextTick(() => {
        this.initError();
      });
      
    },
    setRequired(prop) {
      const lastProp = util.getLastProp(prop);
      const rules = this.formData.rules[lastProp];
      if (rules instanceof Array) {
        rules.forEach((item, index) => {
          if (item.required) {
            this.requiredStatus = true;
          }
        })
      } else if (util.isObject(rules)) {
        if (item.required) {
          this.requiredStatus = true;
        }
      }
    },
    validate(prop, callback, eventName = '') {
      const lastProp = util.getLastProp(prop);
      const rules = this.formData.rules[lastProp];
      const value = util.getObjectValue(this.formData.model, prop);
      if (rules instanceof Array) {
        const len = rules.length;
        let count = 0;
        let success = 0;
        const validatorList = [];
        rules.forEach((item, index) => {
          validatorList.push(() => {
            this.validateValue(item, value, eventName, (vali) => {
              count++;
              if (vali === true) {
                success++;
                if (count === len && success === len) {
                  typeof callback === 'function' && callback(true);
                } else {
                  const cb = validatorList.shift();
                  cb && cb();
                }
              } else {
                typeof callback === 'function' && callback(false);
              }
            });
          })
        });
        const cb = validatorList.shift();
        cb && cb();
      } else if (util.isObject(rules)){
        this.validateValue(rules, value, eventName, (vali) => {
          typeof callback === 'function' && callback(true);
        });
      } else {
        callback(true);
      }
    },
    validateValue(item, value, eventName, callback) {
      if (eventName && item.trigger !== eventName) {
        return false;
      }
      const hasMin = typeof item.min === 'number';
      const hasMax = typeof item.max === 'number';
      if (item.required) {
        let flag = true;
        this.requiredStatus = true;
        if (item.type === undefined) {
          item.type = 'string';
        }
        if (value instanceof Array && item.type !== 'array') {
          throw new Error(`Error: value is array, but rule.type is ${item.type}!`);
        }
        if (!(value instanceof Array) && item.type === 'array') {
          throw new Error(`Error: rule.type is array, but value is not a array!`);
        }
        switch(item.type) {
          case 'array':
            if (value instanceof Array) {
              if (value.length === 0) {
                flag = true;
              } else {
                flag = false;
              }
            } else {
              throw new Error(`value is ${typeof value} , rule.type is array!`);
            }
            break;
          case 'string':
          case 'number':
            if (typeof value === 'string' ||typeof value === 'number') {
              if (value === '' || value === null) {
                flag = true;
              } else {
                flag = false;
              }
            }
            break;
          default: 
            if (typeof value === 'object') {
              if (Object.keys(value).length === 0) {
                flag = true;
              } else {
                flag = false;
              }
            } else {
              throw new Error(`value is ${typeof value} , rule.type is object!`);
            }
        }
        
        if (flag) {
          this.triggle(flag, item.message);
          callback(false);
        } else {
          this.triggle(flag);
          callback(true);
        }
      } else if (hasMin || hasMax) {
        const len = value.length;
        let flag = true;
        if (typeof value === 'number') {
          value = value.toString();
        }
        if (hasMin && hasMax) {
          if (!(len >= item.min && len <= item.max)) {
            flag = false;
          }
        } else if (hasMin) {
          if (len < item.min) {
            flag = false;
          }
        } else if (hasMax) {
          if (len > item.max) {
            flag = false;
          }
        }
        if (!flag) {
          this.triggle(true, item.message);
        } else  {
          this.triggle(false);
        }
        callback(flag);
      } else if (item.pattern instanceof RegExp) {
        if (item.value !== '' && !item.pattern.test(value)) {
          this.triggle(true, item.message);
          callback(false);
          return false;
        } else {
          this.triggle(false);
          callback(true);
        }
      } else if (typeof item.validator === 'function') {
        item.validator(value, {rules: item, prop: this.prop}, (err) => {
          if (err instanceof Error) {
            this.triggle(true, err.message);
            callback(false);
          } else {
            this.triggle(false);
            callback(true)
          }
        });
        
      }
    },
    triggle(flag, str = '') {
      this.errorMsg = str;
      this.errStatus = flag;
    }
  }

}
</script>
<style lang="scss">
.fbg-form-item-content-error .fbg-form-item-status{
  border: 1px solid red!important;
}
</style>
<style lang="scss" scoped>

.fbg {
  &-form {
    &-item {
      display: table;
      width: 100%;
      padding: 5px 0px;
      &-required {
        color: red;
        padding: 0px 3px;
        position: relative;
        width: 0px;
        margin-left: -6px;
        &::before {
          content: '*';
          color: red;
          position: absolute;
          left: -4px;
        }
      }
      &-label {
        display: table-cell;
        vertical-align: middle;
      }
      &-content {
        display: table-cell;
        vertical-align: top;
        &-box {
          display: inline-block;
          width: 100%;
          position: relative;
          &:hover {
            .fbg-form-item-content-box-error {
              visibility: visible;
              opacity: 1;
              margin-bottom: 10px;
            }
          }
          &-error {
            position: absolute;
            bottom: 100%;
            padding: 5px 10px;
            margin-bottom: 0px;
            opacity: 0;
            left: 50%;
            transform: translateX(-50%);
            border: 1px solid red;
            background-color: #FDD9D9;
            color: #333;
            border-radius: 3px;
            visibility: hidden;
            display: inline-block;
            min-width: 100px;
            max-width: 240px;
            transition:all 0.6s;
            &-icon {
              color: red;
              padding: 0px 3px;
              i {
                font-size: 14px;
              }
            }
          }
        }
      }
      &-status {

      }
      &-error {
        display: table-cell;
        vertical-align: middle;
        padding: 3px 10px;
        width: 240px;
        color: red;
      }
    }
  }
}
</style>