<template>
	<div class="w-full h-full flex flex-col">
		<div
			class="h-60px shrink-0 border-b border-b-solid border-gray-200 flex items-center justify-between px-12px"
		>
			<div class="left flex items-center gap-4">
				<el-breadcrumb separator="/" class="flex items-center">
					<el-breadcrumb-item>Workspace</el-breadcrumb-item>
					<el-breadcrumb-item>
						<Input ref="inputRef" v-model="workflow.name" variant="borderless" />
					</el-breadcrumb-item>
				</el-breadcrumb>
				<div class="flex gap-2" v-show="!showTagInput" @click="showTagInput = true">
					<el-tag type="info" v-for="tag in workflow.tags" :key="tag" :disable-transitions="false">
						{{ tag }}
					</el-tag>
				</div>
				<el-input-tag
					v-show="showTagInput"
					v-model="workflow.tags"
					placeholder="按回车键添加标签"
					aria-label="按回车键添加标签"
					:max="5"
					@blur="showTagInput = false"
				/>
				<IconButton
					v-if="!workflow.tags?.length && !showTagInput"
					icon="iconoir:plus"
					type="primary"
					link
					@click="showTagInput = true"
					>标签</IconButton
				>
			</div>
			<div class="right flex items-center gap-2">
				<el-button type="default" size="small">发布</el-button>
				<IconButton icon="lucide:history" type="default" link></IconButton>
				<el-dropdown placement="bottom-end" popper-class="w-120px">
					<IconButton icon="fluent-mdl2:more" type="default" link></IconButton>
					<template #dropdown>
						<el-dropdown-item>描述</el-dropdown-item>
						<el-dropdown-item>复用</el-dropdown-item>
						<el-dropdown-item @click="handleRename">重命名</el-dropdown-item>
						<el-dropdown-item divided @click="handleDelete">删除</el-dropdown-item>
					</template>
				</el-dropdown>
			</div>
		</div>
		<el-splitter layout="vertical" class="flex-1">
			<el-splitter-panel>
				<div class="h-full w-full" ref="workflowWrapperRef" @drop="onDrop">
					<Workflow
						ref="workflowRef"
						:id="workflow?.id"
						:workflow="workflowWithExecutionState"
						:nodeMap="nodeMap"
						@click:node="handleSelectNode"
						@dblclick:node="handleNodeClick"
						@create:node="handleNodeCreate"
						@create:connection:end="onCreateConnection"
						@drag-and-drop="handleDrop"
						@run:node="handleRunNode"
						@update:nodes:position="handleUpdateNodesPosition"
						@update:node:attrs="handleUpdateNodeProps"
						@delete:node="handleDeleteNode"
						@delete:connection="handleDeleteEdge"
						@dragover="onDragOver"
						@dragleave="onDragLeave"
						@create:connection:cancelled="onConnectionOpenNodeLibary"
						@click:connection:add="handleClickConectionAdd"
						class="bg-#f5f5f5"
					>
						<Toolbar
							@create:node="handleNodeCreate"
							@run="handleRunSelectedNode"
							:env-vars="workflow?.env_variables || []"
							@change-env-vars="handleChangeEnvVars"
						/>
					</Workflow>
				</div>
				<RunWorkflow v-model:visible="runVisible" @run="handleRunSelectedNode" />
				<Setter
					:id="nodeID"
					:workflow="workflow!"
					@update:node:data="handleUpdateNode"
					@run-node="handleRunNode"
					v-model:visible="setterVisible"
				/>
				<el-popover
					:visible="showNodeLibary"
					width="360px"
					virtual-triggering
					:show-arrow="false"
					:append-to="workflowWrapperRef"
					:virtual-ref="libaryRefferenceRef"
				>
					<NodeLibary @add-node="handleNodeCreate" @mouseleave="onHideNodeLibary" />
				</el-popover>
			</el-splitter-panel>

			<el-splitter-panel v-model:size.lazy="footerHeight" :min="32">
				<EditorFooter @toggle="handleFooterToggle" />
			</el-splitter-panel>
		</el-splitter>
	</div>
</template>

<script setup lang="ts">
import { computed, ref, inject, type CSSProperties, onBeforeUnmount, watch, nextTick } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { agent } from '@repo/api-service'

import RunWorkflow from '@/components/RunWorkflow/index.vue'
import EditorFooter from '@/features/editorFooter/index.vue'
import NodeLibary from '@/features/nodeLibary/index.vue'
import Toolbar from '@/features/toolbar/index.vue'
import Setter from '@/features/setter/index.vue'

import { IconButton, Input } from '@repo/ui'
import { nodeMap } from '@/nodes'
import { Workflow, useDragAndDrop } from '@repo/workflow'

import { dayjs, ElMessage, ElMessageBox } from 'element-plus'
import { useRunnerStore } from '@/store/modules/runner.store'

import type {
	IWorkflow,
	XYPosition,
	Connection,
	ConnectStartEvent,
	IWorkflowNode,
	CanvasExecutionStatus
} from '@repo/workflow'

const layout = inject<{ setMainStyle: (style: CSSProperties) => void }>('layout')

layout?.setMainStyle({
	padding: '0px'
})

const footerHeight = ref(32)
const route = useRoute()
const router = useRouter()
const id = route.params?.id as string
const runnerStore = useRunnerStore()
const workflowWrapperRef = ref<HTMLElement>()
const showNodeLibary = ref(false)
const libaryRefferenceRef = ref<HTMLElement>()
const peddingHandlePayload = ref<{
	by?: 'node' | 'edge'
	handle?: ConnectStartEvent
	position: XYPosition
	event?: MouseEvent
	parentId?: string
	connection?: Connection
}>()

const projectMap = JSON.parse(localStorage.getItem(`workflow-map`) || '{}') as Record<
	string,
	IWorkflow
>
const showTagInput = ref(false)

const workflow = ref<IWorkflow>(
	projectMap?.[id]
		? projectMap[id]
		: {
				id,
				name: 'workflow_1',
				created: dayjs().format('MM 月 DD 日'),
				nodes: [],
				edges: []
			}
)

const mapNodeExecutionStatus = (status?: string): CanvasExecutionStatus => {
	if (status === 'running') {
		return 'running'
	}

	if (status === 'success') {
		return 'success'
	}

	if (status === 'failed') {
		return 'warning'
	}

	return 'idle'
}

const MIN_NODE_RUNNING_EFFECT_MS = 500
const displayNodeExecutionStatus = ref<Record<string, CanvasExecutionStatus>>({})
const runningStatusStartedAt = new Map<string, number>()
const pendingNodeStatusTimers = new Map<string, number>()

const clearPendingNodeStatusTimer = (nodeId: string) => {
	const timer = pendingNodeStatusTimers.get(nodeId)
	if (timer) {
		window.clearTimeout(timer)
		pendingNodeStatusTimers.delete(nodeId)
	}
}

const applyDisplayedNodeStatus = (nodeId: string, status: CanvasExecutionStatus) => {
	if (status === 'idle') {
		delete displayNodeExecutionStatus.value[nodeId]
		return
	}

	displayNodeExecutionStatus.value[nodeId] = status
}

const syncDisplayedNodeStatus = (nodeId: string, nextStatus: CanvasExecutionStatus) => {
	const currentStatus = displayNodeExecutionStatus.value[nodeId] || 'idle'

	if (nextStatus === 'running') {
		clearPendingNodeStatusTimer(nodeId)
		runningStatusStartedAt.set(nodeId, Date.now())
		applyDisplayedNodeStatus(nodeId, 'running')
		return
	}

	const runningStartedAt = runningStatusStartedAt.get(nodeId)
	const shouldKeepRunning =
		currentStatus === 'running' &&
		typeof runningStartedAt === 'number' &&
		Date.now() - runningStartedAt < MIN_NODE_RUNNING_EFFECT_MS

	if (shouldKeepRunning) {
		clearPendingNodeStatusTimer(nodeId)
		const delay = MIN_NODE_RUNNING_EFFECT_MS - (Date.now() - runningStartedAt)
		const timer = window.setTimeout(() => {
			applyDisplayedNodeStatus(nodeId, nextStatus)
			runningStatusStartedAt.delete(nodeId)
			pendingNodeStatusTimers.delete(nodeId)
		}, delay)
		pendingNodeStatusTimers.set(nodeId, timer)
		return
	}

	clearPendingNodeStatusTimer(nodeId)
	applyDisplayedNodeStatus(nodeId, nextStatus)
	runningStatusStartedAt.delete(nodeId)
}

const resetDisplayedNodeStatuses = () => {
	pendingNodeStatusTimers.forEach((timer) => window.clearTimeout(timer))
	pendingNodeStatusTimers.clear()
	runningStatusStartedAt.clear()
	displayNodeExecutionStatus.value = {}
}

watch(
	() => runnerStore.currentRunnerKey,
	() => {
		resetDisplayedNodeStatuses()
	}
)

watch(
	() => runnerStore.nodes.map((item) => ({ nodeId: item.nodeId, status: item.status })),
	(nodeStates) => {
		const activeNodeIds = new Set(nodeStates.map((item) => item.nodeId))

		nodeStates.forEach((item) => {
			syncDisplayedNodeStatus(item.nodeId, mapNodeExecutionStatus(item.status))
		})

		Object.keys(displayNodeExecutionStatus.value).forEach((nodeId) => {
			if (!activeNodeIds.has(nodeId)) {
				syncDisplayedNodeStatus(nodeId, 'idle')
			}
		})
	},
	{ immediate: true, deep: true }
)

const workflowWithExecutionState = computed<IWorkflow>(() => {
	const baseWorkflow = workflow.value
	const runnerNodeStatusMap = new Map(Object.entries(displayNodeExecutionStatus.value))

	return {
		...baseWorkflow,
		nodes: (baseWorkflow.nodes || []).map((node) => {
			const executionStatus = runnerNodeStatusMap.get(node.id) || 'idle'

			return {
				...node,
				executionStatus,
				data: {
					...(node.data || {}),
					executionStatus
				}
			}
		})
	}
})
const inputRef = ref<InstanceType<typeof Input>>()
const saveAgentTimer = ref<number | undefined>(undefined)
const saveVarsTimer = ref<number | undefined>(undefined)
const isHydrating = ref(false)
const notifyTimestamps = new Map<string, number>()

const { onDragOver, onDrop, onDragLeave } = useDragAndDrop({
	id,
	addNodes: (node) => {
		handleNodeCreate(node)
	}
})

const normalizeNodeType = (node: any) => {
	const sourceNodeType = node?.nodeType || node?.data?.nodeType || node?.data?.type || node?.type
	return sourceNodeType || 'code'
}
const notifySuccess = (key: string, message: string, cooldown = 1500) => {
	const now = Date.now()
	const last = notifyTimestamps.get(key) || 0
	if (now - last < cooldown) return

	notifyTimestamps.set(key, now)
	ElMessage.success(message)
}

const handleApiResult = (response: any, successMessage?: string, errorMessage?: string) => {
	if (response?.isSuccess) {
		if (successMessage) {
			notifySuccess(successMessage, successMessage)
		}
		return true
	}
	if (response?.code === 0 && response?.error) {
		ElMessage.error(response.error)
		return false
	}
	if (errorMessage) {
		ElMessage.error(errorMessage)
	}
	return false
}

const buildUpdateNodePayload = (node: any) => {
	return {
		...node,
		appAgentId: workflow.value.id,
		parentId: node.parentId || node.data?.parentId || '',
		position: node.position || { x: 20, y: 30 },
		width: node.width ?? node.data?.width ?? 96,
		height: node.height ?? node.data?.height ?? 96,
		selected: !!node.selected,
		nodeType: node.data?.nodeType || node.nodeType,
		zIndex: node.zIndex ?? 1
	}
}

const toWorkflowNode = (node: any) => {
	const normalizedNodeType = normalizeNodeType(node)
	const schema = nodeMap[normalizedNodeType]?.schema
	const position = node?.position || schema?.position || { x: 20, y: 30 }
	const width = node?.width ?? schema?.width ?? 96
	const height = node?.height ?? schema?.height ?? 96

	return {
		...schema,
		...node,
		id: node.id,
		type: 'canvas-node',
		position,
		width,
		height,
		zIndex: node?.zIndex ?? schema?.zIndex ?? 1,
		selected: false,
		data: {
			...(schema?.data || {}),
			...(node?.data || {}),
			id: node.id,
			position,
			width,
			height,
			nodeType: normalizedNodeType
		}
	}
}

const normalizeEdgeEndpoints = (
	edge: { source: string; target: string },
	nodes: Array<{ id: string; parentId?: string }>
) => {
	const sourceNode = nodes.find((node) => node.id === edge.source)
	const targetNode = nodes.find((node) => node.id === edge.target)

	const sourceParentId = sourceNode?.parentId || ''
	const targetParentId = targetNode?.parentId || ''

	if (sourceParentId && sourceParentId !== targetParentId) {
		return {
			source: sourceParentId,
			target: edge.target
		}
	}

	if (targetParentId && targetParentId !== sourceParentId) {
		return {
			source: edge.source,
			target: targetParentId
		}
	}

	return edge
}

const toWorkflowEdge = (
	edge: any,
	index: number,
	nodes: Array<{ id: string; parentId?: string }> = []
) => {
	if (!edge || typeof edge !== 'object' || !edge.source || !edge.target) {
		return null
	}

	const normalizedEdge = normalizeEdgeEndpoints(edge, nodes)

	return {
		...edge,
		...normalizedEdge,
		sourceHandle: edge.sourceHandle === 'source' ? `${edge.source}-source` : edge.sourceHandle,
		targetHandle: edge.targetHandle === 'target' ? `${edge.target}-target` : edge.targetHandle,
		id: edge.id || `edge-${normalizedEdge.source}-${normalizedEdge.target}-${index}`,
		type: 'canvas-edge',
		data: edge.data || {}
	}
}

const isPendingCreate = (node: any) => {
	return !!(node as any)?.__pendingCreate
}

const loadAgentWorkflow = async (agentId: string) => {
	if (!agentId) return
	isHydrating.value = true

	try {
		const response = await agent.postAgentGetAgentInfo({ id: agentId })
		const result = response?.result
		if (!response?.isSuccess || !result) {
			throw new Error('获取智能体信息失败')
		}

		const normalizedNodes = (result.nodes || []).map(toWorkflowNode)

		workflow.value = {
			...(result as unknown as IWorkflow),
			nodes: normalizedNodes,
			edges: (result.edges || []).map((edge: any, index: number) =>
				toWorkflowEdge(edge, index, normalizedNodes)
			)
		}
		await nextTick()
	} catch (error) {
		console.error('loadAgentWorkflow error', error)
		ElMessage.error('加载智能体流程失败')
	} finally {
		isHydrating.value = false
	}
}

const saveAgentMeta = async () => {
	if (!workflow.value?.id) return

	try {
		const response = await agent.postAgentDoEditAgent({
			data: workflow.value
		})
		handleApiResult(response, '智能体已保存', '保存智能体失败')
	} catch (error) {
		console.error('saveAgentMeta error', error)
		ElMessage.error('保存智能体失败')
	}
}

const saveAgentVariables = async () => {
	if (!workflow.value?.id) return

	try {
		const response = await agent.postAgentDoSaveAgentVariables({
			appAgentId: workflow.value.id,
			conversation_variables: workflow.value.conversation_variables || [],
			env_variables: (workflow.value.env_variables || []).map((item: any) => ({
				name: item?.name || '',
				value: item?.value ?? '',
				type: item?.type || 'string'
			}))
		})
		handleApiResult(response, '变量已保存', '保存变量失败')
	} catch (error) {
		console.error('saveAgentVariables error', error)
		ElMessage.error('保存变量失败')
	}
}

const scheduleSaveAgentMeta = () => {
	if (saveAgentTimer.value) window.clearTimeout(saveAgentTimer.value)
	if (!workflow.value?.id) return

	saveAgentTimer.value = window.setTimeout(() => {
		saveAgentMeta()
	}, 600)
}

const scheduleSaveAgentVariables = () => {
	if (saveVarsTimer.value) window.clearTimeout(saveVarsTimer.value)
	if (!workflow.value?.id) return

	saveVarsTimer.value = window.setTimeout(() => {
		saveAgentVariables()
	}, 600)
}

watch(
	() => workflow.value,
	(workflow) => {
		projectMap[workflow.id] = workflow
		localStorage.setItem(`workflow-map`, JSON.stringify(projectMap))
	},
	{ deep: true }
)

watch(
	() => [workflow.value?.name, workflow.value?.description, workflow.value?.tags],
	() => {
		if (isHydrating.value) return
		scheduleSaveAgentMeta()
	},
	{ deep: true }
)

watch(
	() => [workflow.value?.conversation_variables, workflow.value?.env_variables],
	() => {
		if (isHydrating.value) return
		scheduleSaveAgentVariables()
	},
	{ deep: true }
)

/**
 * 监听路由参数变化
 */
watch(
	() => route.params?.id,
	async (id) => {
		if (id) {
			await loadAgentWorkflow(id as string)
		}
	},
	{ immediate: true }
)
/**
 * Editor
 */
const handleFooterToggle = (open: boolean) => {
	footerHeight.value = open ? 200 : 32
}

/**
 * Workflow
 */
const nodeID = ref('')
const setterVisible = ref(false)
const runVisible = ref(false)
const pendingSetterInit = new Set<string>()
const workflowRef = ref<InstanceType<typeof Workflow>>()
const handleRunSelectedNode = async () => {
	if (!workflow.value?.id) {
		ElMessage.warning('请先选择需要运行的节点')
		return
	}

	if (!nodeID.value) {
		const selectedNode = workflow.value?.nodes?.find((node) => (node as any)?.selected)
		if (selectedNode?.id) {
			nodeID.value = selectedNode.id
		}
	}

	if (!nodeID.value) {
		ElMessage.warning('请选择需要测试的节点')
		return
	}

	try {
		const response = await agent.postAgentDoExecute({
			appAgentId: workflow.value.id,
			start_node_id: nodeID.value,
			is_debugger: true,
			responseType: 'ws',
			params: {}
		})
		const agentRunnerKey = response?.result
		if (agentRunnerKey) {
			runnerStore.startRunner(agentRunnerKey)
		}
		runVisible.value = false
		// handleApiResult(response, '已提交节点测试', '节点测试失败')
	} catch (error) {
		console.error('postDoTestNodeRunner error', error)
		ElMessage.error('节点测试失败')
	}
}

const handleRunNode = async (id: string) => {
	if (!workflow.value?.id) {
		ElMessage.warning('请先选择需要运行的节点')
		return
	}

	try {
		const response = await agent.postAgentDoExecute({
			appAgentId: workflow.value.id,
			start_node_id: id,
			is_debugger: true,
			responseType: 'ws',
			params: {}
		})
		const agentRunnerKey = response?.result
		if (agentRunnerKey) {
			runnerStore.startRunner(agentRunnerKey)
		}
		// handleApiResult(response, '已提交运行节点', '运行节点失败')
	} catch (error) {
		console.error('postDoTestNodeRunner error', error)
		ElMessage.error('运行节点失败')
	}
}
const handleNodeCreate = (value: { type: string; position?: XYPosition } | string) => {
	// TODO: 处理注释节点
	if (typeof value === 'string') {
		if (value === 'stickyNote') {
			workflow.value?.nodes.push({
				appAgentId: workflow.value.id,
				type: 'canvas-node',
				zIndex: -1,
				nodeType: 'stickyNote',
				position: { x: 600, y: 300 },
				id: 'stickyNote',
				name: '注释',
				remark: '',
				data: {
					id: '',
					version: ['1.0.0'],
					inputs: [],
					outputs: [],
					position: { x: 600, y: 300 },
					nodeType: 'stickyNote',
					content: '注释内容，可以使用 **Markdown** 语法进行格式化, 双击进入编辑。',
					width: 400,
					height: 200,
					color: '#fff5d6'
				}
			})
		}
		return
	}

	const nodeToAdd = nodeMap[value.type]?.schema
	const viewport = workflowRef.value?.getVueFlow()?.viewport

	// 如果存在对应节点则添加
	if (nodeToAdd) {
		const newNodeParam: any = {
			...nodeToAdd,
			appAgentId: workflow.value?.id || '',
			position: value.position
		}

		// 获取当前画布的中心点
		if (newNodeParam.position && viewport) {
			newNodeParam.position = {
				// 计算当前中心点坐标（相对于画布）
				x: (-viewport.value.x + window.innerWidth / 2) / viewport.value.zoom,
				y: (-viewport.value.y + window.innerHeight / 2) / viewport.value.zoom
			}
		}

		// 需要连接前一个节点
		if (peddingHandlePayload.value) {
			const { position, handle, parentId } = peddingHandlePayload.value

			newNodeParam.position = position
			newNodeParam.prevNodeId = handle?.nodeId
			newNodeParam.parentId = parentId
			// 条件句柄
			if (handle?.handleId?.includes('_')) {
				newNodeParam.nodeHandleId = handle?.handleId
			}
		}

		if (!newNodeParam.position) {
			newNodeParam.position = nodeToAdd.position
		}

		// 根据连线添加节点
		if (peddingHandlePayload.value?.by === 'edge' && peddingHandlePayload.value?.connection) {
			const { connection } = peddingHandlePayload.value
			const params = {
				edgeId: connection.id,
				newNode: newNodeParam
			}
			agent.postAgentDoNewAgentNodeWithEdge(params).then(async (response) => {
				if (handleApiResult(response, '节点已添加', '新增节点失败')) {
					await loadAgentWorkflow(workflow.value.id)
				}
			})
			onHideNodeLibary()
			return
		}

		onHideNodeLibary()

		agent
			.postAgentDoNewAgentNode(newNodeParam)
			.then(async (response) => {
				if (handleApiResult(response, '节点已添加', '新增节点失败')) {
					await loadAgentWorkflow(workflow.value.id)
				}
			})
			.catch((error) => {
				console.error('postDoNewAgentNode error', error)
				ElMessage.error('新增节点失败')
			})
	}
}
const handleNodeClick = (id: string, _position: XYPosition) => {
	nodeID.value = id
	pendingSetterInit.add(id)
	setterVisible.value = true
}

const handleDrop = (position: XYPosition, event: DragEvent) => {
	const type = event.dataTransfer?.getData('application/x-node-type')
	if (!type) return

	handleNodeCreate({ type, position })
}

/**
 * 修改工作流名称
 */
const handleRename = () => {
	inputRef.value?.focus()
	inputRef.value?.select()
}

/**
 * 删除工作流
 */
const handleDelete = () => {
	ElMessageBox.confirm('确定要删除吗？', '提示', {
		confirmButtonText: '确定',
		cancelButtonText: '取消',
		type: 'warning'
	}).then(() => {
		localStorage.removeItem(`project_${id}`)
		router.push('/')
	})
}

/**
 * 创建连线
 */
const normalizeConnectionEndpoints = (connection: Connection) => {
	return normalizeEdgeEndpoints(connection, workflow.value?.nodes || [])
}

const onCreateConnection = async (connection: Connection) => {
	const { sourceHandle } = connection
	const { source, target } = normalizeConnectionEndpoints(connection)

	const params: {
		appAgentId: string
		source: string
		target: string
		zIndex: number
		sourceHandle?: string
	} = {
		appAgentId: workflow.value.id,
		source,
		target,
		zIndex: 1
	}

	// 需要传sourceHandle的情况是中间带"_"
	if (sourceHandle && sourceHandle.includes('_')) {
		params.sourceHandle = sourceHandle
	}

	// 如果是source条件节点else的sourceHandle模式就是当前节点id
	// if (sourceHandle && sourceHandle.endsWith('-else')) {
	// 	params.sourceHandle = source
	// }

	if (!workflow.value?.edges.some((edge) => edge.source === source && edge.target === target)) {
		const response = await agent.postAgentDoNewEdge(params)

		if (handleApiResult(response, '连线已创建', '连线创建失败')) {
			await loadAgentWorkflow(workflow.value.id)
		}
	}
}

/**
 * 移动位置
 */
const handleUpdateNodesPosition = (events: { id: string; position: XYPosition }[]) => {
	events?.forEach(({ id, position }) => {
		const node = workflow.value?.nodes.find((node) => node.id === id)
		if (node && !isPendingCreate(node)) {
			if (node.position?.x === position.x && node.position?.y === position.y) {
				return
			}
			node.position = position
			agent
				.postAgentDoUpdateAgentNode(buildUpdateNodePayload(node))
				.then((response) => {
					handleApiResult(response, undefined, '更新节点失败')
				})
				.catch((error) => {
					console.error('postDoUpdateAgentNode error', error)
				})
		}
	})
}

/**
 * 点击选中节点
 * @param id
 * @param position
 */
const handleSelectNode = (id: string) => {
	workflow.value?.nodes.forEach((node) => {
		node.selected = false
	})
	const node = workflow.value?.nodes.find((node) => node.id === id)
	if (node) {
		node.selected = true
	}
}

/**
 * 修改节点数据
 */
const handleUpdateNode = (node: IWorkflowNode) => {
	if (node && !isPendingCreate(node)) {
		if (pendingSetterInit.has(id)) {
			pendingSetterInit.delete(id)
			return
		}

		if (node.nodeType === 'if-else') {
			const cases = node.data?.cases || []
			const offsetHeight = (cases.length > 1 ? cases.length - 1 : 0) * 32
			node.height = 96 + offsetHeight
		}

		agent
			.postAgentDoUpdateAgentNode(buildUpdateNodePayload(node))
			.then((response) => {
				handleApiResult(response, undefined, '更新节点失败')
			})
			.catch((error) => {
				console.error('postDoUpdateAgentNode error', error)
			})
	}
}

/**
 * 修改节点属性
 */
const handleUpdateNodeProps = (id: string, attrs: Record<string, unknown>) => {
	const node = workflow.value?.nodes.find((node) => node.id === id)
	if (node && !isPendingCreate(node)) {
		const keys = Object.keys(attrs || {})
		const meaningfulKeys = keys.filter((key) => !['selected', 'dragging'].includes(key))
		if (meaningfulKeys.length === 0) {
			return
		}
		if (node.data?.nodeType === 'stickyNote') {
			Object.assign(node.data, attrs)
		} else {
			Object.assign(node, attrs)
		}
		agent
			.postAgentDoUpdateAgentNode(buildUpdateNodePayload(node))
			.then((response) => {
				handleApiResult(response, undefined, '更新节点失败')
			})
			.catch((error) => {
				console.error('postDoUpdateAgentNode error', error)
			})
	}
}

/**
 * 删除节点
 */
const handleDeleteNode = async (id: string) => {
	const index = workflow.value.nodes.findIndex((node) => node.id === id)
	if (index != -1) {
		ElMessageBox.confirm('确定要删除吗？', '提示', {
			confirmButtonText: '确定',
			cancelButtonText: '取消',
			type: 'warning'
		}).then(async () => {
			await agent.postAgentDoDeleteAgentNode({
				id: id
			})
			await loadAgentWorkflow(workflow.value.id)
		})
	}
}

/**
 * 删除连线
 */
const handleDeleteEdge = async (connection: Connection) => {
	if (connection.id) {
		ElMessageBox.confirm('确定要删除吗？', '提示', {
			confirmButtonText: '确定',
			cancelButtonText: '取消',
			type: 'warning'
		}).then(async () => {
			await agent.postAgentDoDeleteEdge({
				id: connection.id
			})
			await loadAgentWorkflow(workflow.value.id)
		})
	}
}

/**
 * 修改环境变量
 */
const handleChangeEnvVars = async (
	envVars: {
		name: string
		value: string
		type: 'string' | 'number' | 'boolean' | 'object' | 'array'
	}[]
) => {
	const response = await agent.postAgentDoSaveAgentVariables({
		appAgentId: workflow.value.id,
		conversation_variables: [],
		env_variables: envVars
	})
	handleApiResult(response, '环境变量已保存', '保存环境变量失败')
	await loadAgentWorkflow(workflow.value.id)
}

/**
 * 连线取消时
 */
const onConnectionOpenNodeLibary = async (payload: {
	handle: ConnectStartEvent
	position: XYPosition
	event: MouseEvent
	parentId?: string
}) => {
	await nextTick()
	const { handle } = payload
	libaryRefferenceRef.value = handle.event?.target as HTMLElement
	showNodeLibary.value = true
	peddingHandlePayload.value = payload
}

/**
 * 节点库隐藏时
 */
const onHideNodeLibary = () => {
	peddingHandlePayload.value = undefined
	libaryRefferenceRef.value = undefined
	showNodeLibary.value = false
}

/**
 * 连线添加按钮触发
 */
const handleClickConectionAdd = (connection: Connection) => {
	const el = document.querySelector(`[edge-add-btn="${connection.id}"]`) as HTMLElement
	const screenToFlowCoordinate = workflowRef.value?.getVueFlow()?.screenToFlowCoordinate

	if (el && screenToFlowCoordinate) {
		const rect = el.getBoundingClientRect()
		const position = screenToFlowCoordinate({
			x: rect.left,
			y: rect.top
		})

		libaryRefferenceRef.value = el
		showNodeLibary.value = true
		peddingHandlePayload.value = {
			by: 'edge',
			position,
			connection
		}
	}
}

onBeforeUnmount(() => {
	if (saveAgentTimer.value) window.clearTimeout(saveAgentTimer.value)
	if (saveVarsTimer.value) window.clearTimeout(saveVarsTimer.value)
	resetDisplayedNodeStatuses()
	layout?.setMainStyle({})
})
</script>
