<template>
  <el-form
    ref="FormRef"
    :model="modelData"
    class="sel-group-form"
    v-bind="props"
  >
    <s-grid v-bind="options.grid">
      <s-grid-item
        v-for="(item, index) in fields"
        :key="index"
        v-bind="item.props?.gridItem || defaultOptions?.gridItem"
        v-show="itemVisible(modelData[item.code], item, index)"
        :i="item.code"
      >
        <s-form-item
          v-if="itemVisible(modelData[item.code], item,index)"
          :ref="setItemRefs"
          :code="item.code"
          :prefix="item.prefix"
          :keys="item.key"
          :label="item.label"
          :type="item.type"
          :props="item.props"
          :children="item.children"
          :style="item.style"
          :show="item.show"
          :component="item.component"
          :input-style="item.style"
          :options="item.options"
          v-model:value="modelData[item.code]"
        />
      </s-grid-item>
    </s-grid>
  </el-form>
  <template v-if="slots.length>0">
    <teleport
      v-for="item in slots"
      :key="item.code"
      :to="`#${item.code}`"
    >
      <slot
        :slot-item="item"
        :name="item.key"
        :data="modelData"
        :validator="validator(item)"
      ></slot>
    </teleport>
  </template>
</template>
<script lang="ts" setup>
import {
  computed,
  provide,
  ref,
  watch,
  getCurrentInstance
} from 'vue'
import SFormItem from './FormItem.vue'
import { cloneDeep, get } from 'lodash-es'
import { isFunction, treeForEach } from 'utils'
import {
  useFormData,
  useFormItemRefs,
  useItemHooks,
  initValueHooks,
  initValueGroupHooks,
  useBuildGroupForm
} from '../../Form/hooks'
import { GlobalOptions } from '../types'
import { FI } from '../../Form/types'

defineOptions({
  name: 'SGroupForm'
})

interface PropsType {
  fields: Array<FI>
  modelValue: any
  props?: any
  options: GlobalOptions
}

const cProps = withDefaults(defineProps<PropsType>(), {
  fields: () => ([]),
  modelValue: () => ({}),
  options: () => ({
    grid: {
      cols: 24
    },
    gridItem: {
      span: 6
    }
  })
})

const emits = defineEmits<{
  (e: 'update:modelValue', value: any): void
  (e: 'change', key: string | [], value: any): void
  (e: 'blur', key: string | [], value: any): void
  (e: 'focus', key: string | [], value: any): void
}>()
const instance: any = getCurrentInstance()
const prefix = computed(() => instance?.appContext.config.globalProperties.prefix)
const { formData, setFormData, getFormData } = useFormData(cProps, emits)
const FormRef = ref(null)
const {
  modelData,
  getItem,
  getItemValue
} = useItemHooks(cProps)
provide('getItem', getItem)
provide('getItemValue', getItemValue)

/*
 * 获取所有slot插槽
 * */
const { slots, buildUseForm } = useBuildGroupForm(cProps.fields, formData)

/*
 * 验证器
 * */
function validator (item: any) {
  return async function (value, trigger, callback?: any) {
    const sValue = value?.target ? (value?.target?.valueAsNumber || value?.target?.value) : value
    setFormData(sValue, item.key)
    if (formItemRefs.value[item.code] && item.rules && item.rules?.length > 0) {
      const validate = await formItemRefs.value[item.code].validateValue(sValue, trigger)
      if (callback) callback(sValue, validate)
    }
  }
}

/*
 * 获取循环的ref
 * */
const { formItemRefs, setItemRefs } = useFormItemRefs()

/*
 * 控制显示隐藏
 * */
function itemVisible (val, item, index, groupIndex?) {
  if (isFunction(item.show)) {
    return item.show({ getItem, getItemValue, getGroupValue, val, item, index, groupIndex })
  } else if (item.show === undefined) {
    return true
  } else {
    return !!item.show
  }
}

const defaultOptions = computed(() => {
  return Object.assign({}, {
    grid: {
      cols: 24
    },
    gridItem: {
      span: 6
    }
  }, cProps.options)
})

provide('globalPrefix', prefix)
provide('setFormData', setFormData)
provide('getFormData', getFormData)
provide('modelData', modelData)
provide('changeEvent', changeEvent)
provide('itemVisible', itemVisible)
provide('options', defaultOptions.value)
provide('globalProps', computed(() => cProps.props))

defineExpose({
  slots,
  getItem,
  getItemValue,
  getGroupValue,
  validate
})

async function validate () {
  let access: any = {
    ok: true
  }
  await validateItems(formItemRefs, access)
  return new Promise((resolve, reject) => {
    if (access.ok) {
      resolve(access.ok)
    } else {
      reject(access.ok)
    }
  })
}

async function validateItems (formItemRefs, access) {
  for (let code in formItemRefs.value) {
    const instance = formItemRefs.value[code]
    console.log(instance.visible())
    if (instance.visible()) {
      const result = await instance.validateValue(modelData.value[code], 'all')
      if (!result) {
        access.ok = result
      }
      if (formItemRefs.value.childrenRefs) {
        await validateItems(formItemRefs.value.childrenRefs, access)
      }
    }
  }
}

// 获取Group项对应值
function getGroupValue (group: any) {
  const { label, key, code } = group
  const values: any = {}
  const predicate = (node: any) => {
    if ((label === node.label || key === node.key || code === node.code) && node.type === 'group') {
      values[node.key] = modelData.value[node.code] || []
    }
  }
  treeForEach(cloneDeep(cProps.fields), predicate)
  return values
}

function changeEvent (key: string, value: any) {
  emits('change', key, value)
}

watch(formData, value => {
  buildUseForm(cProps.fields, formData)
}, {
  deep: true
})

initValueHooks(formData, cProps.fields, cProps, modelData)
initValueGroupHooks(formData, cProps.fields, cProps, modelData)

watch(() => cProps.modelValue, () => {
  cProps.fields.forEach((field: FI) => {
    if (field.type === 'group') {
      field.props.renderChildrenOrigin.forEach((children: any, index) => {
        children.forEach((item) => {
          if (Array.isArray(item.key)) {
            const value = []
            item.key.forEach((key: string, index) => {
              value[index] = get(cProps.modelValue, key)
            })
            modelData.value[item.code] = value
          } else {
            let key = item.key
            if (item.key.indexOf('[i]') >= 0) {
              key = item.key.replace('[i]', `[${ index }]`)
            }
            modelData.value[item.code] = get(cProps.modelValue, key)
          }
        })
      })
    } else {
      if (Array.isArray(field.key)) {
        const value = []
        field.key.forEach((key: string, index) => {
          value[index] = get(cProps.modelValue, key)
        })
        modelData.value[field.code] = value
      } else {
        modelData.value[field.code] = get(cProps.modelValue, field.key)
      }
    }
  })
}, {
  deep: true
})
</script>
<style lang="scss">
.sel-group-form {
  .sel-grid {
    .el-form-item {
      width: 100%;
    }
  }
}
</style>
