<template>
  <el-form-item
    ref="formItemRef"
    class="sel-form-item"
    :prop="code"
    :error="errorMessage"
    v-bind="formItemProps"
    :label-width="String(props?.labelWidth)!=='0'?formItemProps.labelWidth:'0'"
  >
    <template v-if="label&&String(props?.labelWidth)!=='0'" #label>
      <label class="sel-form-label">
        <span class="required" v-if="props.required||props?.rules&&hasRequired(props?.rules)">*</span>
        {{ label }}
        <template v-if="label && configOptions.labelSuffix">{{ configOptions.labelSuffix }}</template>
      </label>
    </template>
    <template v-if="type === 'slot'">
      <div :id="code"></div>
    </template>
    <template v-else>
      <s-form-content
        :component="component"
        :prefix="mergePrefix"
        :code="code"
        :keys="keys"
        :type="type"
        :options="options"
        :props="itemProps.props"
        :errorMessage="errorMessage"
        @validateValue="validateValue"
      />
    </template>
  </el-form-item>
</template>
<script lang="ts" setup>
import { inject, ref, Ref, onMounted, computed, provide, nextTick } from 'vue'
import SFormContent from './FormContent.vue'
import Schema from 'async-validator'
import { Op, FI } from '../types'
import { useFormItemProps, useMergeStyle } from '../hooks'
import { cloneDeep } from 'lodash-es'
import { isFunction } from 'utils'

interface Props {
  modelValue: any
  label: string
  prefix: string
  code: string
  keys: Array<string> | string
  type: string
  options?: Array<Op>
  props?: any
  style?: any
  component?: any
  show: any
  index: number
}

const itemProps = withDefaults(defineProps<Props>(), {
  label: '',
  prefix: '',
  code: '',
  keys: '',
  type: '',
  options: () => ([]),
  props: () => ({}),
  style: () => ({})
})

const emits = defineEmits<{
  (e: 'update:modelValue', value: any): void
  (e: 'change', value: any): void
}>()
const mergeGrid = computed(() => {
  return Object.assign(configOptions?.grid, itemProps.props?.grid)
})
const formItemRef = ref()
const errorMessage: Ref<string> = ref('')
const modelData: any = inject('modelData')
const setFormData: any = inject('setFormData')
const configOptions: any = inject('options')
const globalPrefix: any = inject('globalPrefix')
const globalProps: any = inject('globalProps')
const getItem: any = inject('getItem')
const getItemValue: any = inject('getItemValue')

const mergePrefix = computed(() => itemProps.prefix ?? globalPrefix.value)

const formItemProps = useFormItemProps(globalProps, itemProps)
const { mergeStyle, styled } = useMergeStyle(configOptions, itemProps.style)
onMounted(() => {
  styled.value(formItemRef.value.$el, mergeStyle.value)
})

function hasRequired (rules) {
  return !!rules.find(item => item.required === true)
}

/*
 * 根据规则验证数值
 * */
function validateValue (val, trigger) {
  const allRules = itemProps.props?.rules
  const rules = trigger == 'all' ? allRules : allRules.filter(item => item.trigger === trigger)
  const useRules = cloneDeep(rules)
  if (useRules) {
    return new Promise(resolve => {
      let v = val
      const isNumberStr = new RegExp(/^[\d|\.]*$/)
      if (typeof v === 'string' && v.length > 0 && isNumberStr.test(v)) {
        useRules.forEach(item => {
          if (!item.type) item.type = 'number'
        })
        v = Number(v)
      }
      if (typeof v === 'number') {
        useRules.forEach(item => {
          if (!item.type) item.type = 'number'
        })
        v = Number(v)
      }

      if (Array.isArray(v)) {
        const r = v.filter((item) => {
          return item !== null && typeof item !== 'undefined' && item !== ''
        })
        v = r
      }
      const validator = new Schema({
        [itemProps.code]: useRules
      })
      validator.validate({ [itemProps.code]: v }, (errors: any, fields) => {
        if (errors) {
          errorMessage.value = errors[0].message
          resolve(false)
        } else {
          errorMessage.value = ''
          resolve(true)
        }
      })
    })
  } else {
    return new Promise(resolve => {
      errorMessage.value = ''
      resolve(true)
    })
  }
}

async function setErrorMessage (message: string) {
  await nextTick()
  errorMessage.value = message
}

provide('setErrorMessage', setErrorMessage)

function visible () {
  let visible = true
  if (isFunction(itemProps.show)) {
    const params = {
      getItem,
      getItemValue,
      val: itemProps.modelValue,
      index: itemProps.index,
      item: getItem({ code: itemProps.code })
    }
    visible = itemProps.show(params)
  }
  return visible
}

defineExpose({
  validateValue,
  visible,
  code: itemProps.code
})
</script>
<style lang="scss">
.sel-form-item {
  .el-form-item__content {
    & > div {
      flex: 1;
    }
  }
}

.sel-form-label {
  .required {
    color: var(--el-color-danger);
  }
}
</style>
