<template>
    <el-form ref="formRef" :model="localModel" :rules="formRules" label-width="auto" v-bind="$attrs"
        :validate-on-rule-change="false">
        <el-row :gutter="20">
            <el-col v-for="item in visibleConfig" :key="item.prop" :span="item.span || 6">
                <el-form-item :label="item.label" :prop="item.prop" :class="{ 'search-form-item': isSearch }"
                    :label-position="item.labelPosition || 'top'">
                    <!-- Input -->
                    <el-input v-if="item.type === 'input'" v-model="localModel[item.prop]"
                        :placeholder="item.placeholder || `请输入${item.label}`" :clearable="true"
                        @input="(val) => handleNumberInput(val, item)"
                        @change="(val) => handleModelChange(val, item)" />

                    <!-- Select (支持 dictType / api / options) -->
                    <el-select v-else-if="item.type === 'select'" v-model="localModel[item.prop]"
                        :multiple="!!item.multiple" :placeholder="item.placeholder || `请选择${item.label}`"
                        :clearable="true" filterable :loading="remoteLoading[item.prop] || false"
                        @change="(val) => handleModelChange(val, item)" @focus="() => loadRemoteOptions(item)"
                        @filter-change="(keyword) => handleFilterChange(keyword, item)">
                        <el-option v-for="opt in getSelectOptions(item)" :key="opt.value" :label="opt.label"
                            :value="opt.value" />
                    </el-select>
                    <!-- Date -->
                    <el-date-picker v-else-if="item.type === 'date'" v-model="localModel[item.prop]" type="date"
                        :placeholder="`选择${item.label}`" :value-format="item.valueFormat || 'YYYY-MM-DD'"
                        style="width: 100%" :disabled-date="getDisabledDateFn(item)"
                        @change="(val) => handleModelChange(val, item)" />
                    <!-- Month -->
                    <el-date-picker v-else-if="item.type === 'month'" v-model="localModel[item.prop]" type="month"
                        :placeholder="`选择${item.label}`" :value-format="item.valueFormat || 'YYYY-MM'"
                        style="width: 100%" :disabled-date="getDisabledDateFn(item)"
                        @change="(val) => handleModelChange(val, item)" />


                    <!-- Daterange -->
                    <el-date-picker v-else-if="item.type === 'daterange'" v-model="localModel[item.prop]"
                        type="daterange" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期"
                        :value-format="item.valueFormat || 'YYYY-MM-DD'" :disabled-date="getDisabledDateFn(item)"
                        style="width: 100%" @change="(val) => handleModelChange(val, item)" />

                    <!-- Checkbox Group -->
                    <el-checkbox-group v-else-if="item.type === 'checkbox-group'" v-model="localModel[item.prop]"
                        @change="(val) => handleModelChange(val, item)">
                        <el-checkbox v-for="opt in getSelectOptions(item)" :key="opt.value" :label="opt.value">
                            {{ opt.label }}
                        </el-checkbox>
                    </el-checkbox-group>
                    <!-- textarea -->
                    <el-input v-else-if="item.type === 'textarea'" v-model="localModel[item.prop]" style="width: 240px"
                        autosize type="textarea" placeholder="请输入" :clearable="true"
                        @change="(val) => handleModelChange(val, item)" />
                    <span v-else>不支持的类型: {{ item.type }}</span>
                </el-form-item>
            </el-col>
        </el-row>
    </el-form>
</template>

<script setup>
import { ref, watch, onMounted, nextTick, computed } from 'vue'
import { ElMessage } from 'element-plus'

// 🔑 引入你的字典方法
import useDictStore from '@/store/modules/dict'
import { getDicts } from '@/api/system/dict/data'
import request from '@/utils/request'
import dayjs from 'dayjs'
// ==================== 工具函数：深拷贝配置（保留函数） ====================
function deepCloneConfig(obj) {
    if (obj === null || typeof obj !== 'object') return obj
    if (Array.isArray(obj)) return obj.map(deepCloneConfig)
    const cloned = {}
    for (const key in obj) {
        if (Object.prototype.hasOwnProperty.call(obj, key)) {
            const val = obj[key]
            cloned[key] = typeof val === 'function' ? val : deepCloneConfig(val)
        }
    }
    return cloned
}
function parseToDate(str) {
    if (!str) return null
    if (str === 'today') {
        return dayjs().startOf('day')
    }
    if (typeof str === 'string') {
        const d = dayjs(str)
        return d.isValid() ? d.startOf('day') : null
    }
    if (str instanceof Date) {
        return dayjs(str).startOf('day')
    }
    return null
}
// ==================== 生成 disabledDate 函数 ====================
function getDisabledDateFn(item) {
    const { minDate, maxDate } = item

    // 如果都没有限制，返回 null（不禁用任何日期）
    if (minDate == null && maxDate == null) {
        return () => false
    }

    return (date) => {
        const currentDate = dayjs(date).startOf('day')
        let minD = null
        let maxD = null

        // 解析最小日期
        if (minDate != null) {
            if (typeof minDate === 'function') {
                const val = minDate(localModel.value)
                minD = parseToDate(val)
            } else {
                minD = parseToDate(minDate)
            }
        }

        // 解析最大日期
        if (maxDate != null) {
            if (typeof maxDate === 'function') {
                const val = maxDate(localModel.value)
                maxD = parseToDate(val)
            } else {
                maxD = parseToDate(maxDate)
            }
        }

        // 判断是否被禁用
        if (minD && currentDate.isBefore(minD)) {
            return true
        }
        if (maxD && currentDate.isAfter(maxD)) {
            return true
        }
        return false
    }
}
// ==================== Props & Emits ====================
const props = defineProps({
    modelValue: { type: Object, default: () => ({}) },
    config: { type: Array, default: () => [] },
    isSearch: { type: Boolean, default: false }
})

const emit = defineEmits(['update:modelValue', 'update'])

// ==================== Refs ====================
const formRef = ref(null)
// 使用 shallowRef 避免深层响应式（表单通常扁平）
const localModel = ref({ ...props.modelValue })
// 记录哪些字段的字典已加载
const dictLoaded = ref(new Set())
const internalConfig = ref([])
const remoteOptions = ref({}) // { prop: [options] }
const remoteLoading = ref({}) // { prop: boolean }

// ==================== 条件显隐 & 规则 ====================
const visibleConfig = computed(() => {
    return internalConfig.value.filter(item => {
        if (typeof item.visible === 'function') {
            return item.visible(localModel.value)
        }
        return true
    })
})

const formRules = computed(() => {
    const rules = {}
    visibleConfig.value.forEach(item => {
        if (item.rules) rules[item.prop] = item.rules
    })
    return rules
})

// 1. 外部 modelValue 变化时，安全同步（仅当内容不同时）
// 监听 config 变化（支持动态 config）
watch(
    () => props.config,
    (newConfig) => {
        if (!newConfig || newConfig.length === 0) return

        internalConfig.value = deepCloneConfig(newConfig)

        // 构建初始模型
        const initialModel = {}
        for (const item of internalConfig.value) {
            const key = item.prop
            // 优先用父传值，否则用默认值
            if (props.modelValue?.[key] !== undefined) {
                initialModel[key] = props.modelValue[key]
            } else if (item.multiple || ['checkbox-group', 'daterange'].includes(item.type)) {
                initialModel[key] = item.defaultValue ?? []
            } else {
                initialModel[key] = item.defaultValue ?? ''
            }
        }

        // ✅ 在这里同步 modelValue（包括 extra 字段）
        localModel.value = syncModelFromProps(props.modelValue, internalConfig.value)
    },
    { immediate: true }
)
console.log('🚀 子组件 props.modelValue 初始值:', props.modelValue)

// 监听 modelValue（用于后续外部更新）
watch(
    () => props.modelValue,
    (newVal) => {
        if (!newVal || !internalConfig.value) return
        // ✅ 同样使用 sync 函数
        localModel.value = syncModelFromProps(newVal, internalConfig.value)
    },
    { deep: true }
)

// 提取同步逻辑
function syncModelFromProps(newModelValue, newConfig) {
    if (!newModelValue || !newConfig) return {}

    const synced = {}

    // 1️⃣ 同步主字段（config 中定义的 prop）
    for (const item of newConfig) {
        const key = item.prop
        if (newModelValue.hasOwnProperty(key)) {
            synced[key] = newModelValue[key]
        } else if (item.multiple || ['checkbox-group', 'daterange'].includes(item.type)) {
            synced[key] = item.defaultValue ?? []
        } else {
            synced[key] = item.defaultValue ?? ''
        }
    }

    // 2️⃣ 同步 extra 字段（从主字段的 raw 中提取）
    for (const item of newConfig) {
        const sourceField = item.prop
        const extraMap = item.onChangeExtraFields
        if (!extraMap || typeof extraMap !== 'object') continue

        const sourceObj = newModelValue[sourceField]
        if (!sourceObj || typeof sourceObj !== 'object') continue

        for (const [targetKey, subPath] of Object.entries(extraMap)) {
            const val = getNestedValue(sourceObj, subPath)
            if (val !== undefined) {
                synced[targetKey] = val
            }
        }
    }

    // ✅ 3️⃣ 【关键】保留 localModel 中已有的、未被覆盖的 extra 字段
    for (const key in localModel.value) {
        // 如果这个 key 不是主字段（不在 config 中），且 synced 没有覆盖它
        if (
            !synced.hasOwnProperty(key) &&
            !newConfig.some(item => item.prop === key)
        ) {
            // 说明它是 extra 字段，且这次同步没提供新值 → 保留旧值
            synced[key] = localModel.value[key]
        }
    }

    return synced
}

function getNestedValue(obj, path) {
    return path.split('.').reduce((current, key) => current?.[key], obj)
}
// 当字典加载完成时，触发同步
function markDictLoaded(prop) {
    dictLoaded.value.add(prop)
    // 尝试同步该字段
    if (props.modelValue?.[prop] !== undefined) {
        localModel.value[prop] = props.modelValue[prop]
    }
}
// 2. 用户操作导致 localModel 变化时，emit（防抖可选）

function handleModelChange(value, item) {
  console.group('🔄 handleModelChange')
  console.log('输入 value:', value)
  console.log('item:', item)
  console.log('当前 localModel:', localModel.value)

  const newModel = { ...localModel.value, [item.prop]: value }

  if (item?.type === 'select' && item.onChangeExtraFields) {
    const options = getSelectOptions(item)
    console.log('可用 options:', options)
    const opt = options.find(o => o.value === value)
    console.log('匹配的 opt:', opt)
    if (opt?.raw) {
      for (const [targetProp, sourceKey] of Object.entries(item.onChangeExtraFields)) {
        const extraValue = opt.raw[sourceKey]
        newModel[targetProp] = extraValue
        console.log(`✅ 设置 ${targetProp} =`, extraValue)
      }
    }
  }

  console.log('🆕 newModel:', newModel)
  console.log('📦 props.modelValue:', props.modelValue)
  console.log('isEqualShallow?', isEqualShallow(props.modelValue, newModel))

  localModel.value = newModel

  nextTick(() => {
    if (!isEqualShallow(props.modelValue, newModel)) {
      console.log('📤 emit update:modelValue:', newModel)
      emit('update:modelValue', newModel)
    } else {
      console.log('🚫 跳过 emit：认为相等')
    }
  })
  console.groupEnd()
}
// 辅助函数：浅比较两个对象
function isEqualShallow(a, b) {
    const keysA = Object.keys(a)
    const keysB = Object.keys(b)
    if (keysA.length !== keysB.length) return false
    for (let key of keysA) {
        if (a[key] !== b[key]) return false
    }
    return true
}

// ==================== 加载字典选项 ====================
async function loadDictOptions(dictType) {
    const dictStore = useDictStore()
    let options = dictStore.getDict(dictType)

    if (options && options.length > 0) {
        return options
    }

    try {
        const resp = await getDicts(dictType)
        options = resp.data.map(p => ({
            label: p.itemLabel,
            value: p.itemValue,
            raw: p
        }))
        dictStore.setDict(dictType, options)
        return options
    } catch (err) {
        console.error(`加载字典 ${dictType} 失败`, err)
        ElMessage.error(`字典 ${dictType} 加载失败`)
        return []
    }
}

// ==================== 初始化 ====================
onMounted(async () => {
    internalConfig.value = deepCloneConfig(props.config)

    const initialData = {}
    const dictTypePromises = []
    const apiPromises = [] // ← 新增：收集 api 加载 promise
    for (const item of internalConfig.value) {
        const key = item.prop
        if (localModel.value[key] == null) {
            if (item.multiple) {
                initialData[key] = item.defaultValue ?? []
            } else if (['checkbox-group', 'daterange'].includes(item.type)) {
                initialData[key] = item.defaultValue ?? []
            } else {
                initialData[key] = item.defaultValue ?? ''
            }
        }

        // 预加载 dictType
        if (item.type === 'select' && item.dictType) {
            dictTypePromises.push(
                loadDictOptions(item.dictType).then(opts => {
                    remoteOptions.value[key] = opts
                    markDictLoaded(key) // ← 立即标记已加载
                })
            )
        } // 预加载 api（远程接口）← 关键新增！
        else if (item.type === 'select' && item.api) {
            apiPromises.push(
                loadRemoteOptionsForInit(item) // ← 专门用于初始化的加载函数
            )
        } else if (item.type === 'select' && item.options) {
            remoteOptions.value[key] = [...item.options]
            markDictLoaded(key)
        }
        // api 类型：延迟加载（focus 时）
    }

    if (Object.keys(initialData).length > 0) {
        localModel.value = { ...localModel.value, ...initialData }
    }

    // 等待所有字典加载完成
    await Promise.allSettled(dictTypePromises)
})

// ==================== 获取 select 选项 ====================
function getSelectOptions(item) {
    const key = item.prop
    // 字典选项
    if (item.dictType || item.api) {
        // 字典选项
        return (remoteOptions.value[key] || []).map(opt => ({
            value: opt.value,
            label: opt.label,
            raw: opt.raw // ← 必须存 raw
        }))
    } else if (item.options) {
        // 静态选项s
        return item.options.map(opt => ({
            value: opt.value,
            label: opt.label,
            raw: opt.raw // 保留原始
        }))
    }
    return []
}
async function loadRemoteOptionsForInit(item) {
    const { prop, api, requestParams = {} } = item
    try {
        // 构造请求体：只传 requestParams，不传 keyword
        const payload = {
            ...(requestParams || {})
        }

        const res = await request({
            url: api,
            method: 'post',
            data: payload
        })

        const list = typeof item.transform === 'function'
            ? item.transform(res)
            : res.data?.records || res.data || []

        // 建议：统一转成字符串（或根据 item.valueType 判断）
        const newOptions = list.map(i => ({
            value: String(i[item.valueKey || 'value']), // ← 强制转字符串
            label: i[item.labelKey || 'label'],
            raw: i
        }))

        remoteOptions.value[prop] = newOptions
        markDictLoaded(prop) // ← 关键：标记已加载
    } catch (err) {
        ElMessage.error(`预加载 ${item.label} 失败`)
        remoteOptions.value[prop] = []
    }
}
// ==================== 加载远程 API 选项（首次 focus 时加载，无搜索词） ====================
async function loadRemoteOptions(item) {
    const { prop, api, requestParams = {}, keywordField, debounceWait, ...rest } = item
    if (!api || remoteOptions.value[prop]?.length > 0) return

    try {
        remoteLoading.value[prop] = true

        // 构造请求体：合并 requestParams + 分页（默认第一页）
        const payload = {
            ...(requestParams || {})
            // 注意：此时无 keyword，所以不加 keywordField
        }

        const res = await request({
            url: api,
            method: 'post', // ← 改为 POST
            data: payload   // ← 参数放 body
        })

        const list = typeof item.transform === 'function'
            ? item.transform(res)
            : res.data?.records || res.data || []

        // 建议：统一转成字符串（或根据 item.valueType 判断）
        const newOptions = list.map(i => ({
            value: String(i[item.valueKey || 'value']), // ← 强制转字符串
            label: i[item.labelKey || 'label'],
            raw: i
        }))
        remoteOptions.value[prop] = newOptions
        // ✅ 关键：标记该字段字典已加载
        markDictLoaded(prop)
    } catch (err) {
        ElMessage.error(`加载 ${item.label} 失败`)
        remoteOptions.value[prop] = []
    } finally {
        remoteLoading.value[prop] = false
    }
}

// ==================== 远程搜索（带关键词，防抖） ====================
let searchTimeout = null
function handleFilterChange(keyword, item) {
    const { prop, api, requestParams = {}, keywordField = 'keyword', debounceWait = 300 } = item
    if (!api) return

    clearTimeout(searchTimeout)
    searchTimeout = setTimeout(async () => {
        try {
            remoteLoading.value[prop] = true

            // 构造请求体：requestParams + 分页 + 动态关键词字段
            const payload = {
                ...(requestParams || {}),
                [keywordField]: keyword // ← 动态字段名，如 name / companyName
            }

            const res = await request({
                url: api,
                method: 'post', // ← POST 请求
                data: payload   // ← body 传参
            })

            const list = typeof item.transform === 'function'
                ? item.transform(res)
                : res.data?.records || res.data || []

            remoteOptions.value[prop] = list.map(i => ({
                value: i[item.valueKey || 'value'],
                label: i[item.labelKey || 'label'],
                raw: i // ← 保存完整对象
            }))
        } catch (err) {
            ElMessage.error(`搜索 ${item.label} 失败`)
        } finally {
            remoteLoading.value[prop] = false
        }
    }, debounceWait)
}

// ==================== 数字输入处理 ====================
function handleNumberInput(value, item) {
    const { inputType = 'text', decimalDigits = 2, prop } = item
    if (!prop) return

    let result = String(value ?? '').trim()

    if (inputType === 'integer') {
        // 只保留数字
        result = result.replace(/[^\d]/g, '')
    } else if (inputType === 'decimal') {
        // 1. 只保留数字和小数点
        result = result.replace(/[^\d.]/g, '')

        // 2. 去掉开头的小数点（不允许 ".5" → 改为 "0.5" 更好，但这里先简单处理）
        if (result.startsWith('.')) {
            result = '0.' + result.slice(1)
        }

        // 3. 保证最多一个小数点
        const parts = result.split('.')
        if (parts.length > 2) {
            result = parts[0] + '.' + parts.slice(1).join('')
        }

        // 4. 限制小数位数（但保留结尾的小数点！）
        if (result.includes('.')) {
            const [intPart, decPart] = result.split('.')
            // 如果小数部分超过限制，截断
            if (decPart.length > decimalDigits) {
                result = intPart + '.' + decPart.slice(0, decimalDigits)
            }
            // ✅ 不再删除结尾的 '.'
        }
    }

    // 防止重复赋值（可选优化）
    if (localModel.value[prop] !== result) {
        localModel.value = { ...localModel.value, [prop]: result }
    }
}

// ==================== 暴露方法 ====================
defineExpose({
    getFormData() {
        return { ...localModel.value }
    },
    async validate() {
        return new Promise((resolve, reject) => {
            formRef.value?.validate((valid) => {
                if (valid) resolve(localModel.value)
                else reject(new Error('Validation failed'))
            })
        })
    },
    resetForm() {
        const resetData = {}
        internalConfig.value.forEach(item => {
            const key = item.prop
            if (['checkbox-group', 'daterange'].includes(item.type) || item.multiple) {
                resetData[key] = item.defaultValue ?? []
            } else {
                resetData[key] = item.defaultValue ?? ''
            }
        })
        localModel.value = { ...resetData }
        nextTick(() => formRef.value?.clearValidate())
    }
})
</script>

<style scoped>
.search-form-item {
    margin-bottom: 20px;
}
</style>