import { Form, FormItem } from 'element-ui'
import FormItemWrap from '@/components/FormItemWrap'
import { iterator } from './shared'

const ISchema = {
  type: Object,
  default: () => ({})
}

const IConfig = {
  type: Object,
  default: () => ({})
}

const IValue = {
  type: Object,
  default: () => ({})
}

// el-form组件props的key
const EL_FORM_PROP_KEYS = Object.keys(Form.props)
// el-form-item组件props的key
const EL_FORM_ITEM_PROP_KEYS = Object.keys(FormItem.props)

export default {
  components: { FormItemWrap },
  props: {
    schema: ISchema,
    config: IConfig,
    value: IValue,
    disabled: {
      type: Boolean,
      default: false
    }
  },
  data() {
    return {
      formData: {
        ...(this.value || {})
      },
      formType: {},
      formOptions: {},
      formConfig: {
        size: 'small',
        queryAfterReset: true,
        ...(this.config || {})
      },
      lastLinkageValue: {}
    }
  },
  mounted() {
    // 先初始化default的值
    this.initDefault()
    // 初始化类型
    this.initType()
    // 再初始化options的值
    this.initOptions()
  },
  watch: {
    /**
     * 监听value变化，如果value发生变化，引用地址发生变化，则覆盖formData
     */
    value: {
      handler() {
        // 取出当前值
        const { value, formData } = this
        // 如果值发生变化
        if (value === formData) {
          return
        }
        // 遍历数据
        Object.keys(value).forEach(key => {
          // 如果当前项的值和传入的值不一致，则使用传入的值
          if (formData[key] !== value[key]) {
            formData[key] = value[key]
          }
        })
      },
      deep: true
    }
  },
  methods: {
    /**
     * 先将default的值初始化至formData中
     */
    initDefault() {
      iterator(this.schema, (schema, key) => {
        this.$set(this.formData, key, (this.value[key] === undefined || this.value[key] === null) ? schema.default : this.value[key])
      })
      // 数据开始双向绑定
      this.$emit('input', this.formData)
      // 触发change方法
      this.$emit('change', this.formData)
    },

    /**
     * 初始化formType类型
     */
    initType() {
      iterator(this.schema, (schema, key) => {
        // 重新生成的类型
        const schemaType = schema.type instanceof Function ? schema.type(this.formData) : schema.type
        // 如果当前和上一次的类型不一致，则重新赋值
        if (this.formType[key] !== schemaType) {
          this.$set(this.formType, key, schemaType)
        }
      })
    },

    /**
     * 初始化select等的下拉选项
     */
    initOptions() {
      iterator(this.schema, async(schema, key) => {
        // 如果当前是数组，则直接赋值
        if (schema.options instanceof Array) {
          this.$set(this.formOptions, key, schema.options)
        }
        // 如果当前options是promise，则直接在then中获取结果
        if (schema.options instanceof Promise) {
          const options = await schema.options
          this.$set(this.formOptions, key, options)
        }
        // 如果当前是方法，则根据是否有联动项，判断是否立即执行
        if (schema.options instanceof Function) {
          const options = await schema.options(this.formData)
          this.$set(this.formOptions, key, options)
        }
      })
    },

    /**
     * 初始化绑定的方法
     * @param schemaOn 当前组件上需要绑定的方法
     */
    initBindFunction(schemaOn) {
      // 如果没有需要绑定的方法
      if (!schemaOn) {
        return {}
      }

      // 将值绑定上formData
      return Object.keys(schemaOn).reduce((memo, key) => {
        memo[key] = schemaOn[key].bind(this, this.formData)
        return memo
      }, {})
    },

    /**
     * 绑定属性
     * @param {Object} attrs 当前属性值
     * @param {String} type  当前组件类型
     */
    bindAttrs(attrs, type) {
      // 组件有的属性
      let componentAttrs = []
      // 判断类型
      switch (type) {
        case 'el-form':
          componentAttrs = EL_FORM_PROP_KEYS
          break
        case 'el-form-item':
          componentAttrs = EL_FORM_ITEM_PROP_KEYS
          break
      }
      return Object.keys(attrs).reduce((memo, current) => {
        // 如果选择的属性，是组件的属性
        if (componentAttrs.includes(current)) {
          memo[current] = attrs[current]
        }
        return memo
      }, {})
    },

    /**
     * 过滤掉不需要显示的schema
     */
    filterUnShowSchema() {
      // 如果当前没有数据
      if (!this.schema) {
        return {}
      }
      // 当前需要显示的formSchema
      const formSchema = {}
      // 便利原始数据
      iterator(this.schema, (value, key) => {
        // 如果vif是方法
        if (value.vif instanceof Function) {
          // 如果当前vif返回结果是true，则需要直接展示
          if (value.vif(this.formData)) {
            formSchema[key] = value
          }
        } else {
          formSchema[key] = value
        }
      })
      return formSchema
    },

    /**
     * 占位文案
     * @param schema 当前schema对象
     */
    placeholder(schema) {
      // 如果当前设置了占位文案
      if (schema.attrs && schema.attrs.placeholder) {
        return schema.attrs.placeholder
      }
      // 如果当前时下拉或者级联组件，则展示选择，其他展示输入
      return `请${['el-select', 'el-cascader'].includes(schema.type) ? '选择' : '输入'}${schema.label || ''}`
    },

    /**
     * 表单数据发生变化：触发一次options方法
     * @param value 当前发生值变化的表单的值
     * @param key 当前发生值变化的表单的key
     */
    handleFormDataChange(value, key) {
      const { lastLinkageValue } = this
      // 便利数据
      iterator(this.schema, async(schema, schemaKey) => {
        // 如果当前是方法，且配置了联动项，则当联动项发生变化才会触发options
        if (schema.options instanceof Function && schema.linkage) {
          // 如果当前改变的值是联动项，则和上一次改变的值不一样，则执行options方法
          if (schema.linkage.includes(key) && value !== lastLinkageValue[key]) {
            // 重新赋值，防止保留了上一次的记过
            this.formData[schemaKey] = schema.default
            // 将options的值初始化为空
            this.$set(this.formOptions, schemaKey, [])
            // 开始执行options方法
            const options = await schema.options(this.formData)
            // 开始绑定值，渲染页面
            this.$set(this.formOptions, schemaKey, options)
          }
        }
      })
      // 记录下当前的联动项的值
      lastLinkageValue[key] = value
      // 开始初始化type
      this.initType()
      // 开始双向绑定数据
      this.$emit('input', this.formData)
      // 通过change方法，向上级暴露当前表单输入的值
      this.$emit('change', this.formData)
      // 双向绑定数据
      this.$emit('input', this.formData)
    },

    /**
     * 点击提交方法
     */
    handleSubmit() {
      this.$emit('submit', this.formData)
    },

    /**
     * 点击重置方法
     */
    handleReset() {
      // 去掉输入内容以及校验结果
      this.$refs.formRender.resetFields()
      // 通知父组件
      this.$emit('reset')
      // 如果当前需要点击重置后发起一起请求
      this.formConfig.queryAfterReset && this.handleSubmit()
    },

    /**
     * 拿到对应key的options
     * @param fieldKey 字段key
     */
    getOptions(fieldKey) {
      return this.formOptions[fieldKey]
    },

    /**
     * 拿到指定field的，具体值得option
     * @param fieldKey 字段key
     * @param optionValue 字段的值
     */
    getOption(fieldKey, optionValue) {
      return this.formOptions[fieldKey].find(option => option.value === optionValue)
    },

    /**
     * 重新加载options方法
     */
    async reloadOptions(key, data) {
      // 生成options的方法
      const { options } = this.schema[key]
      // 如果当前是方法
      if (options instanceof Function) {
        // 开始调用方法
        const option = await options(this.formData, data)
        // 开始赋值为默认值
        this.formData[key] = this.schema[key].default
        // 开始绑定值，渲染页面
        this.$set(this.formOptions, key, option)
      }
    }
  }
}
