<script setup lang="ts">
import type { WritableComputedRef } from "vue"
import type { FormItemRule } from "element-plus/es/components/form/src/types"
import type { VbFormItem } from "@@@/form/models"
import getConfigRule, { RULE_KEYS } from "@@/config/rules"

const props = withDefaults(
	defineProps<{
		data: any
		item: VbFormItem
		labelWidth?: number | string
		rules?: Record<string, FormItemRule[]>
		span?: number
		size?: "large" | "default" | "small"
	}>(),
	{
		labelWidth: 80,
		span: 24,
		size: "default"
	}
)
const emits = defineEmits<(e: "update:data", v: any) => void>()
const { item } = toRefs(props)

// 双向绑定的数据
const data: WritableComputedRef<any> = computed({
	get(): any {
		return typeof props.data === "function" ? props.data() : props.data
	},
	set(value: any): void {
		emits("update:data", value)
	}
})

// 动态解析值
const resolveDynamicValue = <T,>(
	value: T | ((v?: any) => T) | undefined,
	data?: any
): T | undefined => {
	if (typeof value === "function") {
		return (value as Function)(data)
	}
	return value
}

// 计算属性
const itemId = computed(() => resolveDynamicValue<string | undefined>(item.value.id, undefined))
const itemData = computed(() => resolveDynamicValue(item.value.data, undefined))
const colSpan = computed(() => resolveDynamicValue(item.value.span, data.value) || props.span)
const isShow = computed(() => {
	if (item.value.show === undefined) return true
	return resolveDynamicValue(item.value.show, data.value)
})
const isRequired = computed(() => resolveDynamicValue(item.value.required, data.value) || false)
const isReadonly = computed(() => resolveDynamicValue(item.value.readonly, data.value) || false)
const isDisabled = computed(() => resolveDynamicValue(item.value.disabled, data.value) || false)
const itemType = computed(() => resolveDynamicValue(item.value.type, data.value))
const itemSize = computed(() => resolveDynamicValue(item.value.size, data.value) || props.size)
const elItemClass = computed(() => resolveDynamicValue(item.value.itemClass, data.value))
const itemClass = computed(() => resolveDynamicValue(item.value.class, data.value))
const elItemStyle = computed(() => resolveDynamicValue(item.value.itemStyle, data.value))
const itemStyle = computed(() => resolveDynamicValue(item.value.style, data.value))
const itemLabel = computed(() => resolveDynamicValue(item.value.label, data.value))
const itemPlaceholder = computed(
	() => resolveDynamicValue(item.value.placeholder, data.value) || itemLabel.value
)
const itemInnerText = computed(() => resolveDynamicValue(item.value.innerText, data.value))
const tipsContent = computed(() => resolveDynamicValue(item.value.tips, data.value))
const itemLabelWidth = computed(() => {
	const _lw = resolveDynamicValue(item.value.labelWidth, data.value) || props.labelWidth
	if (_lw && !isNaN(Number(_lw))) {
		return `${_lw}px`
	}
	return _lw
})
const itemLabelStyle = computed(() => resolveDynamicValue(item.value.labelStyle, data.value))
const itemListeners = computed(() => item.value.listeners || {})
// 获取验证规则
const getRules = (item: VbFormItem): FormItemRule[] => {
	const _rules: FormItemRule[] = []

	if (item.rules) {
		for (let i = 0; i < item.rules.length; i++) {
			const r = item.rules[i]
			let rule

			if (typeof r === "string") {
				rule = props.rules ? props.rules[r] : getConfigRule(r, item.ruleFormat?.[i])
			} else if (typeof r === "object") {
				rule = r
			}

			if (rule) {
				Array.isArray(rule) ? _rules.push(...rule) : _rules.push(rule)
			}
		}
	}

	// 添加默认的 required 规则
	if (isRequired.value && !_rules.find((v) => v.required === true)) {
		const rule = getConfigRule(RULE_KEYS.REQUIRED, [itemLabel.value?.toString() || ""])
		if (rule) {
			Array.isArray(rule) ? _rules.push(...rule) : _rules.push(rule)
		}
	}

	return _rules
}

// 检查组件类型
const checkComponent = (name: string): boolean => {
	const component = props.item?.component || "I"
	if (typeof component === "string") {
		return component.toUpperCase() === name.toUpperCase()
	}
	return (component as any)?.name === name
}

// 判断选项是否禁用
const isOptionDisable = (option: any): boolean => {
	if (typeof option.disabled === "function") {
		return option.disabled(option)
	}
	return option.disabled || false
}

// Tips 相关
const hasTips = computed(() => !!tipsContent.value)
const tipsIconClass = computed(() => {
	const icon = resolveDynamicValue(item.value.tipsIcon, data.value) || "bi bi-question-circle-fill"
	const iconClass = resolveDynamicValue(item.value.tipsIconClass, data.value) || "text-muted ms-1"
	return `${icon} ${iconClass}`
})

// Prepend 相关
const hasPrepend = computed(() => {
	if (resolveDynamicValue(item.value.prependDisabled, data.value) === true) {
		return false
	}
	return !!(item.value.prepend || item.value.prependIcon || item.value.prependClickFunc)
})
const hasPrependSlot = computed(
	() => item.value.prepend === "slot" || item.value.prepend === "Slot"
)
const prependIcon = computed(() => {
	if (item.value.prependIcon) {
		return resolveDynamicValue(item.value.prependIcon, data.value)
	}
	if (item.value.prepend && !["icon", "Icon"].includes(item.value.prepend)) {
		return ""
	}
	if (item.value.prependClickFunc || ["icon", "Icon"].includes(item.value.prepend || "")) {
		return "bi bi-search text-primary"
	}
	return ""
})
const prependText = computed(() => {
	if (item.value.prepend && !["slot", "Slot", "icon", "Icon"].includes(item.value.prepend)) {
		return item.value.prepend
	}
	return ""
})

// Append 相关
const hasAppend = computed(() => {
	if (resolveDynamicValue(item.value.appendDisabled, data.value) === true) {
		return false
	}
	return !!(item.value.append || item.value.appendIcon || item.value.appendClickFunc)
})
const hasAppendSlot = computed(() => item.value.append === "slot" || item.value.append === "Slot")
const appendIcon = computed(() => {
	if (item.value.appendIcon) {
		return resolveDynamicValue(item.value.appendIcon, data.value)
	}
	if (item.value.append && !["icon", "Icon"].includes(item.value.append)) {
		return ""
	}
	if (item.value.appendClickFunc || ["icon", "Icon"].includes(item.value.append || "")) {
		return "bi bi-search text-primary"
	}
	return ""
})
const appendText = computed(() => {
	if (item.value.append && !["slot", "Slot", "icon", "Icon"].includes(item.value.append)) {
		return item.value.append
	}
	return ""
})

// 事件处理
const handlePrependClick = () => {
	item.value.prependClickFunc?.(data.value)
}
const handleAppendClick = () => {
	item.value.appendClickFunc?.(data.value)
}
</script>
<template>
	<el-col v-if="isShow" :span="colSpan">
		<el-form-item
			:prop="item.field"
			:class="elItemClass"
			:style="elItemStyle"
			:required="isRequired"
			:rules="getRules(item)"
			:label="itemLabel"
			:label-width="itemLabelWidth"
			v-bind="item.itemProps">
			<template #label v-if="hasTips">
				<span class="fw-bold text-dark" :style="itemLabelStyle">{{ itemLabel }}</span>
				<vb-tooltip :content="tipsContent" placement="top">
					<span :class="tipsIconClass" style="cursor: pointer"></span>
				</vb-tooltip>
			</template>
			<slot v-if="checkComponent('slot')" :name="item.field"></slot>
			<div
				v-else-if="checkComponent('text') || checkComponent('innerText')"
				:id="itemId"
				:class="itemClass"
				:style="itemStyle">
				{{ itemInnerText || data[item.field] }}
			</div>
			<template v-else-if="checkComponent('I') || checkComponent('ElInput')">
				<el-input
					v-model="data[item.field]"
					:id="itemId"
					:required="isRequired"
					:type="itemType || 'text'"
					:class="itemClass"
					:style="itemStyle"
					:placeholder="itemPlaceholder || itemLabel"
					:size="itemSize"
					:disabled="isDisabled"
					:readonly="isReadonly"
					v-bind="item.props"
					v-on="itemListeners">
					<template #prepend v-if="hasPrepend">
						<slot v-if="hasPrependSlot" :name="`${item.field}_prepend`"></slot>
						<el-button v-else-if="prependIcon" @click="handlePrependClick">
							<i :class="prependIcon"></i>
						</el-button>
						<span v-else @click="handlePrependClick">{{ prependText }}</span>
					</template>
					<template #append v-if="hasAppend">
						<slot v-if="hasAppendSlot" :name="`${item.field}_append`"></slot>
						<el-button v-else-if="appendIcon" @click="handleAppendClick">
							<i :class="appendIcon"></i>
						</el-button>
						<span v-else @click="handleAppendClick">{{ appendText }}</span>
					</template>
				</el-input>
			</template>
			<template v-else-if="checkComponent('VS') || checkComponent('VbSelect')">
				<vb-select
					v-model="data[item.field]"
					:id="itemId"
					:type="(itemType || 'select') as any"
					:required="isRequired"
					:class="itemClass"
					:style="itemStyle"
					:placeholder="itemPlaceholder || itemLabel"
					:size="itemSize"
					:data="itemData"
					:disabled="isDisabled"
					:readonly="isReadonly"
					v-bind="item.props"
					v-on="itemListeners"></vb-select>
			</template>
			<template v-else-if="checkComponent('VU') || checkComponent('VbUpload')">
				<vb-upload
					v-model="data[item.field]"
					:id="itemId"
					:required="isRequired"
					:class="itemClass"
					:style="itemStyle"
					:placeholder="itemPlaceholder || itemLabel"
					:size="itemSize"
					:data="itemData"
					:disabled="isDisabled"
					:readonly="isReadonly"
					v-bind="item.props"
					v-on="itemListeners"></vb-upload>
			</template>
			<template v-else-if="checkComponent('Dict') || checkComponent('DictSelect')">
				<dict-select
					v-model="data[item.field]"
					:id="itemId"
					:dictType="item.props?.dictType || ''"
					:type="(itemType || 'select') as any"
					:required="isRequired"
					:class="itemClass"
					:style="itemStyle"
					:placeholder="itemPlaceholder || itemLabel"
					:size="itemSize"
					:disabled="isDisabled"
					:readonly="isReadonly"
					v-bind="item.props"
					v-on="itemListeners"></dict-select>
			</template>
			<template v-else-if="checkComponent('S') || checkComponent('ElSelect')">
				<el-select
					v-model="data[item.field]"
					:id="itemId"
					:required="isRequired"
					:class="itemClass"
					:style="itemStyle"
					:placeholder="itemPlaceholder || itemLabel"
					:size="itemSize"
					:disabled="isDisabled"
					:readonly="isReadonly"
					v-bind="item.props"
					v-on="itemListeners">
					<el-option
						v-for="(select, selectIndex) in itemData"
						:key="selectIndex"
						:value="select.value"
						:label="select.label"
						:class="select.class"
						:disabled="isOptionDisable(select)"
						:style="select.style"></el-option>
				</el-select>
			</template>
			<template v-else-if="checkComponent('R') || checkComponent('ElRadioGroup')">
				<el-radio-group
					v-model="data[item.field]"
					:id="itemId"
					:required="isRequired"
					:class="itemClass"
					:style="itemStyle"
					:size="itemSize"
					:disabled="isDisabled"
					:readonly="isReadonly"
					v-bind="item.props"
					v-on="itemListeners">
					<el-radio
						v-for="(radio, radioIndex) in itemData"
						:key="radioIndex"
						:value="radio.value"
						:class="radio.class"
						:style="radio.style"
						:disabled="isOptionDisable(radio)">
						{{ radio.label }}
					</el-radio>
				</el-radio-group>
			</template>
			<template v-else-if="checkComponent('C') || checkComponent('ElCheckboxGroup')">
				<el-checkbox-group
					v-model="data[item.field]"
					:id="itemId"
					:required="isRequired"
					:class="itemClass"
					:style="itemStyle"
					:size="itemSize"
					:disabled="isDisabled"
					:readonly="isReadonly"
					v-bind="item.props"
					v-on="itemListeners">
					<el-checkbox
						v-for="(checkbox, checkboxIndex) in itemData"
						:key="checkboxIndex"
						:value="checkbox.value"
						:class="checkbox.class"
						:style="checkbox.style"
						:disabled="isOptionDisable(checkbox)">
						{{ checkbox.label }}
					</el-checkbox>
				</el-checkbox-group>
			</template>
			<template v-else-if="checkComponent('D') || checkComponent('ElDatePicker')">
				<el-date-picker
					v-model="data[item.field]"
					:id="itemId"
					:required="isRequired"
					:class="itemClass"
					:style="itemStyle"
					:size="itemSize"
					:disabled="isDisabled"
					:readonly="isReadonly"
					v-bind="item.props"
					v-on="itemListeners"></el-date-picker>
			</template>
			<template v-else-if="checkComponent('VST') || checkComponent('VbSelectTree')">
				<vb-select-tree
					v-model="data[item.field]"
					:id="itemId"
					:required="isRequired"
					:data="itemData"
					:placeholder="itemPlaceholder || itemLabel"
					:class="itemClass"
					:style="itemStyle"
					:size="itemSize"
					:disabled="isDisabled"
					:readonly="isReadonly"
					v-bind="item.props"
					v-on="itemListeners"></vb-select-tree>
			</template>
			<template v-else-if="checkComponent('VE') || checkComponent('VbEditor')">
				<vb-editor
					v-model="data[item.field]"
					:id="itemId"
					:required="isRequired"
					:class="itemClass"
					:style="itemStyle"
					:placeholder="itemPlaceholder || itemLabel"
					:size="itemSize"
					:data="itemData"
					:disabled="isDisabled"
					:readonly="isReadonly"
					v-bind="item.props"
					v-on="itemListeners"></vb-editor>
			</template>
			<template v-else>
				<component
					:is="item.component"
					v-model="data[item.field]"
					v-model:value="data[item.field]"
					:id="itemId"
					:required="isRequired"
					:class="itemClass"
					:style="itemStyle"
					:size="itemSize"
					:placeholder="itemPlaceholder || itemLabel"
					:disabled="isDisabled"
					:readonly="isReadonly"
					v-bind="item.props"
					v-on="itemListeners"></component>
			</template>
		</el-form-item>
	</el-col>
</template>
