<template>
    <uv-form ref="form" :model="model" :rules="mergedRules">
        <slot name="prefix"/>
        <view v-if="renderRuleLoading" class="fx-row fx-cen-cen py-30">
            <uv-loading-icon text="正在获取录取规则信息..." color="var(--primary-color)"
                             text-color="var(--primary-color)"/>
        </view>
        <ai-form-item-group v-for="(rules, c) in groupedRenderRules" :category="c" :rules="rules"/>
        <uv-form-item v-if="false&&renderRules.length">
            <uv-button text="自动填写（测试专用）" @click="handleAutoFill"/>
        </uv-form-item>
        <slot name="suffix"/>
    </uv-form>
</template>

<script>
import MxConst from "@/common/MxConst";
import _ from 'lodash';
import AiFormItemGroup from "@/pages/ie/components/ai-form/items/ai-form-item-group.vue";
import {toast} from "@/uni_modules/uv-ui-tools/libs/function";

export default {
    name: "ai-form",
    components: {AiFormItemGroup},
    props: {
        model: {
            type: Object,
            default: () => ({})
        },
        rules: {
            type: Object,
            default: () => ({})
        },
        renderRules: {
            type: Array,
            default: () => []
        },
        renderRuleLoading: {
            type: Boolean,
            default: false
        },
        idPrefix: {
            type: [String, Number],
            default: ''
        }
    },
    data() {
        return {
            originalFields: null
        }
    },
    computed: {
        groupedRenderRules() {
            // object的key值只能是字符串或Symbol,所以这里的iteratee没有直接去匹配mxConst中的枚举对象
            // 而是改为由子组件渲染时再去匹配
            return _.groupBy(this.renderRules, r => r.enumRuleCategory)
        },
        mergedRules() {
            const autoRules = {}
            this.renderRules.forEach(r => {
                const fieldRules = []
                // 录取规则，自动添加非空校验
                if (r.enumRuleCategory == MxConst.enum.ai.ruleCategory.enroll.value || r.required) {
                    fieldRules.push({required: true, message: `请填写得分`})
                }
                // 分制类型的输入，要同时校验分制与得分
                if (r.enumInputType == MxConst.enum.ai.inputType.score) {
                    fieldRules.push({
                        validator: (_r, _v, cb) => {
                            const fieldTotal = r.fieldName + 'Total'
                            let score = this.model[r.fieldName]
                            let total = this.model[fieldTotal]
                            if (!total) {
                                cb(`请选择总分`)
                                return
                            }
                            if (!score) {
                                cb(`请填写得分`)
                                return
                            }
                            score = score * 1, total = total * 1 // 强转数值
                            if (score < 0 || score > total) {
                                cb(`得分不能超过总分${total}`)
                                return
                            }
                            cb()
                        }
                    })
                }
                const hasVal = val => val !== null && val !== undefined
                if (hasVal(r.min) || hasVal(r.max)) {
                    const createRangeMsg = () => {
                        if (r.enumInputType == MxConst.enum.ai.inputType.text) {
                            if (hasVal(r.min) && hasVal(r.max)) return `长度必须在${r.min}-${r.max}个字符之间`
                            if (hasVal(r.min)) return `长度至少${r.min}个字符`
                            if (hasVal(r.max)) return `长度不超过${r.max}个字符`
                        } else if (r.enumInputType == MxConst.enum.ai.inputType.number) {
                            if (hasVal(r.min) && hasVal(r.max)) return `数值必须在${r.min}-${r.max}之间`
                            if (hasVal(r.min)) return `数值不能小于${r.min}`
                            if (hasVal(r.max)) return `数值不能大于${r.max}`
                        } else if (r.enumInputType == MxConst.enum.ai.inputType.checkbox) {
                            if (hasVal(r.min) && hasVal(r.max)) return `必须选择${r.min}-${r.max}项`
                            if (hasVal(r.min)) return `至少选择${r.min}项`
                            if (hasVal(r.max)) return `至多选择${r.max}项`
                        }
                    }
                    const createRangeType = () => {
                        switch (r.enumInputType) {
                            case MxConst.enum.ai.inputType.number:
                            case MxConst.enum.ai.inputType.score:
                                return 'number'
                            case MxConst.enum.ai.inputType.checkbox:
                                return 'array'
                            default:
                                return 'string'
                        }
                    }
                    const rangeRule = {
                        type: createRangeType(),
                        min: r.min,
                        max: r.max,
                        message: createRangeMsg()
                    }
                    if (!hasVal(r.min)) delete rangeRule.min
                    if (!hasVal(r.max)) delete rangeRule.max
                    if (r.enumInputType == MxConst.enum.ai.inputType.number)
                        rangeRule.transform = val => hasVal(val) ? val * 1 : val
                    fieldRules.push(rangeRule)
                }
                if (r.regex) {
                    fieldRules.push({
                        pattern: r.regex,
                        message: `${r.label}格式不符合要求`
                    })
                }
                if (fieldRules.length) autoRules[r.fieldName] = fieldRules
            })
            return {
                ...autoRules,
                ...this.rules
            }
        }
    },
    watch: {
        renderRules: {
            immediate: true,
            handler: function (rules) {
                // when rule changed, clear all dynamic fields
                this.clearDynamicFields()
                // make form properties reactive
                rules.forEach(r => {
                    this.$set(this.model, r.fieldName, r.defaultValue)
                    if (r.enumInputType == MxConst.enum.ai.inputType.score) {
                        // 分数类直接补充分制字段
                        const defTotal = r.options?.length == 1 ? _.first(r.options) : ''
                        this.$set(this.model, r.fieldName + 'Total', defTotal)
                    } else if (r.enumInputType == MxConst.enum.ai.inputType.checkbox) {
                        // 多选修正modelValue
                        if (!this.model[r.fieldName]) this.model[r.fieldName] = []
                    }
                })
            }
        }
    },
    provide() {
        return {
            getIdPrefix: () => this.idPrefix, // for validation failed location
            getFormRef: () => this.$refs.form,
            getFormModel: () => this.model,
            getFormRules: () => this.rules,
            getRenderRules: () => this.renderRules
        }
    },
    methods: {
        handleAutoFill() {
            const formKeys = Object.keys(this.model)
            formKeys.forEach(key => {
                if (this.originalFields.includes(key)) return
                const rule = this.renderRules.find(r => r.fieldName == key)
                if (!rule) return
                switch (rule.enumInputType) {
                    case MxConst.enum.ai.inputType.score:
                        this.model[key] = _.first(rule.options)
                        break
                    case MxConst.enum.ai.inputType.number:
                        this.model[key] = 1
                        break
                    default:
                        break
                }
            })
        },
        clearDynamicFields() {
            const formKeys = Object.keys(this.model)
            if (formKeys.length) {
                if (!this.originalFields) this.originalFields = [...formKeys]
                formKeys.forEach(key => {
                    if (!this.originalFields.includes(key)) delete this.model[key]
                })
            }
        },
        async validate() {
            try {
                await this.$refs.form.validate()
            } catch (e) {
                if (Array.isArray(e) && e.length) {
                    const demo = e[0]
                    toast(demo.message)
                    const demoDom = document.getElementById(this.idPrefix + demo.field)
                    if (demoDom) demoDom.scrollIntoView(MxConst.scrollIntoOption)
                }
                return Promise.reject(e)
            }
        }
    }
}
</script>

<style scoped>

</style>