<script lang="ts" setup>
import { computed, watch } from 'vue'
import NodeRuntimeConfig from '@/nodes/_base/NodeRuntimeConfig.vue'
import VarInput from '@/nodes/_base/VarInput.vue'
import VarSelect from '@/nodes/_base/VarSelect.vue'
import { VARIABLE_TYPE_OPERATORS } from '@/constant'
import { useSetterModel } from '../_shared/useSetterModel'
import { ALLOWED_INPUT_TYPES, filterArrayVariables, type AllowedListInputType } from '../utils'

import type { ConditionType, NodeVariable, NodeVariableType } from '@/nodes/Interface'
import type { ListData } from './index'

type FilterValueType = Extract<ConditionType['varType'], 'string' | 'number' | 'boolean' | 'object'>

type OperatorsType = ListData['filter_by']['conditions'][0]['comparison_operator']

const FILTER_VALUE_TYPE_MAP: Record<AllowedListInputType, FilterValueType> = {
	'array[string]': 'string',
	'array[number]': 'number',
	'array[boolean]': 'boolean',
	'array[file]': 'string',
	'array[object]': 'object'
}

const DEFAULT_OPERATOR_MAP: Record<FilterValueType, ConditionType['comparison_operator']> = {
	string: 'contains',
	number: '=',
	boolean: 'is',
	object: 'is'
}

const OUTPUT_ITEM_TYPE_MAP: Record<AllowedListInputType, NodeVariableType> = {
	'array[string]': 'string',
	'array[number]': 'number',
	'array[boolean]': 'boolean',
	'array[file]': 'object',
	'array[object]': 'object'
}

const FILE_OPTIONS = [
	{ label: 'ID', value: 'id' },
	{ label: 'Name', value: 'name' },
	{ label: 'ExtensionName', value: 'extensionName' },
	{ label: 'Size', value: 'size' },
	{ label: 'Path', value: 'path' }
]

const FILE_FILTER_FIELD = {
	label: 'name',
	value: 'name'
} as const

const BOOLEAN_OPTIONS = [
	{ label: 'True', value: 'true' },
	{ label: 'False', value: 'false' }
]

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

const emit = defineEmits<{
	update: [data: ListData]
}>()

const formData = useSetterModel<ListData>(props, emit)

const inputVar = computed({
	get() {
		return formData.value.variables?.[0]
	},
	set(val) {
		if (val && formData.value.variables?.[0]) {
			formData.value.variables[0] = val
		} else if (val) {
			formData.value.variables = [val]
		}
	}
})

const conditions = computed({
	get() {
		return formData.value.filter_by.conditions?.[0]
	},
	set(val) {
		if (val && formData.value.filter_by.conditions?.[0]) {
			formData.value.filter_by.conditions[0] = val
		} else if (val) {
			formData.value.filter_by.conditions = [val]
		}
	}
})

const currentInputType = computed<AllowedListInputType | undefined>(() => {
	const type = inputVar.value?.type
	return type && ALLOWED_INPUT_TYPES.includes(type as AllowedListInputType)
		? (type as AllowedListInputType)
		: undefined
})

const currentFilterValueType = computed<FilterValueType>(() => {
	if (!currentInputType.value) return 'string'
	return FILTER_VALUE_TYPE_MAP[currentInputType.value]
})

const isFileInputType = computed(() => currentInputType.value === 'array[file]')

const operatorOptions = computed(() => {
	return VARIABLE_TYPE_OPERATORS[currentFilterValueType.value] ?? []
})

const getOperatorsByFilterType = (type: FilterValueType) => {
	return VARIABLE_TYPE_OPERATORS[type] ?? []
}

const normalizeBooleanValue = (value?: string) => {
	return value === 'false' ? 'false' : 'true'
}

const syncOutputs = (type?: AllowedListInputType) => {
	if (!type) return

	const resultType = type
	const itemType = OUTPUT_ITEM_TYPE_MAP[type]
	const outputTemplates: Array<Pick<NodeVariable, 'name' | 'describe' | 'type'>> = [
		{ name: 'result', describe: '过滤结果', type: resultType },
		{ name: 'first_record', describe: '第一条记录', type: itemType },
		{ name: 'last_record', describe: '最后一条记录', type: itemType }
	]

	formData.value.outputs = outputTemplates.map((template, index) => ({
		...(formData.value.outputs?.[index] || {}),
		...template
	}))
}

const syncConditionByInputType = (type?: AllowedListInputType) => {
	if (!type) return

	const nextVarType = FILTER_VALUE_TYPE_MAP[type]
	const nextOperators = getOperatorsByFilterType(nextVarType)
	const currentOperator = conditions.value?.comparison_operator
	const nextOperator = nextOperators.some((item) => item.value === currentOperator)
		? currentOperator
		: DEFAULT_OPERATOR_MAP[nextVarType]

	conditions.value = {
		...conditions.value,
		comparison_operator: nextOperator as OperatorsType,
		right_value:
			nextVarType === 'boolean'
				? normalizeBooleanValue(conditions.value?.right_value)
				: (conditions.value?.right_value ?? ''),
		left_value: type === 'array[file]' ? FILE_FILTER_FIELD.value : '',
		varType: nextVarType
	}
}

watch(
	currentInputType,
	(type) => {
		syncOutputs(type)
		syncConditionByInputType(type)
	},
	{ immediate: true }
)

const handleInputVarChange = (val: { value: string; type: string }) => {
	const nextType = val.type as NodeVariableType
	const nextInputVar: NodeVariable = {
		...(inputVar.value || { name: '' }),
		value: val.value,
		type: nextType
	}

	inputVar.value = nextInputVar
	if (ALLOWED_INPUT_TYPES.includes(nextType as AllowedListInputType)) {
		syncOutputs(nextType as AllowedListInputType)
		syncConditionByInputType(nextType as AllowedListInputType)
	}
}

const handleLeftChange = (val: string) => {
	if (conditions.value) {
		conditions.value.varType = val === 'size' ? 'number' : 'string'
	}
}
</script>

<template>
	<el-scrollbar class="box-border p-12px">
		<el-form label-width="50px">
			<el-form-item label="" label-position="top">
				<div class="w-full flex items-center justify-between beautify">
					<label class="text-14px font-bold text-gray-700">输入变量</label>
				</div>
				<VarSelect
					:model-value="inputVar?.value"
					:filter-fn="filterArrayVariables"
					@change="handleInputVarChange"
					placeholder="请选择输入变量"
				/>
			</el-form-item>

			<el-form-item label="" label-position="top">
				<div class="w-full flex items-center justify-between beautify">
					<label class="text-14px font-bold text-gray-700">过滤条件</label>
					<el-switch v-model="formData.filter_by.enabled" />
				</div>

				<div v-if="conditions && formData.filter_by.enabled" class="w-full flex flex-col gap-12px">
					<div v-if="isFileInputType" class="w-full">
						<el-select
							v-model="conditions.left_value"
							:options="FILE_OPTIONS"
							@change="handleLeftChange"
						/>
					</div>

					<div class="w-full flex items-center gap-12px">
						<el-select
							v-model="conditions.comparison_operator"
							:options="operatorOptions"
							style="width: 120px"
						/>

						<el-radio-group
							v-if="currentFilterValueType === 'boolean'"
							v-model="conditions.right_value"
							class="list-boolean-group"
						>
							<el-radio-button
								v-for="option in BOOLEAN_OPTIONS"
								:key="option.value"
								:label="option.label"
								:value="option.value"
								class="list-boolean-group__item"
							/>
						</el-radio-group>

						<VarInput
							v-else
							v-model="conditions.right_value"
							class="flex-1"
							placeholder="键入 '/' 键快速插入变量"
						/>
					</div>
				</div>
			</el-form-item>

			<el-form-item label="" label-position="top">
				<div class="w-full flex items-center justify-between">
					<label class="text-14px font-bold text-gray-700">取第 N 项</label>
					<el-switch v-model="formData.extract_by.enabled" />
				</div>
				<div class="w-full">
					<VarInput v-if="formData.extract_by.enabled" v-model="formData.extract_by.serial" />
				</div>
			</el-form-item>

			<el-form-item label="" label-position="top">
				<div class="w-full flex items-center justify-between">
					<label class="text-14px font-bold text-gray-700">取前 N 项</label>
					<el-switch v-model="formData.limit.enabled" />
				</div>
				<div class="w-full flex gap-12px">
					<el-input-number v-model="formData.limit.size" :min="1" :max="20" />
					<div class="flex-1 pr-10px">
						<el-slider v-model="formData.limit.size" :min="1" :max="20" />
					</div>
				</div>
			</el-form-item>

			<el-form-item label="" label-position="top">
				<div class="w-full flex items-center justify-between">
					<label class="text-14px font-bold text-gray-700">排序</label>
					<el-switch v-model="formData.order_by.enabled" />
				</div>
				<div class="w-full flex gap-12px" v-if="formData.order_by.enabled">
					<el-select
						v-if="isFileInputType"
						v-model="formData.order_by.key"
						:options="FILE_OPTIONS"
						style=""
					/>
					<el-radio-group v-model="formData.order_by.value" size="small" class="w-160px">
						<el-radio-button label="升序" value="asc" />
						<el-radio-button label="降序" value="desc" />
					</el-radio-group>
				</div>
			</el-form-item>

			<el-collapse class="w-full">
				<el-collapse-item title="" name="2">
					<template #title>
						<div class="flex items-center justify-between beautify">
							<label class="text-14px font-bold text-gray-700">输出变量</label>
						</div>
					</template>
					<ul>
						<li v-for="output in formData.outputs" :key="output.name">
							<div>
								<span class="text-#333">{{ output.name }}</span>
								<span class="text-#999 ml-8px">{{ output.type }}</span>
							</div>
							<div class="text-#666">{{ output.describe }}</div>
						</li>
					</ul>
				</el-collapse-item>
			</el-collapse>

			<NodeRuntimeConfig v-model="formData" class="px-0!" />
		</el-form>
	</el-scrollbar>
</template>

<style scoped lang="less">
:deep(.el-form-item) {
	padding-bottom: 10px;
	margin-bottom: 8px;
	border-bottom: 1px solid #eee;
}

.list-filter-field__tag {
	display: inline-flex;
	align-items: center;
	padding: 6px 10px;
	border-radius: 10px;
	background: #fff;
	color: var(--el-color-primary);
	font-size: 14px;
	line-height: 1;
}

.list-boolean-group {
	display: flex;
	width: 100%;
}

.list-boolean-group__item {
	flex: 1;
}

:deep(.list-boolean-group .el-radio-button) {
	flex: 1;
}

:deep(.el-collapse-item__header) {
	padding: 0 !important;
}

:deep(.list-boolean-group .el-radio-button__inner) {
	width: 100%;
}

:deep(.el-collapse) {
	border-top: none;
}
</style>
