import {ref, toValue, watch, nextTick, isRef} from 'vue';
import {empty, func} from "@/uni_modules/uv-ui-tools/libs/function/test";
import _ from "lodash";
import {fnPlaceholder} from "@/utils/uni-helper";


// 一般情况下用queryParams即可，但有可能有些全局参数不在queryParams里面，则用sharedData注入进来
// 通过dependentKeys和independentKeys，获取请求必须的参数
// dependentKeys是必须参数，independentKeys是非必须参数
export const useConditionFactory = function (config, eventManager, queryParams, sharedData, console) {
    const {
        key, handler, autoInit, required, rule, allLabel, title, multiple,
        dependentKeys, independentKeys, keyName, keyValue
    } = config

    if (empty(key)) throw new Error('condition must define a key')
    if (!func(handler)) throw new Error('handler must be a function')

    const list = ref([])
    if (required && !rule.some(r => r.required)) {
        // 没有必填规则就自动创建一条
        const requiredRule = {required: true, message: `${title}不能为空`, transform: (v) => v + ''}
        config.rule = [...rule, requiredRule] // 不要直接修改rule，rule可能来自于conditionSharedConfig
    }

    const makePayload = async () => {
        const payload = {}
        const params = toValue(queryParams)
        // 先检查依赖项
        for (const key of dependentKeys) {
            const val = params[key]
            if (empty(val)) {
                // 只是给一个警告，reject的方式太重了，会让后续开发认为产生了不可修复问题。
                // 一般情况下缺必传字段，只需要等必传字段autoInit完成就会自动修复这个问题。
                // 当然万一运行不符合预期，这里也确实是需要着重检查的点。
                console.warn(`${config.key}: independent key ${key} is required, wait for next turn.`)
                return new Promise(fnPlaceholder).then()
                // return Promise.reject('some message')
            }
            payload[key] = val
        }
        for (const key of independentKeys) {
            payload[key] = params[key]
        }
        return {...toValue(sharedData), ...payload}
    }

    const loadData = async () => {
        eventManager.push('loadData calling', key)

        const payload = await makePayload()
        const results = await config.handler(payload)
        console.log('dataManager data loaded', key, results)

        const processData = function (results) {
            if (allLabel && !required) {
                const add = keyValue ? {[keyName]: allLabel, [keyValue]: ''} : allLabel
                list.value = [add, ...results] // 不直接修改，可能会影响缓存结果
            } else {
                list.value = results
            }

            const current = toValue(queryParams)[key]
            const currentInvalid = (val) => {
                const valSource = list.value.map(i => keyValue ? i[keyValue] : i)
                const diff = _.difference(valSource, [].concat(val))
                console.log('loadData invalid current:', val, ', diff', diff)
                return diff.length > 0
            }
            if (!empty(current) && currentInvalid(current)) {
                console.log('loadData clear invalid', key, current)
                queryParams.value[key] = multiple ? [] : ''
            }
            if (required && autoInit && empty(current)) {
                const first = _.first(list.value)
                const firstValue = keyValue ? first[keyValue] : first
                if (first) queryParams.value[key] = multiple ? [firstValue] : firstValue
                console.log('loadData auto init', key, firstValue)
            }

            eventManager.pop('loadData called', key)
        }

        if (isRef(results)) {
            const pureResults = toValue(results)
            if (empty(pureResults)) {
                // 此时没有数据，说明后续可能会更新，使用watch监听即可
                watch(results, (values) => processData(values))
            } else {
                processData(pureResults)
            }
        } else {
            processData(results)
        }
    }

    // 数据加载
    if (!dependentKeys.length) {
        // 无依赖项，直接调用。这里使用了nextTick做保险，万一立即进行校验，要保证uv-form渲染之后才能进行
        nextTick(async () => await loadData())
    } else {
        // 有依赖项，建立监听，依赖变更时重新加载数据
        const watches = dependentKeys.map(k => () => toValue(queryParams)[k])
        watch(watches, async () => await loadData(), {immediate: true})
    }

    return {list, config}
}