<template>
	<el-scrollbar class="w-full box-border p-12px">
		<div class="start-setter">
			<div class="w-full flex items-center justify-between beautify">
				<label class="text-14px font-bold text-gray-700">{{ texts.input }}</label>
				<IconButton link icon="lucide:plus" class="text-#296dff" @click="handleAddVariable" />
			</div>

			<div v-if="!formData.variables?.length" class="empty-state">
				<div class="empty-desc">{{ texts.empty }}</div>
			</div>

			<VueDraggable
				v-model="formData.variables"
				:animation="150"
				handle=".handle"
				class="variable-list"
			>
				<div
					v-for="(variable, index) in formData.variables"
					:key="`${variable.name || 'variable'}-${index}`"
					class="variable-card"
				>
					<div class="variable-left">
						<Icon icon="lucide:grip-vertical" :size="16" class="handle drag-icon" />
						<div class="field-icon">
							<Icon :icon="getFieldIcon(variable.formType)" :size="16" />
						</div>
						<div class="min-w-0">
							<div class="variable-title">
								<span>{{ variable.name || `field_${index + 1}` }}</span>
								<span class="title-separator">/</span>
								<span>{{
									variable.label ||
									variable.name ||
									t('pages.startSetter.fieldFallback', { index: index + 1 })
								}}</span>
								<span v-if="variable.formType === 'file-list'" class="legacy-badge">
									{{ texts.legacyBadge }}
								</span>
							</div>
							<div class="variable-subtitle">
								<span>{{ getFormTypeLabel(variable.formType) }}</span>
								<span v-if="supportsMaxLength(variable.formType)">
									{{ texts.maxLength }}: {{ variable.max_length ?? texts.none }}
								</span>
								<span v-if="variable.is_hide">{{ texts.hidden }}</span>
							</div>
						</div>
					</div>

					<div class="variable-right">
						<span v-if="variable.is_require" class="required-tag">{{ texts.required }}</span>
						<span class="type-tag">{{ getValueTypeLabel(variable.formType) }}</span>
						<span class="flex items-center ml-8px!">
							<IconButton
								link
								icon="lucide:pencil"
								class="text-#667085"
								@click="handleEditVariable(index)"
							/>
							<IconButton
								link
								icon="lucide:trash-2"
								class="text-#f04438 ml-0!"
								@click="handleRemoveVariable(index)"
							/>
						</span>
					</div>
				</div>
			</VueDraggable>
			<NodeRuntimeConfig v-model="formData" />
		</div>
	</el-scrollbar>

	<el-dialog
		v-model="dialogVisible"
		:title="editingIndex === -1 ? texts.addVariableTitle : texts.editVariableTitle"
		width="640px"
		append-to-body
		:close-on-click-modal="false"
	>
		<el-form ref="dialogFormRef" :model="dialogForm" :rules="dialogRules" label-position="top">
			<el-form-item :label="texts.fieldType" prop="formType">
				<div class="w-full type-picker pl-2px!">
					<el-select
						v-model="dialogForm.formType"
						style="flex: 1"
						:placeholder="texts.fieldTypePlaceholder"
						@change="handleFormTypeChange"
					>
						<el-option
							v-for="item in formTypeOptions"
							:key="item.value"
							:label="item.label"
							:value="item.value"
						/>
					</el-select>
					<div class="type-preview">
						<div>
							<div class="type-preview__title">{{ getFormTypeLabel(dialogForm.formType) }}</div>
							<div class="type-preview__desc">{{ getValueTypeLabel(dialogForm.formType) }}</div>
						</div>
						<div class="field-icon field-icon--large">
							<Icon :icon="getFieldIcon(dialogForm.formType)" :size="18" />
						</div>
					</div>
				</div>
			</el-form-item>

			<el-form-item :label="texts.variableName" prop="name">
				<el-input
					v-model="dialogForm.name"
					:placeholder="texts.variableNamePlaceholder"
					clearable
				/>
			</el-form-item>

			<el-form-item :label="texts.displayName" prop="label">
				<el-input
					v-model="dialogForm.label"
					:placeholder="texts.displayNamePlaceholder"
					clearable
				/>
			</el-form-item>

			<el-form-item
				v-if="supportsMaxLength(dialogForm.formType)"
				:label="texts.maxLength"
				prop="max_length"
			>
				<div class="w-full">
					<el-input-number v-model="dialogForm.max_length" :min="1" :max="5000" class="w-full" />
				</div>
			</el-form-item>

			<el-form-item v-if="dialogForm.formType === 'select'" :label="texts.options" prop="options">
				<el-input-tag
					v-model="dialogForm.options"
					:placeholder="texts.optionsPlaceholder"
					:aria-label="texts.optionsPlaceholder"
				/>
			</el-form-item>

			<el-form-item v-if="supportsFileConfig(dialogForm.formType)" :label="texts.fileTypes">
				<el-select
					v-model="dialogForm.file_types"
					multiple
					class="w-full"
					:placeholder="texts.fileTypesPlaceholder"
					@change="handleFileTypesChange"
				>
					<el-option
						v-for="item in fileTypeOptions"
						:key="item.value"
						:label="item.label"
						:value="item.value"
					/>
				</el-select>
			</el-form-item>

			<el-form-item
				v-if="supportsFileConfig(dialogForm.formType) && dialogForm.file_types?.includes('custom')"
				:label="texts.fileExtensions"
				prop="file_extensions"
			>
				<el-input-tag
					v-model="dialogForm.file_extensions"
					:placeholder="texts.fileExtensionsPlaceholder"
					:aria-label="texts.fileExtensionsAria"
				/>
			</el-form-item>

			<el-form-item
				v-if="dialogForm.formType !== 'json_object'"
				:label="texts.defaultValue"
				prop="default_value"
			>
				<div class="w-full">
					<el-input
						v-if="isStringDefaultEditor(dialogForm.formType)"
						v-model="stringDefaultValue"
						:type="dialogForm.formType === 'text-area' ? 'textarea' : 'text'"
						:rows="dialogForm.formType === 'text-area' ? 4 : undefined"
						:placeholder="texts.defaultValuePlaceholder"
					/>
					<div v-else-if="dialogForm.formType === 'number'" class="w-full">
						<el-input-number
							v-model="numberDefaultValue"
							controls-position="right"
							class="w-full"
						/>
					</div>
					<div v-else-if="dialogForm.formType === 'checkbox'" class="switch-line">
						<el-switch v-model="booleanDefaultValue" />
						<span>{{ booleanDefaultValue ? texts.booleanTrue : texts.booleanFalse }}</span>
					</div>
					<FileUploadInput
						v-else-if="dialogForm.formType === 'file'"
						v-model="singleFileDefaultValue"
						:file-types="dialogForm.file_types || []"
						:file-extensions="dialogForm.file_extensions || []"
						:allow-link-input="false"
					/>
					<FileUploadInput
						v-else-if="dialogForm.formType === 'file-list'"
						v-model="fileListDefaultValue"
						multiple
						:file-types="dialogForm.file_types || []"
						:file-extensions="dialogForm.file_extensions || []"
						:allow-link-input="false"
					/>
				</div>
			</el-form-item>

			<el-form-item v-else :label="texts.jsonSchema">
				<div class="w-full">
					<CodeEditor v-model="dialogForm.jsonText" :tools="false" language="json" />
				</div>
			</el-form-item>

			<div class="check-line">
				<el-checkbox v-model="dialogForm.is_require">{{ texts.required }}</el-checkbox>
			</div>
			<div class="check-line">
				<el-checkbox v-model="dialogForm.is_hide">{{ texts.hidden }}</el-checkbox>
			</div>
		</el-form>

		<template #footer>
			<div class="dialog-footer">
				<el-button @click="handleCloseDialog">{{ texts.cancel }}</el-button>
				<el-button type="primary" @click="handleSaveVariable">{{ texts.save }}</el-button>
			</div>
		</template>
	</el-dialog>
</template>

<script setup lang="ts">
import { computed, reactive, ref } from 'vue'
import { ElMessage, type FormInstance, type FormRules } from 'element-plus'
import { cloneDeep } from 'lodash-es'
import { Icon, IconButton } from '@repo/ui'
import { VueDraggable } from 'vue-draggable-plus'

import NodeRuntimeConfig from '@/nodes/_base/NodeRuntimeConfig.vue'
import { useSetterModel } from '../_shared/useSetterModel'

import FileUploadInput from '@/features/fileUpload/FileUploadInput.vue'
import { useI18n } from '@/composables/useI18n'

import type { WorkflowUploadFile } from '@/features/fileUpload/shared'
import type { FileType, FormType, StartData, StartVariable } from './index'

interface Emits {
	(e: 'update', value: StartData): void
}

interface StartVariableEditor extends StartVariable {
	defaultValueText: string
	jsonText: string
}

const props = defineProps<{
	data: StartData
}>()

const emit = defineEmits<Emits>()
const { t } = useI18n()

const texts = computed(() => ({
	input: t('pages.startSetter.input'),
	empty: t('pages.startSetter.empty'),
	field: t('pages.startSetter.field'),
	maxLength: t('pages.startSetter.maxLength'),
	none: t('pages.startSetter.none'),
	hidden: t('pages.startSetter.hidden'),
	required: t('pages.startSetter.required'),
	addVariableTitle: t('pages.startSetter.addVariableTitle'),
	editVariableTitle: t('pages.startSetter.editVariableTitle'),
	fieldType: t('pages.startSetter.fieldType'),
	fieldTypePlaceholder: t('pages.startSetter.fieldTypePlaceholder'),
	variableName: t('pages.startSetter.variableName'),
	variableNamePlaceholder: t('pages.startSetter.variableNamePlaceholder'),
	displayName: t('pages.startSetter.displayName'),
	displayNamePlaceholder: t('pages.startSetter.displayNamePlaceholder'),
	options: t('pages.startSetter.options'),
	optionsPlaceholder: t('pages.startSetter.optionsPlaceholder'),
	fileTypes: t('pages.startSetter.fileTypes'),
	fileTypesPlaceholder: t('pages.startSetter.fileTypesPlaceholder'),
	fileExtensions: t('pages.startSetter.fileExtensions'),
	fileExtensionsPlaceholder: t('pages.startSetter.fileExtensionsPlaceholder'),
	fileExtensionsAria: t('pages.startSetter.fileExtensionsAria'),
	defaultValue: t('pages.startSetter.defaultValue'),
	defaultValuePlaceholder: t('pages.startSetter.defaultValuePlaceholder'),
	cancel: t('common.cancel'),
	save: t('common.save'),
	text: t('pages.startSetter.text'),
	textarea: t('pages.startSetter.textarea'),
	select: t('pages.startSetter.select'),
	number: t('pages.startSetter.number'),
	boolean: t('pages.startSetter.boolean'),
	file: t('pages.startSetter.file'),
	fileList: t('pages.startSetter.fileList'),
	jsonObject: t('pages.startSetter.jsonObject'),
	document: t('pages.startSetter.document'),
	image: t('pages.startSetter.image'),
	audio: t('pages.startSetter.audio'),
	video: t('pages.startSetter.video'),
	custom: t('pages.startSetter.custom'),
	selectShort: t('pages.startSetter.selectShort'),
	selectFieldType: t('pages.startSetter.selectFieldType'),
	enterVariableName: t('pages.startSetter.enterVariableName'),
	variableNamePattern: t('pages.startSetter.variableNamePattern'),
	variableNameDuplicate: t('pages.startSetter.variableNameDuplicate'),
	enterDisplayName: t('pages.startSetter.enterDisplayName'),
	displayNameDuplicate: t('pages.startSetter.displayNameDuplicate'),
	atLeastOneOption: t('pages.startSetter.atLeastOneOption'),
	customExtensionRequired: t('pages.startSetter.customExtensionRequired'),
	defaultJsonInvalid: t('pages.startSetter.defaultJsonInvalid'),
	jsonConfigInvalid: t('pages.startSetter.jsonConfigInvalid'),
	variableAdded: t('pages.startSetter.variableAdded'),
	variableUpdated: t('pages.startSetter.variableUpdated'),
	jsonSchema: t('pages.startSetter.jsonSchema'),
	fieldFallback: t('pages.startSetter.fieldFallback'),
	booleanTrue: t('pages.startSetter.booleanTrue'),
	booleanFalse: t('pages.startSetter.booleanFalse'),
	legacyBadge: t('pages.startSetter.legacyBadge'),
	valueTypeString: t('pages.startSetter.valueTypeString'),
	valueTypeNumber: t('pages.startSetter.valueTypeNumber'),
	valueTypeBoolean: t('pages.startSetter.valueTypeBoolean'),
	valueTypeFile: t('pages.startSetter.valueTypeFile'),
	valueTypeFileArray: t('pages.startSetter.valueTypeFileArray'),
	valueTypeObject: t('pages.startSetter.valueTypeObject')
}))

const formTypeOptions = computed<Array<{ label: string; value: FormType }>>(() => [
	{ label: texts.value.text, value: 'text-input' },
	{ label: texts.value.textarea, value: 'text-area' },
	{ label: texts.value.select, value: 'select' },
	{ label: texts.value.number, value: 'number' },
	{ label: texts.value.boolean, value: 'checkbox' },
	{ label: texts.value.file, value: 'file' },
	{ label: texts.value.fileList, value: 'file-list' },
	{ label: texts.value.jsonObject, value: 'json_object' }
])

const fileTypeOptions = computed<Array<{ label: string; value: FileType }>>(() => [
	{ label: texts.value.document, value: 'document' },
	{ label: texts.value.image, value: 'image' },
	{ label: texts.value.audio, value: 'audio' },
	{ label: texts.value.video, value: 'video' },
	{ label: texts.value.custom, value: 'custom' }
])

const fieldIconMap: Record<FormType, string> = {
	'text-input': 'lucide:type',
	'text-area': 'lucide:align-left',
	select: 'lucide:list-filter',
	number: 'lucide:hash',
	checkbox: 'lucide:check-square',
	file: 'lucide:file',
	'file-list': 'lucide:files',
	json_object: 'lucide:braces'
}

const VARIABLE_NAME_REGEXP = /^[A-Za-z][A-Za-z0-9_]*$/

const formData = useSetterModel<StartData>(props, emit)
const dialogVisible = ref(false)
const editingIndex = ref(-1)
const dialogFormRef = ref<FormInstance>()

const createDefaultValue = (formType: FormType): StartVariable['default_value'] => {
	switch (formType) {
		case 'number':
			return 0
		case 'checkbox':
			return false
		case 'file':
			return {}
		case 'file-list':
			return []
		case 'json_object':
			return {}
		default:
			return ''
	}
}

const createEmptyVariable = (formType: FormType = 'text-input'): StartVariable => ({
	name: '',
	label: '',
	max_length: undefined,
	default_value: createDefaultValue(formType),
	json: {},
	is_require: true,
	is_hide: false,
	formType,
	options: [],
	file_types: [],
	file_extensions: []
})

const normalizeVariable = (variable?: Partial<StartVariable>): StartVariable => {
	const formType = variable?.formType || 'text-input'
	const base = createEmptyVariable(formType)

	const normalized: StartVariable = {
		...base,
		...variable,
		name: (variable?.name || '').trim(),
		label: (variable?.label || '').trim(),
		formType,
		options: Array.isArray(variable?.options)
			? variable.options.map((item) => `${item}`.trim()).filter(Boolean)
			: [],
		file_types: Array.isArray(variable?.file_types) ? [...variable.file_types] : [],
		file_extensions: Array.isArray(variable?.file_extensions)
			? variable.file_extensions.map((item) => `${item}`.trim()).filter(Boolean)
			: [],
		json:
			variable?.json && typeof variable.json === 'object' && !Array.isArray(variable.json)
				? cloneDeep(variable.json)
				: {}
	}

	if (!normalized.label && normalized.name) {
		normalized.label = normalized.name
	}
	if (!normalized.name && normalized.label) {
		normalized.name = normalized.label
	}

	if (normalized.default_value === undefined) {
		normalized.default_value = createDefaultValue(formType)
	}

	return normalized
}

const serializeJsonValue = (value: unknown, fallback: string) => {
	if (value === undefined || value === null || value === '') {
		return fallback
	}

	try {
		return JSON.stringify(value, null, 2)
	} catch {
		return fallback
	}
}

const parseJsonValue = (value: string, fallback: unknown) => {
	const text = value?.trim()
	if (!text) {
		return fallback
	}

	return JSON.parse(text)
}

const toEditorModel = (variable?: Partial<StartVariable>): StartVariableEditor => {
	const normalized = normalizeVariable(variable)

	return {
		...normalized,
		defaultValueText: serializeJsonValue(
			normalized.default_value,
			normalized.formType === 'file-list' ? '[]' : '{}'
		),
		jsonText: serializeJsonValue(normalized.json, '{}')
	}
}

const dialogForm = reactive<StartVariableEditor>(toEditorModel())

const dialogRules = computed<FormRules<StartVariableEditor>>(() => ({
	formType: [{ required: true, message: texts.value.selectFieldType, trigger: 'change' }],
	name: [
		{
			required: true,
			message: texts.value.enterVariableName,
			trigger: 'blur'
		},
		{
			trigger: 'blur',
			validator: (_rule, value: string, callback) => {
				const name = value?.trim()
				if (!name) {
					callback(new Error(texts.value.enterVariableName))
					return
				}

				if (!VARIABLE_NAME_REGEXP.test(name)) {
					callback(new Error(texts.value.variableNamePattern))
					return
				}

				const duplicateIndex = (formData.value.variables || []).findIndex(
					(item, index) => index !== editingIndex.value && item.name?.trim() === name
				)

				if (duplicateIndex !== -1) {
					callback(new Error(texts.value.variableNameDuplicate))
					return
				}

				callback()
			}
		}
	],
	label: [
		{ required: true, message: texts.value.enterDisplayName, trigger: 'blur' },
		{
			trigger: 'blur',
			validator: (_rule, value: string, callback) => {
				const name = value?.trim()
				if (!name) {
					callback(new Error(texts.value.enterDisplayName))
					return
				}

				const duplicateIndex = (formData.value.variables || []).findIndex(
					(item: StartVariable, index) =>
						index !== editingIndex.value && item.label?.trim() === name
				)

				if (duplicateIndex !== -1) {
					callback(new Error(texts.value.displayNameDuplicate))
					return
				}

				callback()
			}
		}
	],
	options: [
		{
			trigger: 'change',
			validator: (_rule, value: unknown[], callback) => {
				if (dialogForm.formType !== 'select') {
					callback()
					return
				}

				if (!Array.isArray(value) || value.length === 0) {
					callback(new Error(texts.value.atLeastOneOption))
					return
				}

				callback()
			}
		}
	],
	file_extensions: [
		{
			trigger: 'blur',
			validator: (_rule, value: string[], callback) => {
				if (
					!supportsFileConfig(dialogForm.formType) ||
					!dialogForm.file_types?.includes('custom')
				) {
					callback()
					return
				}

				if (!Array.isArray(value) || value.length === 0) {
					callback(new Error(texts.value.customExtensionRequired))
					return
				}

				callback()
			}
		}
	],
	default_value: [
		{
			trigger: 'blur',
			validator: (_rule, _value, callback) => {
				if (dialogForm.formType === 'json_object') {
					try {
						const parsed = parseJsonValue(dialogForm.defaultValueText, {})
						if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
							callback()
							return
						}
					} catch {
						// fall through
					}
					callback(new Error(texts.value.defaultJsonInvalid))
					return
				}

				callback()
			}
		}
	],
	jsonText: [
		{
			trigger: 'blur',
			validator: (_rule, value: string, callback) => {
				if (dialogForm.formType !== 'json_object') {
					callback()
					return
				}

				try {
					const parsed = parseJsonValue(value, {})
					if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
						callback()
						return
					}
				} catch {
					// fall through
				}

				callback(new Error(texts.value.jsonConfigInvalid))
			}
		}
	]
}))

const getFieldIcon = (formType: FormType) => fieldIconMap[formType]

const getFormTypeLabel = (formType: FormType) => {
	const labelMap: Record<FormType, string> = {
		'text-input': texts.value.text,
		'text-area': texts.value.textarea,
		select: texts.value.selectShort,
		number: texts.value.number,
		checkbox: texts.value.boolean,
		file: texts.value.file,
		'file-list': texts.value.fileList,
		json_object: texts.value.jsonObject
	}

	return labelMap[formType]
}

const getValueTypeLabel = (formType: FormType) => {
	const labelMap: Record<FormType, string> = {
		'text-input': texts.value.valueTypeString,
		'text-area': texts.value.valueTypeString,
		select: texts.value.valueTypeString,
		number: texts.value.valueTypeNumber,
		checkbox: texts.value.valueTypeBoolean,
		file: texts.value.valueTypeFile,
		'file-list': texts.value.valueTypeFileArray,
		json_object: texts.value.valueTypeObject
	}

	return labelMap[formType]
}
const supportsMaxLength = (formType: FormType) => ['text-input', 'text-area'].includes(formType)
const supportsFileConfig = (formType: FormType) => ['file', 'file-list'].includes(formType)
const isStringDefaultEditor = (formType: FormType) =>
	['text-input', 'text-area', 'select'].includes(formType)

const stringDefaultValue = computed({
	get: () => (typeof dialogForm.default_value === 'string' ? dialogForm.default_value : ''),
	set: (value: string) => {
		dialogForm.default_value = value
	}
})

const numberDefaultValue = computed({
	get: () => (typeof dialogForm.default_value === 'number' ? dialogForm.default_value : 0),
	set: (value: number | null | undefined) => {
		dialogForm.default_value = Number(value ?? 0)
	}
})

const booleanDefaultValue = computed({
	get: () => (typeof dialogForm.default_value === 'boolean' ? dialogForm.default_value : false),
	set: (value: boolean) => {
		dialogForm.default_value = value
	}
})

const singleFileDefaultValue = computed<WorkflowUploadFile | null>({
	get: () =>
		dialogForm.default_value &&
		typeof dialogForm.default_value === 'object' &&
		!Array.isArray(dialogForm.default_value)
			? (dialogForm.default_value as WorkflowUploadFile)
			: null,
	set: (value) => {
		dialogForm.default_value = value || {}
	}
})

const fileListDefaultValue = computed<WorkflowUploadFile[]>({
	get: () =>
		Array.isArray(dialogForm.default_value)
			? (dialogForm.default_value as WorkflowUploadFile[])
			: [],
	set: (value) => {
		dialogForm.default_value = value
	}
})

const resetDialogForm = (variable?: Partial<StartVariable>) => {
	Object.assign(dialogForm, toEditorModel(variable))
	dialogFormRef.value?.clearValidate()
}

const handleAddVariable = () => {
	editingIndex.value = -1
	resetDialogForm(createEmptyVariable())
	dialogVisible.value = true
}

const handleEditVariable = (index: number) => {
	editingIndex.value = index
	resetDialogForm(formData.value.variables?.[index])
	dialogVisible.value = true
}

const handleRemoveVariable = (index: number) => {
	formData.value.variables.splice(index, 1)
}

const handleCloseDialog = () => {
	dialogVisible.value = false
	resetDialogForm(createEmptyVariable())
	editingIndex.value = -1
}

const handleFileTypesChange = (value: FileType[]) => {
	if (!value?.includes('custom')) {
		dialogForm.file_extensions = []
	}
}

const handleFormTypeChange = (formType: FormType) => {
	const nextDefaultValue = createDefaultValue(formType)
	dialogForm.formType = formType
	dialogForm.max_length = supportsMaxLength(formType) ? dialogForm.max_length || 50 : 50
	dialogForm.default_value = nextDefaultValue
	dialogForm.defaultValueText = serializeJsonValue(
		nextDefaultValue,
		formType === 'file-list' ? '[]' : '{}'
	)

	if (formType !== 'select') {
		dialogForm.options = []
	}

	if (!supportsFileConfig(formType)) {
		dialogForm.file_types = []
		dialogForm.file_extensions = []
	}

	if (formType !== 'json_object') {
		dialogForm.json = {}
		dialogForm.jsonText = '{}'
	}
}

const normalizedDialogVariable = computed<StartVariable>(() => {
	const formType = dialogForm.formType
	let defaultValue: StartVariable['default_value']

	switch (formType) {
		case 'number':
			defaultValue = Number(dialogForm.default_value ?? 0)
			break
		case 'checkbox':
			defaultValue = Boolean(dialogForm.default_value)
			break
		case 'file':
			defaultValue = singleFileDefaultValue.value || {}
			break
		case 'file-list':
			defaultValue = fileListDefaultValue.value
			break
		case 'json_object':
			defaultValue = parseJsonValue(dialogForm.defaultValueText, {})
			break
		default:
			defaultValue = `${dialogForm.default_value ?? ''}`
			break
	}

	return normalizeVariable({
		name: dialogForm.name,
		label: dialogForm.label,
		max_length: dialogForm.max_length,
		default_value: defaultValue,
		json: dialogForm.formType === 'json_object' ? parseJsonValue(dialogForm.jsonText, {}) : {},
		is_require: dialogForm.is_require,
		is_hide: dialogForm.is_hide,
		formType,
		options:
			formType === 'select'
				? dialogForm.options?.map((item) => `${item}`.trim()).filter(Boolean)
				: [],
		file_types: supportsFileConfig(formType) ? dialogForm.file_types || [] : [],
		file_extensions: supportsFileConfig(formType) ? dialogForm.file_extensions || [] : []
	})
})

const handleSaveVariable = async () => {
	if (!dialogFormRef.value) return

	try {
		await dialogFormRef.value.validate()
	} catch {
		return
	}

	const nextVariable = normalizedDialogVariable.value

	if (editingIndex.value === -1) {
		formData.value.variables.push(nextVariable)
	} else if (formData.value.variables?.[editingIndex.value]) {
		formData.value.variables[editingIndex.value] = nextVariable
	}

	ElMessage.success(
		editingIndex.value === -1 ? texts.value.variableAdded : texts.value.variableUpdated
	)
	handleCloseDialog()
}
</script>

<style scoped lang="less">
.start-setter {
	display: flex;
	flex-direction: column;
	gap: 12px;
}

.empty-state {
	display: flex;
	flex-direction: column;
	align-items: flex-start;
	gap: 12px;
	padding: 18px 16px;
	border: 1px dashed #d0d5dd;
	border-radius: 12px;
	background: #f8fafc;
}

.empty-desc {
	font-size: 12px;
	color: #667085;
}

.variable-list {
	display: flex;
	flex-direction: column;
	gap: 12px;
}

.variable-card {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 12px;
	padding: 14px 16px;
	border: 1px solid #eaecf0;
	border-radius: 14px;
	background: #fff;
	box-shadow: 0 6px 18px rgba(16, 24, 40, 0.06);
}

.variable-left {
	display: flex;
	align-items: center;
	gap: 12px;
	flex: 1;
	min-width: 0;
}

.variable-right {
	display: flex;
	align-items: center;
	gap: 2px;
	flex-shrink: 0;
}

.drag-icon {
	color: #98a2b3;
	flex-shrink: 0;
	cursor: move;
}

.field-icon {
	display: flex;
	align-items: center;
	justify-content: center;
	width: 32px;
	height: 32px;
	border-radius: 10px;
	background: #eef4ff;
	color: #296dff;
	flex-shrink: 0;
}

.field-icon--large {
	width: 40px;
	height: 40px;
}

.variable-title {
	display: flex;
	align-items: center;
	gap: 2px;
	min-width: 0;
	font-size: 14px;
	font-weight: 600;
	color: #344054;
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}

.title-separator {
	color: #98a2b3;
}

.variable-subtitle {
	display: flex;
	flex-wrap: wrap;
	gap: 10px;
	margin-top: 4px;
	font-size: 12px;
	color: #667085;
}

.required-tag,
.type-tag,
.legacy-badge {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	padding: 4px 8px;
	border-radius: 999px;
	font-size: 12px;
	line-height: 1;
}

.required-tag {
	color: #475467;
	background: #f2f4f7;
}

.type-tag {
	color: #344054;
	background: #f8fafc;
	border: 1px solid #d0d5dd;
}

.legacy-badge {
	color: #296dff;
	background: #eef4ff;
	border: 1px solid #b2ccff;
}

.type-picker {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 12px;
	padding: 12px 14px;
	border-radius: 12px;
	background: #f8fafc;
}

.type-preview {
	display: flex;
	align-items: center;
	gap: 12px;
}

.type-preview__title {
	font-size: 14px;
	font-weight: 600;
	color: #344054;
}

.type-preview__desc {
	margin-top: 2px;
	font-size: 12px;
	color: #667085;
	line-height: 1;
}

.switch-line,
.check-line {
	display: flex;
	align-items: center;
	gap: 10px;
	margin-top: 4px;
	color: #344054;
}

.dialog-footer {
	display: flex;
	justify-content: flex-end;
	gap: 8px;
}

:deep(.el-input-number.w-full) {
	width: 100%;
}
</style>
