<template>
    <view class="form">
        <slot />
    </view>
</template>

<script lang="ts">
import { FORM_KEY, FormInstance } from '@/config/symbol'
import { PropType, defineComponent, getCurrentInstance, nextTick, provide, ref, watch } from 'vue'
import Schema from '@/uni_modules/uview-plus/libs/util/async-validator'
// 去除警告信息
Schema.warning = function () {}

interface IRuleInstance {
    required: boolean
    message: string
    trigger: 'blur' | 'change'
    validator?: () => void
}
interface IRule {
    [key: string]: IRuleInstance
}
interface IError {
    field: string
    message: string
}
export default defineComponent({
    name: 'design-form',
    components: {
        // zbDropdownMenu,
        // zbDropdownItem
    },
    props: {
        rules: {
            type: Object as PropType<IRule>,
            default: () => ({})
        },
        errorType: {
            type: String,
            default: 'message'
        },
        model: {
            type: Object,
            default: () => ({})
        }
    },
    setup(props) {
        const instance = getCurrentInstance() as FormInstance

        provide(FORM_KEY, instance.proxy)

        const formRules = ref({})
        const validator = ref({})
        const originalModel = ref(null)

        const setRules = rules => {
            if (Object.keys(rules).length === 0) return
            formRules.value = rules
            validator.value = new Schema(rules)
        }

        const validateField = (value: string[], callback, event) => {
            nextTick(() => {
                const errorsRes = [] // 校验错误信息，返回给回调方法，用于存放所有design-form-item的错误信息
                instance.proxy.children.map(child => {
                    const childErrors: IError[] = [] //用于存放design-form-item的错误信息
                    if (value.includes(child.prop)) {
                        const propertyVal = uni.$u.getProperty(props.model, child.prop)

                        const propertyChain = child.prop.split('.')
                        const propertyName = propertyChain[propertyChain.length - 1]

                        const rule = formRules.value[child.prop]
                        if (!rule) return
                        // rule规则可为数组形式，也可为对象形式，此处拼接成为数组
                        const rules = [].concat(rule)

                        for (let i = 0; i < rules.length; i++) {
                            const ruleItem = rules[i]
                            const trigger = [].concat(ruleItem?.trigger)
                            // 如果是有传入触发事件，但是此form-item却没有配置此触发器的话，不执行校验操作
                            if (event && !trigger.includes(event)) continue
                            const validator = new Schema({
                                [propertyName]: ruleItem
                            })
                            validator.validate(
                                {
                                    [propertyName]: propertyVal
                                },
                                (errors: IError[], fields) => {
                                    if (uni.$u.test.array(errors)) {
                                        errorsRes.push(...errors)
                                        childErrors.push(...errors)
                                    }
                                    child.message = childErrors[0]?.message
                                        ? childErrors[0].message
                                        : null
                                }
                            )
                        }
                    }
                    child.showMessage(child.message, props.errorType)
                })
                // 执行回调函数
                typeof callback === 'function' && callback(errorsRes)
            })
        }
        const validate = () => {
            return new Promise((resolve, reject) => {
                nextTick(() => {
                    const formItemProps = instance.proxy.children.map(item => item.prop)
                    validateField(formItemProps, errors => {
                        if (errors.length) {
                            props.errorType === 'toast' && uni.$u.toast(errors[0].message)
                            reject(errors)
                        } else {
                            resolve(true)
                        }
                    })
                })
            })
        }
        const resetFields = () => {
            instance.proxy.children.map(child => {
                const prop = child.prop
                const value = uni.$u.getProperty(originalModel.value, prop)
                uni.$u.setProperty(props.model, prop, value)
            })
        }
        watch(
            () => props.rules,
            newVal => {
                setRules(newVal)
            },
            { immediate: true }
        )

        watch(
            () => props.model,
            newVal => {
                if (!originalModel.value) {
                    originalModel.value = uni.$u.deepClone(newVal)
                }
            },
            { immediate: true }
        )
        return {
            children: [],
            validate,
            resetFields
        }
    }
})
</script>

<style lang="scss" scoped></style>
