<script setup lang="ts">
import { ElForm, FormItemRule } from 'element-plus'
import { Arrayable } from 'element-plus/es/utils'
import type { ValidateFieldsError } from 'async-validator'
import { FormConfigType } from '@/components/AForm/type'
import { StrAnyObj } from '@/typings'
import { getResult } from '@/utils'
import FileUpload from '@/components/FlieUpload/index.vue'

const props = withDefaults(
  defineProps<{
    modelValue: StrAnyObj
    config: FormConfigType[]
    span?: number
    clearable?: boolean | (() => boolean)
    disabled?: boolean | (() => boolean)
    readonly?: boolean | (() => boolean)
  }>(),
  {
    span: 12,
    clearable: true,
    disabled: false,
    readonly: false
  }
)

const initData = JSON.parse(JSON.stringify(props.modelValue))

const emits = defineEmits(['update:modelValue'])

const computedModelValue = computed({
  get() {
    return props.modelValue
  },
  set(newValue) {
    emits('update:modelValue', newValue)
  }
})

const formRef = ref<InstanceType<typeof ElForm>>()
const formItemRefs: StrAnyObj = {}
const rules: Partial<Record<string, Arrayable<FormItemRule>>> = {}
props.config.forEach((item) => {
  rules[item.prop] = item.rule ?? []
})

defineExpose({ validate, clearValidate, resetFields, loadOption, reloadOption, clearOption })

/**
 * 验证表单
 * @param success 验证成功执行方法
 * @param fail 验证失败执行方法
 */
function validate(success?: () => void, fail?: (invalidFields?: ValidateFieldsError) => void) {
  formRef.value?.validate((isValid: boolean, invalidFields?: ValidateFieldsError) => {
    if (isValid) {
      success && success()
    } else {
      fail && fail(invalidFields)
    }
  })
}

/**
 * 移除校验结果
 */
function clearValidate() {
  formRef?.value?.clearValidate()
}

/**
 * 重置该表单项，将其值重置为初始值，并移除校验结果
 */
function resetFields() {
  computedModelValue.value = JSON.parse(JSON.stringify(initData))
  clearValidate()
}

/**
 * 执行方法
 */
function executeFormItemFun(prop: string, funName: string) {
  return formItemRefs?.[prop]?.[funName]?.()
}

/**
 * 加载Option
 */
function loadOption(prop: string) {
  executeFormItemFun(prop, 'loadOption')
}

/**
 * 重新加载Option
 */
function reloadOption(prop: string) {
  executeFormItemFun(prop, 'reloadOption')
}

/**
 * 清空Option
 */
function clearOption(prop: string) {
  executeFormItemFun(prop, 'clearOption')
}
</script>

<template>
  <el-form ref="formRef" :model="computedModelValue" :rules="rules" label-width="auto">
    <el-row :gutter="10">
      <template v-for="item in config" :key="item.prop">
        <el-col
          v-if="getResult(item.if ?? true)"
          v-show="getResult(item.show ?? true)"
          v-permissions="item.permissions"
          :span="item.span ?? span"
        >
          <el-form-item :label="item.label" :prop="item.prop">
            <template v-if="item.type === 'input'">
              <a-input
                v-model="computedModelValue[item.prop]"
                :ref="(el) => (formItemRefs[item.prop] = el)"
                :style="item.style"
                :placeholder="item.placeholder ?? `请输入${item.label}`"
                :clearable="getResult(item.clearable ?? clearable)"
                :disabled="getResult(item.disabled ?? disabled)"
                :readonly="getResult(item.readonly ?? readonly)"
                :type="item.itemType"
                :maxlength="item.maxlength"
                :minlength="item.minlength"
                :show-word-limit="item.showWordLimit"
                :show-password="item.showPassword"
                :rows="item.rows"
                :autosize="item.autosize"
                :resize="item.resize"
                :change="item.change"
              />
            </template>
            <template v-else-if="item.type === 'inputNumber'">
              <a-input-number
                v-model="computedModelValue[item.prop]"
                :ref="(el) => (formItemRefs[item.prop] = el)"
                :style="item.style"
                :placeholder="item.placeholder ?? `请输入${item.label}`"
                :disabled="getResult(item.disabled ?? disabled)"
                :readonly="getResult(item.readonly ?? readonly)"
                :min="item.min"
                :max="item.max"
                :precision="item.precision"
                :step="item.step"
                :step-strictly="item.stepStrictly"
                :controls="item.controls"
                :value-on-clear="item.valueOnClear"
                :change="item.change"
              />
            </template>
            <template v-else-if="item.type === 'select'">
              <a-select
                v-model="computedModelValue[item.prop]"
                :ref="(el) => (formItemRefs[item.prop] = el)"
                :style="item.style"
                :placeholder="item.placeholder ?? `请选择${item.label}`"
                :clearable="getResult(item.clearable ?? clearable)"
                :disabled="getResult(item.disabled ?? disabled)"
                :filterable="item.filterable"
                :multiple="item.multiple"
                :auto-load-option="item.autoLoadOption"
                :dict="item.dict"
                :option="item.option"
                :key-name="item.keyName"
                :label-name="item.labelName"
                :disabled-name="item.disabledName"
                :change="item.change"
              />
            </template>
            <template v-else-if="item.type === 'radio'">
              <a-radio
                v-model="computedModelValue[item.prop]"
                :ref="(el) => (formItemRefs[item.prop] = el)"
                :style="item.style"
                :disabled="getResult(item.disabled ?? disabled)"
                :auto-load-option="item.autoLoadOption"
                :dict="item.dict"
                :option="item.option"
                :key-name="item.keyName"
                :label-name="item.labelName"
                :disabled-name="item.disabledName"
                :change="item.change"
              />
            </template>
            <template v-else-if="item.type === 'checkbox'">
              <a-checkbox
                v-model="computedModelValue[item.prop]"
                :ref="(el) => (formItemRefs[item.prop] = el)"
                :style="item.style"
                :disabled="getResult(item.disabled ?? disabled)"
                :auto-load-option="item.autoLoadOption"
                :dict="item.dict"
                :option="item.option"
                :key-name="item.keyName"
                :label-name="item.labelName"
                :disabled-name="item.disabledName"
                :change="item.change"
              />
            </template>
            <template v-else-if="item.type === 'datePicker'">
              <a-date-picker
                v-model="computedModelValue[item.prop]"
                :ref="(el) => (formItemRefs[item.prop] = el)"
                :style="item.style"
                :placeholder="item.placeholder ?? `请选择${item.label}`"
                :clearable="getResult(item.clearable ?? clearable)"
                :disabled="getResult(item.disabled ?? disabled)"
                :readonly="getResult(item.readonly ?? readonly)"
                :type="item.datePickerType"
                :format="item.format"
                :value-format="item.valueFormat"
                :default-value="item.defaultValue"
                :default-time="item.defaultTime"
                :disabled-date="item.disabledDate"
                :shortcuts="item.shortcuts"
                :start-placeholder="item.startPlaceholder"
                :end-placeholder="item.endPlaceholder"
                :range-separator="item.rangeSeparator"
                :change="item.change"
              />
            </template>
            <template v-else-if="item.type === 'upload'">
              <file-upload
                v-model="computedModelValue[item.prop]"
                :disabled="getResult(item.disabled ?? disabled)"
                :tip="item.tip"
                :limit="item.limit"
                :width="item.width"
              />
            </template>
            <template v-else-if="item.type === 'slot'">
              <slot :name="item.prop"></slot>
            </template>
          </el-form-item>
        </el-col>
      </template>
    </el-row>
  </el-form>
</template>
