<template>
	<div class="chat-container">
		<!-- 左侧边栏 -->
		<ChatSidebar
			:conversations="conversations"
			:active-id="activeConversationId"
			:has-more="hasMore"
			:is-loading-more="isLoadingMore"
			@new-chat="handleNewChat"
			@select-conversation="handleSelectConversation"
			@conv-command="handleConvCommand"
			@load-more="loadMoreConversations"
		/>

		<!-- 右侧聊天区域 -->
		<div class="chat-main">
			<ChatHeader :title="activeConversationTitle">
				<template #actions>
					<!-- 展示/隐藏表单 -->
					<el-popover
						:visible="showForm"
						placement="bottom"
						trigger="manual"
						:width="520"
						popper-class="workflow-form-popper"
					>
						<div v-loading="workflowFormLoading" class="workflow-form">
							<div class="workflow-form__title">{{ t('pages.chat.workflowChatFormTitle') }}</div>
							<InputTab
								:start-node="workflowFormStartNode"
								:visible-variables="workflowVisibleVariables"
								:input-values="workflowInputValues"
								:json-drafts="workflowJsonDrafts"
								:validation-errors="workflowValidationErrors"
								:is-running="isLoading"
								:show-action-bar="false"
								:pane-style="workflowFormPaneStyle"
							/>
						</div>
						<template #reference>
							<IconButton
								v-if="hasVisibleForm"
								icon="lucide:sliders-horizontal"
								:type="showForm ? 'primary' : 'default'"
								:icon-color="showForm ? '#fff' : undefined"
								height="18"
								width="18"
								@click="showForm = !showForm"
								size="small"
								square
							/>
						</template>
					</el-popover>
					<!-- 创建连接 -->
					<el-button v-if="!isPresetMode" @click="shareConversation">
						<span class="flex items-center gap-4px">
							<Icon icon="lucide:share-2" />
							<span>{{ t('pages.chat.generateLink') }}</span>
						</span>
					</el-button>
				</template>
			</ChatHeader>

			<MessageList
				ref="messageListRef"
				:messages="messages"
				:loading="isLoading"
				@retry="handleRetry"
				@add-to-kb="handleAddKb"
				@card-submit="handleSend"
			>
				<template #message-extra="{ item }">
					<div class="min-w-400px">
						<WorkflowTraceBubble v-if="item.workflowTraceVisible" :message="item" />
					</div>
				</template>

				<div
					v-if="settingsDraft.type === 'agent' && agentPromptsItems?.length"
					style="margin-top: 12px; width: 80%"
				>
					<Prompts
						:title="t('pages.chat.quickStartTitle')"
						:items="agentPromptsItems"
						@item-click="handlePromptItemClick"
						wrap
					/>
				</div>
			</MessageList>

			<!-- 对话框 -->
			<ChatInput
				v-model="senderValue"
				ref="chatInputRef"
				:loading="isLoading"
				:attachments="currentAttachments"
				@submit="handleSend"
				@cancel="handleCancel"
			>
				<template #header v-if="!isPresetMode && settingsDraft.type !== 'flow'">
					<div class="px-8px py-12px flex items-center gap-12px">
						<div class="flex items-center gap-4px">
							<!-- <div class="title">知识库：</div> -->
							<el-select
								class="w-180px!"
								:placeholder="t('pages.chat.selectKnowledgeBasePlaceholder')"
								v-model="settingsDraft.knowledgeBaseIds"
								:options="knowledgeBaseOptions"
								@change="fetchKnowledgeOptions"
								multiple
							/>
						</div>
						<div class="flex items-center gap-4px">
							<!-- <div class="title">知识：</div> -->
							<el-select
								class="w-180px!"
								:placeholder="t('pages.chat.selectKnowledgePlaceholder')"
								v-model="settingsDraft.knowledgeIds"
								:options="knowledgeOptions"
								multiple
							/>
						</div>
					</div>
				</template>
				<template #prefix-extra>
					<!-- 智能体与智能体编排 -->
					<el-cascader
						v-if="!isPresetMode"
						v-model="targetSelection"
						:options="getTargetOptions"
						:placeholder="t('pages.chat.selectPlaceholder')"
						popper-class="target-popper"
						class="chat-target-cascader"
						@change="handleChangeTarget"
						filterable
					/>
					<!-- 附件 -->
					<el-badge :value="currentAttachments.length" :hidden="!currentAttachments.length">
						<el-button
							v-if="showImageUploadButton"
							round
							plain
							color="#626aef"
							@click="imageUploadDialogVisible = true"
						>
							<el-icon>
								<PictureFilled />
							</el-icon>
						</el-button>
					</el-badge>
				</template>

				<template #action>
					<el-select
						v-if="!isPresetMode && settingsDraft.type !== 'flow'"
						:placeholder="t('pages.chat.selectModelPlaceholder')"
						placement="top"
						v-model="settingsDraft.summaryModelId"
						:options="modelOptions"
						class="w-120px!"
					/>
				</template>
			</ChatInput>
		</div>

		<!-- 重命名对话弹窗 -->
		<el-dialog
			v-model="renameDialogVisible"
			:title="t('pages.chat.renameDialogTitle')"
			width="400px"
		>
			<el-input v-model="renameInput" :placeholder="t('pages.chat.renamePlaceholder')" />
			<template #footer>
				<el-button @click="renameDialogVisible = false">{{ t('pages.chat.cancel') }}</el-button>
				<el-button type="primary" @click="handleRename">{{ t('common.confirm') }}</el-button>
			</template>
		</el-dialog>

		<!-- 图片上传 -->
		<el-dialog
			v-model="imageUploadDialogVisible"
			:title="t('pages.chat.imageUploadTitle')"
			width="560px"
		>
			<FileUploadInput
				v-model="currentAttachments"
				:multiple="true"
				:allow-link-input="false"
				:tip="t('pages.chat.imageUploadTip')"
				style="width: 100%"
			/>
			<template #footer>
				<el-button @click="imageUploadDialogVisible = false">{{
					t('pages.chat.cancel')
				}}</el-button>
				<el-button type="primary" @click="imageUploadDialogVisible = false">{{
					t('pages.chat.imageUploadDone')
				}}</el-button>
			</template>
		</el-dialog>

		<el-dialog v-model="shareDialogVisible" :title="t('pages.chat.shareDialogTitle')" width="620px">
			<div class="share-dialog">
				<div class="share-dialog__tip">
					{{ t('pages.chat.shareDialogTip') }}
				</div>
				<el-input v-model="shareUrl" type="textarea" :rows="4" readonly />
			</div>
			<template #footer>
				<el-button @click="shareDialogVisible = false">{{ t('common.close') }}</el-button>
				<el-button type="primary" @click="copyShareUrl">{{
					t('pages.chat.copyShareLink')
				}}</el-button>
			</template>
		</el-dialog>
	</div>

	<AddKbModal ref="addkbRef" />
</template>

<script setup lang="ts">
import { computed, nextTick, onMounted, reactive, ref, watch } from 'vue'
import { useRoute } from 'vue-router'
import { ElMessage, ElMessageBox } from 'element-plus'
import { PictureFilled } from '@element-plus/icons-vue'
import { useI18n } from '@/composables/useI18n'
import FileUploadInput from '@/features/fileUpload/FileUploadInput.vue'
import InputTab from '@/features/RunWorkflow/components/InputTab.vue'
import { buildExecuteParams, createEmptyValue } from '@/features/RunWorkflow/utils'
import { useChatStream } from './composables/useChatStream'
import { Prompts } from 'vue-element-plus-x'
import {
	buildChatRequestBody,
	createSession,
	deleteSession,
	getAgentInfo,
	getAgentOptions,
	getFlowOptions,
	getChatUrl,
	getKnowledgeBaseOptions,
	getModelOptions,
	getSessionList,
	getSessionMessages,
	updateSessionName,
	type ChatOptionItem
} from './api/chat.api'
import ChatHeader from './components/ChatHeader.vue'
import ChatInput from '@/components/Chat/ChatInput.vue'
import ChatSidebar from './components/ChatSidebar.vue'
import MessageList from '@/components/Chat/MessageList.vue'
import AddKbModal from './components/AddKbModal.vue'
import WorkflowTraceBubble from '@/features/ChatDrawer/WorkflowTraceBubble.vue'
import { agent, agentApplication, knowledge, aiChat } from '@repo/api-service'

import type {
	BubbleMessage,
	ChatReference,
	ChatSseMessage,
	ChatTargetConfig,
	ChatTargetType,
	ChatToolCall,
	ChatToolResult,
	Conversation
} from './types'
import type { PromptsItemsProps } from 'vue-element-plus-x/types/Prompts'
import type { WorkflowUploadFile } from '@/features/fileUpload/shared'
import type { CSSProperties } from 'vue'
import type { IWorkflowNode } from '@repo/workflow'
import type { FileType, FormType, StartVariable } from '@/nodes/src/start'
import type { RunnerNodeState } from '@/store/modules/runner.store'
import { Icon, IconButton } from '@repo/ui'

const { t } = useI18n()
const { isLoading, cancelRequest, streamChat } = useChatStream()
const route = useRoute()

const conversations = ref<Conversation[]>([])
const sessionConfigMap = reactive<Record<string, ChatTargetConfig>>({})
const activeConversationId = ref('')
const activeTargetType = ref<ChatTargetType>('agent')
const imageUploadDialogVisible = ref(false)
const currentAttachments = ref<WorkflowUploadFile[]>([])
const allowImageUpload = ref(false)
const agentOptions = ref<ChatOptionItem[]>([])
const flowOptions = ref<ChatOptionItem[]>([])
const targetSelection = ref<[ChatTargetType, string]>(['agent', ''])
const knowledgeBaseOptions = ref<ChatOptionItem[]>([])
const knowledgeOptions = ref<{ label: string; value: string }[]>([])
const modelOptions = ref<ChatOptionItem[]>([])
const currentPage = ref(1)
const pageSize = ref(20)
const hasMore = ref(true)
const isLoadingMore = ref(false)
const messages = ref<BubbleMessage[]>([])
const renameDialogVisible = ref(false)
const renameInput = ref('')
const renamingConvId = ref('')
const senderValue = ref('')
const messageListRef = ref()
const activeStreamToken = ref('')
const chatInputRef = ref<InstanceType<typeof ChatInput>>()
const agentPromptsItems = ref<PromptsItemsProps[]>()
const addkbRef = ref<InstanceType<typeof AddKbModal>>()
const shareDialogVisible = ref(false)
const shareUrl = ref('')
const presetConfig = ref<ChatTargetConfig | null>(null)
const isPresetMode = computed(() => !!presetConfig.value)
const showForm = ref(false)
const workflowFormLoading = ref(false)
const workflowStartVariables = ref<StartVariable[]>([])
const workflowInputValues = reactive<Record<string, any>>({})
const workflowJsonDrafts = reactive<Record<string, string>>({})
const workflowValidationErrors = reactive<Record<string, string>>({})
const workflowFormPaneStyle: CSSProperties = {
	marginTop: '0',
	padding: '0'
}
let scrollToBottomPending = false

// 联动下拉列表
const getTargetOptions = computed(() => {
	return [
		{
			label: t('pages.chat.settingsAgent'),
			value: 'agent',
			children: agentOptions.value
		},
		{
			label: t('sidebar.menu.orchestration'),
			value: 'flow',
			children: flowOptions.value
		}
	]
})

/**
 * 解析当前级联选择器的显示信息（类型标签、名称、Tag 颜色）
 */
const selectionDisplayInfo = computed(() => {
	const [type, id] = targetSelection.value
	if (!type || !id) return null

	const isAgent = type === 'agent'
	const typeLabel = isAgent ? t('pages.chat.settingsAgent') : t('sidebar.menu.orchestration')
	const options = isAgent ? agentOptions.value : flowOptions.value
	const matched = options.find((o) => o.value === id)

	return {
		typeLabel,
		name: matched?.label || id,
		tagType: isAgent ? 'success' : 'warning'
	}
})
const scrollToBottom = async () => {
	if (scrollToBottomPending) return
	scrollToBottomPending = true
	await nextTick()
	scrollToBottomPending = false
	messageListRef.value?.scrollToBottom?.()
}

const scrollToBottomIfNearBottom = () => {
	if (messageListRef.value?.isNearBottom?.() === false) return
	scrollToBottom()
}

/**
 * 创建知识
 */
const handleAddKb = (txt: string) => {
	addkbRef.value?.open(txt)
}

/**
 * 计算当前激活会话的标题
 */
const activeConversationTitle = computed(() => {
	const conv = conversations.value.find((c) => c.id === activeConversationId.value)
	return conv ? conv.title : t('pages.chat.newConversation')
})

/**
 * 判断是否显示图片上传按钮
 * agent 模式：根据智能体配置决定是否显示
 * flow 模式：始终显示
 */
const showImageUploadButton = computed(
	() =>
		(activeTargetType.value === 'agent' && allowImageUpload.value) ||
		activeTargetType.value === 'flow'
)

/**
 * 计算当前聊天配置对象，用于发送消息时使用
 */
const currentChatConfig = computed<ChatTargetConfig>(() => ({
	type: activeTargetType.value,
	knowledgeBaseIds: activeTargetType.value === 'flow' ? [] : [...settingsDraft.knowledgeBaseIds],
	knowledgeIds: activeTargetType.value === 'flow' ? [] : [...settingsDraft.knowledgeIds],
	summaryModelId: activeTargetType.value === 'flow' ? '' : settingsDraft.summaryModelId,
	disableTitle: settingsDraft.disableTitle,
	enableMemory: settingsDraft.enableMemory,
	agentId: settingsDraft.agentId,
	agentEnabled: settingsDraft.agentEnabled,
	webSearchEnabled: settingsDraft.webSearchEnabled,
	images: currentAttachments.value.map((item) => item.path || item.id || item.name).filter(Boolean)
}))

/**
 * 获取默认的智能体 ID
 * @returns 第一个智能体的 value，若无则返回空字符串
 */
const getDefaultAgentId = () => {
	return agentOptions.value[0]?.value || ''
}

const getDefaultFlowId = () => {
	return flowOptions.value[0]?.value || ''
}

onMounted(async () => {
	presetConfig.value = parsePresetConfigFromRoute()
	await loadChatOptions()
	if (presetConfig.value) {
		await initializePresetConversation(presetConfig.value)
		return
	}
	await loadConversations()
	// 默认选中第一个会话
	// if (conversations.value.length > 0 && !activeConversationId.value) {
	// 	activeConversationId.value = conversations.value?.[0]?.id!
	// 	await loadConversationMessages(activeConversationId.value)
	// 	await scrollToBottom()
	// }
})

/**
 * 创建默认的聊天目标配置
 * @param type - 聊天目标类型，默认为 'agent'
 * @returns 默认配置对象
 */
const createDefaultConfig = (type: ChatTargetType = 'agent'): ChatTargetConfig => {
	return {
		type,
		knowledgeBaseIds: [],
		knowledgeIds: [],
		summaryModelId: '',
		disableTitle: false,
		enableMemory: true,
		agentId: type === 'flow' ? getDefaultFlowId() : type === 'agent' ? getDefaultAgentId() : '',
		agentEnabled: true,
		webSearchEnabled: false,
		images: []
	}
}

const settingsDraft = reactive<ChatTargetConfig>(createDefaultConfig('agent'))
const workflowVisibleVariables = computed(() =>
	workflowStartVariables.value.filter((item) => !item.is_hide)
)
const workflowFormStartNode = computed<IWorkflowNode | null>(() =>
	settingsDraft.type === 'flow'
		? ({
				id: 'chat-workflow-start',
				type: 'canvas-node',
				position: { x: 0, y: 0 },
				data: {
					nodeType: 'start',
					variables: workflowStartVariables.value
				}
			} as unknown as IWorkflowNode)
		: null
)
const hasVisibleForm = computed(
	() => settingsDraft.type === 'flow' && workflowVisibleVariables.value.length > 0
)

const resetWorkflowFormValidation = () => {
	Object.keys(workflowValidationErrors).forEach((key) => {
		delete workflowValidationErrors[key]
	})
}

const resetWorkflowFormState = () => {
	workflowStartVariables.value = []
	Object.keys(workflowInputValues).forEach((key) => {
		delete workflowInputValues[key]
	})
	Object.keys(workflowJsonDrafts).forEach((key) => {
		delete workflowJsonDrafts[key]
	})
	resetWorkflowFormValidation()
}

const formatJsonDraft = (value: unknown, fallback = '{}') => {
	if (value === undefined || value === null || value === '') return fallback
	try {
		return JSON.stringify(value, null, 2)
	} catch {
		return fallback
	}
}

const normalizeFormType = (formType?: string): FormType => {
	const allowed = new Set<FormType>([
		'text-input',
		'text-area',
		'select',
		'number',
		'checkbox',
		'file',
		'file-list',
		'json_object'
	])
	return allowed.has(formType as FormType) ? (formType as FormType) : 'text-input'
}

const normalizeWorkflowVariable = (item: Record<string, any>): StartVariable | null => {
	const name = `${item?.name || ''}`.trim()
	if (!name) return null

	const formType = normalizeFormType(item.formType)
	return {
		name,
		label: item.label || name,
		max_length: item.max_length,
		default_value: item.default_value,
		json: item.json,
		is_require: !!item.is_require,
		is_hide: !!item.is_hide,
		formType,
		options: Array.isArray(item.options) ? item.options : [],
		file_types: Array.isArray(item.file_types) ? (item.file_types as FileType[]) : [],
		file_extensions: Array.isArray(item.file_extensions) ? item.file_extensions : [],
		allow_link_input: item.allow_link_input
	}
}

const initializeWorkflowFormValues = () => {
	Object.keys(workflowInputValues).forEach((key) => {
		delete workflowInputValues[key]
	})
	Object.keys(workflowJsonDrafts).forEach((key) => {
		delete workflowJsonDrafts[key]
	})

	workflowStartVariables.value.forEach((variable) => {
		const initialValue =
			variable.default_value !== undefined
				? structuredClone(variable.default_value)
				: createEmptyValue(variable.formType)

		if (variable.formType === 'json_object') {
			workflowInputValues[variable.name] =
				initialValue && typeof initialValue === 'object' && !Array.isArray(initialValue)
					? initialValue
					: {}
			workflowJsonDrafts[variable.name] = formatJsonDraft(workflowInputValues[variable.name], '{}')
			return
		}

		workflowInputValues[variable.name] = initialValue
	})
}

const loadWorkflowExecuteInfo = async (id: string) => {
	resetWorkflowFormState()
	if (!id) return

	workflowFormLoading.value = true
	try {
		const res = await agent.postAgentGetExecuteInfo({ id })
		if (!res?.isSuccess || !res.result) {
			throw new Error('load workflow execute info failed')
		}

		workflowStartVariables.value = (res.result.form_variables || [])
			.map((item) => normalizeWorkflowVariable(item as Record<string, any>))
			.filter(Boolean) as StartVariable[]
		initializeWorkflowFormValues()
		showForm.value = workflowVisibleVariables.value.length > 0
	} catch (error) {
		console.error('loadWorkflowExecuteInfo error', error)
		ElMessage.error(t('pages.editor.loadAgentInfoFailed'))
	} finally {
		workflowFormLoading.value = false
	}
}

const buildWorkflowChatParams = () => {
	if (settingsDraft.type !== 'flow') return {}

	const params = buildExecuteParams({
		startVariables: workflowStartVariables.value,
		inputValues: workflowInputValues,
		jsonDrafts: workflowJsonDrafts,
		validationErrors: workflowValidationErrors,
		translateFieldRequired: (name) =>
			t('pages.runWorkflow.fieldRequired', {
				name
			}),
		translateInvalidJson: () => t('pages.runWorkflow.invalidJson'),
		translateFieldTooLong: (name, max) =>
			t('pages.runWorkflow.fieldTooLong', {
				name,
				max
			})
	})

	if (!params) {
		showForm.value = true
		ElMessage.warning(t('pages.runWorkflow.inputPanel.completeRequired'))
		return false
	}

	return params
}

/**
 * 深拷贝聊天配置对象
 * @param config - 源配置对象
 * @returns 拷贝后的新配置对象
 */
const cloneConfig = (config: ChatTargetConfig): ChatTargetConfig => {
	const isFlow = config.type === 'flow'
	return {
		type: config.type,
		knowledgeBaseIds: isFlow ? [] : [...config.knowledgeBaseIds],
		knowledgeIds: isFlow ? [] : [...config.knowledgeIds],
		summaryModelId: isFlow ? '' : config.summaryModelId,
		disableTitle: config.disableTitle,
		enableMemory: config.enableMemory,
		agentId: config.agentId,
		agentEnabled: config.agentEnabled,
		webSearchEnabled: config.webSearchEnabled,
		images: [...config.images]
	}
}

/**
 * 选择智能体活智能编排
 */
const handleChangeTarget = async (value: [ChatTargetType, string] | string[]) => {
	const [type, id] = value as [ChatTargetType, string]
	if (!type || !id) return

	activeTargetType.value = type
	settingsDraft.type = type
	settingsDraft.agentId = id

	if (type === 'flow') {
		settingsDraft.knowledgeBaseIds = []
		settingsDraft.knowledgeIds = []
		settingsDraft.summaryModelId = ''
		settingsDraft.agentEnabled = true
		settingsDraft.webSearchEnabled = false
		knowledgeOptions.value = []
		currentAttachments.value = []
		agentPromptsItems.value = undefined
		allowImageUpload.value = true
		await loadWorkflowExecuteInfo(id)
		return
	}

	showForm.value = false
	resetWorkflowFormState()
	await refreshAgentUploadCapability(id)
}

const normalizeConfig = (config: Partial<ChatTargetConfig>): ChatTargetConfig => {
	const base = createDefaultConfig(config.type || 'agent')
	const type = config.type || base.type
	const isFlow = type === 'flow'
	return {
		...base,
		...config,
		type,
		knowledgeBaseIds:
			!isFlow && Array.isArray(config.knowledgeBaseIds) ? config.knowledgeBaseIds : [],
		knowledgeIds: !isFlow && Array.isArray(config.knowledgeIds) ? config.knowledgeIds : [],
		summaryModelId: isFlow ? '' : config.summaryModelId || '',
		disableTitle: config.disableTitle ?? base.disableTitle,
		enableMemory: config.enableMemory ?? base.enableMemory,
		agentId:
			config.agentId || (isFlow ? getDefaultFlowId() : type === 'agent' ? getDefaultAgentId() : ''),
		agentEnabled: config.agentEnabled ?? base.agentEnabled,
		webSearchEnabled: config.webSearchEnabled ?? base.webSearchEnabled,
		images: []
	}
}

const parsePresetConfigFromRoute = () => {
	if (route.query.preset !== '1' || typeof route.query.chatConfig !== 'string') return null
	try {
		const rawConfig = JSON.parse(decodeURIComponent(route.query.chatConfig))
		return normalizeConfig(rawConfig)
	} catch (error) {
		console.error('Failed to parse chat preset config', error)
		ElMessage.error(t('pages.chat.invalidShareLink'))
		return null
	}
}

const buildShareUrl = () => {
	const config = normalizeConfig(currentChatConfig.value)
	const url = new URL(window.location.href)
	const hashPath = route.path || '/chat'
	const params = new URLSearchParams()
	params.set('preset', '1')
	params.set('chatConfig', encodeURIComponent(JSON.stringify(config)))
	url.hash = `${hashPath}?${params.toString()}`
	return url.toString()
}

const shareConversation = () => {
	shareUrl.value = buildShareUrl()
	shareDialogVisible.value = true
}

const copyShareUrl = async () => {
	if (!shareUrl.value) return
	try {
		await navigator.clipboard.writeText(shareUrl.value)
		ElMessage.success(t('pages.chat.shareLinkCopied'))
	} catch {
		ElMessage.warning(t('pages.chat.shareLinkCopyFailed'))
	}
}

const initializePresetConversation = async (config: ChatTargetConfig) => {
	syncSettingsDraft(config)
	if (config.type === 'flow') {
		await loadWorkflowExecuteInfo(config.agentId)
	} else {
		await fetchKnowledgeOptions(config.knowledgeBaseIds)
		await refreshAgentUploadCapability(config.agentId)
	}
	await loadConversations(true)

	const res = await createSession()
	if (!res.isSuccess || !res.result) return

	await loadConversations(true)
	const targetConv = conversations.value.find((c) => c.id === res.result)
	if (targetConv) {
		sessionConfigMap[targetConv.id] = cloneConfig(config)
		await handleSelectConversation(targetConv.id)
	}
}

/**
 * 同步配置到当前的设置草稿中
 * @param config - 要同步的配置对象
 */
const syncSettingsDraft = (config: ChatTargetConfig) => {
	const next = createDefaultConfig(config.type)
	Object.assign(next, config)
	activeTargetType.value = next.type
	Object.assign(settingsDraft, next)
	targetSelection.value = [next.type, next.agentId]
}

/**
 * 加载聊天所需的选项数据（智能体、知识库、模型）
 * 并在加载完成后设置默认的模型和智能体
 */
const loadChatOptions = async () => {
	try {
		const [agents, knowledgeBases, models, flows] = await Promise.all([
			getAgentOptions(),
			getKnowledgeBaseOptions(),
			getModelOptions('', 'KnowledgeQA'),
			getFlowOptions()
		])
		agentOptions.value = agents
		knowledgeBaseOptions.value = knowledgeBases
		modelOptions.value = models
		flowOptions.value = flows

		if (activeTargetType.value === 'agent' && !settingsDraft.agentId) {
			settingsDraft.agentId = getDefaultAgentId()
		}
		if (activeTargetType.value === 'flow' && !settingsDraft.agentId) {
			settingsDraft.agentId = getDefaultFlowId()
		}
		targetSelection.value = [settingsDraft.type, settingsDraft.agentId]
		if (settingsDraft.type === 'flow' && settingsDraft.agentId) {
			await loadWorkflowExecuteInfo(settingsDraft.agentId)
		}
	} catch (error) {
		console.error('Failed to load chat options', error)
	}
}

/**
 * 刷新智能体的图片上传能力配置
 * @param agentId - 智能体 ID
 */
const refreshAgentUploadCapability = async (agentId: string) => {
	allowImageUpload.value = false
	if (!agentId || activeTargetType.value !== 'agent') return

	try {
		const res = await getAgentInfo(agentId)
		allowImageUpload.value = !!res.result?.config?.img_vlm_config?.image_upload_enabled
		if (!allowImageUpload.value) {
			currentAttachments.value = []
		}
	} catch (error) {
		console.error('Failed to load agent info', error)
	}
}

watch(
	() => [settingsDraft.type, chatInputRef.value],
	([type, chatRef]) => {
		if (type && chatRef) {
			const chat = chatInputRef.value?.getInstance()
			if (type !== 'model' && type !== 'flow') {
				chat?.openHeader()
			} else {
				chat?.closeHeader()
			}
		}
	},
	{
		immediate: true
	}
)

/**
 * 处理智能体选择变更事件
 * @param value - 选中的智能体 ID
 */
const handleAgentSelectChange = async (value: string) => {
	settingsDraft.agentId = value
	targetSelection.value = [settingsDraft.type, value]
	await refreshAgentUploadCapability(value)
}

/**
 * 获取建议问题列表并更新提示项
 */
const getSuggestQuestion = async () => {
	if (!settingsDraft.agentId) return

	// 获取建议问题
	const res = await agentApplication.postAiAgentSuggestedQuestions({
		id: settingsDraft.agentId,
		limit: 6
	})

	if (res.result) {
		agentPromptsItems.value = res.result.map((item, index) => {
			return {
				key: index,
				label: item.question,
				itemStyle: {
					height: 'auto',
					width: 'calc(50% - 6px)',
					boxSizing: 'border-box'
				}
			}
		})
	}
}

watch(
	() => settingsDraft.agentId,
	async () => {
		if (settingsDraft.type === 'agent') getSuggestQuestion()
	},
	{
		immediate: true
	}
)

/**
 * 处理提示项点击事件
 * @param item - 被点击的提示项
 */
const handlePromptItemClick = (item: PromptsItemsProps) => {
	handleSend(item.label)
}

/**
 * 追加思考内容到消息对象
 * @param message - 消息对象
 * @param text - 要追加的思考文本
 */
const appendThinking = (message: BubbleMessage, text?: string) => {
	const thought = text
	if (!thought) return
	message.eventThinkingText = `${message.eventThinkingText || ''}${thought}`
	updateMessageThinking(message)
}

/**
 * 解析回答中的思考状态
 * 提取 <think> 标签内的内容和标签外的回答内容
 * @param text - 原始文本
 * @returns 包含思考内容、回答内容及标签状态的對象
 */
const parseAnswerThinkState = (text?: string) => {
	const raw = `${text || ''}`
	if (!raw) return { thinking: '', answer: '', hasThinkStart: false, hasThinkEnd: false }
	const hasThinkStart = /^<think\b[^>]*>/i.test(raw.trimStart())

	const thinking: string[] = []
	let answer = raw

	if (hasThinkStart) {
		answer = answer.replace(
			/^\s*<think\b[^>]*>([\s\S]*?)(?:<\/think>|$)/i,
			(_match, inner: string) => {
				const piece = inner
				if (piece) thinking.push(piece)
				return ''
			}
		)
	}

	return {
		thinking: thinking.join('\n\n'),
		answer: answer,
		hasThinkStart,
		hasThinkEnd: /<\/think>/i.test(raw)
	}
}

/**
 * 更新消息对象的思考内容字段
 * @param message - 消息对象
 * @param answerThinking - 回答中的思考内容
 */
const updateMessageThinking = (message: BubbleMessage, answerThinking = '') => {
	message.answerThinkingText = answerThinking
	message.thinking = [message.eventThinkingText, message.answerThinkingText]
		.map((item) => item)
		.filter(Boolean)
		.join('\n\n')
}

/**
 * 更新消息对象的内容字段，并处理思考状态
 * @param message - 消息对象
 */
const updateMessageContent = (message: BubbleMessage) => {
	const answerParts = parseAnswerThinkState(message.answerText || message.output)
	message.content = answerParts.answer

	updateMessageThinking(message, answerParts.thinking)

	if (answerParts.hasThinkStart) {
		message.thinkingOpen = !answerParts.hasThinkEnd
	} else if (message.streamCompleted) {
		message.thinkingOpen = false
	}
}

/**
 * 判断消息是否具有可渲染的内容
 * @param message - 消息对象
 * @returns 是否有可渲染内容
 */
const hasRenderableContent = (message: BubbleMessage) => {
	return !!(
		message.content ||
		message.answerText ||
		message.thinking ||
		(message.toolCalls || []).length ||
		(message.toolResults || []).length ||
		(message.references || []).length ||
		message.error
	)
}

const appendInlineError = (message: BubbleMessage, error: string) => {
	const trimmedError = error.trim()
	if (!trimmedError) return
	const inlineErrors = message.inlineErrors || []
	if (!inlineErrors.includes(trimmedError)) {
		message.inlineErrors = [...inlineErrors, trimmedError]
	}
}

/**
 * 创建用户消息对象
 * @param content - 消息内容
 * @param updateTime - 更新时间
 * @returns 用户消息对象
 */
const createUserMessage = (content: string, updateTime?: string): BubbleMessage => {
	const id = `user-${Date.now()}-${Math.random()}`
	return {
		id,
		key: id,
		role: 'user',
		placement: 'end',
		content: content,
		rawText: content,
		loading: false,
		shape: 'corner',
		variant: 'outlined',
		isMarkdown: false,
		typing: false,
		isFog: false,
		streamCompleted: true,
		updateTime
	}
}

/**
 * 获取当前附件的文件 ID 列表
 * @returns 文件 ID 数组
 */
const getAttachmentFileIds = () => {
	return currentAttachments.value
		.map((item) => item.path || item.id || item.name)
		.filter(Boolean) as string[]
}

/**
 * 创建带有附件的用户消息对象
 * @param content - 消息内容
 * @param updateTime - 更新时间
 * @returns 用户消息对象
 */
const createUserMessageWithAttachments = (content: string, updateTime?: string): BubbleMessage => {
	const message = createUserMessage(content, updateTime)
	const fileIds = getAttachmentFileIds()
	if (fileIds.length) {
		message.message_files = fileIds
	}
	return message
}

/**
 * 创建 AI 消息对象
 * @param updateTime - 更新时间
 * @returns AI 消息对象
 */
const createAiMessage = (updateTime?: string): BubbleMessage => {
	const id = `ai-${Date.now()}-${Math.random()}`
	return {
		id,
		msgId: '',
		key: id,
		role: 'ai',
		placement: 'start',
		content: '',
		answerText: '',
		thinking: '',
		thinkingOpen: false,
		toolCalls: [],
		toolResults: [],
		references: [],
		assistantMessageId: '',
		loading: true,
		shape: 'corner',
		variant: 'filled',
		isMarkdown: true,
		typing: false,
		isFog: true,
		streamCompleted: false,
		updateTime,
		workflowTraceVisible: activeTargetType.value === 'flow',
		workflowTraceNodes: []
	}
}

/**
 * 同步消息身份
 * @param message
 * @param event
 */
const syncMessageIdentity = (message: BubbleMessage, event: ChatSseMessage) => {
	if (event?.assistant_message_id) {
		message.assistantMessageId = event.assistant_message_id
		message.msgId = event.id
	}
}

/**
 * 标准化工具调用事件数据
 * @param event - SSE 事件对象
 * @returns 标准化的工具调用对象
 */
const normalizeToolCall = (event: ChatSseMessage): ChatToolCall => {
	const data = event.data || {}
	return {
		id: data.id || event.id,
		toolCallId: data.tool_call_id || data.toolCallId,
		toolName: data.tool_name || data.toolName,
		arguments: data.arguments || data.params || data.args || {},
		content: event.content || data.content
	}
}

/**
 * 标准化工具结果事件数据
 * @param event - SSE 事件对象
 * @returns 标准化的工具结果对象
 */
const normalizeToolResult = (event: ChatSseMessage): ChatToolResult => {
	const data = event.data || {}
	return {
		id: data.id || event.id,
		toolCallId: data.tool_call_id || data.toolCallId,
		toolName: data.tool_name || data.toolName,
		success: data.success,
		error: data.error,
		output: data.output,
		thought: data.thought,
		durationMs: data.duration_ms || data.durationMs,
		displayType: data.display_type || data.displayType,
		contentItems: data.content_items || data.contentItems
	}
}

/**
 * 标准化引用文献事件数据
 * @param event - SSE 事件对象
 * @returns 标准化的引用文献数组
 */
const normalizeReferences = (event: ChatSseMessage): ChatReference[] => {
	const data = event.data || {}
	const refs = data.knowledge_references || data.references || []
	if (!Array.isArray(refs)) return []
	return refs.map((item: Record<string, any>) => ({
		id: item.id,
		knowledgeBaseId: item.knowledge_base_id,
		knowledgeId: item.knowledge_id,
		knowledgeTitle: item.knowledge_title,
		knowledgeFilename: item.knowledge_filename,
		knowledgeDescription: item.knowledge_description,
		content: item.content,
		matchedContent: item.matched_content || item.matchedContent,
		score: item.score,
		chunkType: item.chunk_type || item.chunkType
	}))
}

const normalizeChatContentEvent = (raw: Record<string, any>): ChatSseMessage => {
	const data = raw.data && typeof raw.data === 'object' ? raw.data : raw
	const content =
		raw.content ??
		raw.answer ??
		raw.output ??
		raw.delta ??
		raw.text ??
		data.content ??
		data.answer ??
		data.output ??
		data.delta ??
		data.text ??
		''

	return {
		id: raw.id || data.id,
		response_type:
			raw.response_type ||
			raw.responseType ||
			raw.type ||
			raw.event ||
			(content ? 'agent_query' : 'complete'),
		content: `${content || ''}`,
		done: raw.done || raw.is_end || raw.finished,
		data
	}
}

const normalizeWorkflowRunnerEvent = (event: ChatSseMessage): ChatSseMessage => {
	const raw = event as ChatSseMessage & {
		cmd?: string
		msg?: Record<string, any>
		result?: unknown
		errorMsg?: string
	}

	switch (raw.cmd) {
		case 'CMD_AGENT_CHAT_MSG':
			return normalizeChatContentEvent(raw.msg || {})
		case 'CMD_AGENT_FINISH_MSG':
			return {
				response_type: 'complete',
				done: true,
				data: { result: raw.result }
			}
		case 'CMD_AGENT_ERROR_MSG':
		case 'CMD_CONNECT_ERROR_MSG':
			return {
				response_type: 'error',
				content: raw.errorMsg || t('pages.chat.unknownError'),
				done: true,
				data: raw as unknown as Record<string, any>
			}
		case 'CMD_AGENT_SUSPEND_MSG':
			return {
				response_type: 'error',
				content: t('pages.runWorkflow.suspended'),
				done: true,
				data: raw as unknown as Record<string, any>
			}
		default:
			return event
	}
}

const createRunnerNodeState = (
	node: Record<string, any>,
	status: RunnerNodeState['status']
): RunnerNodeState => ({
	nodeId: node.nodeId || node.id || '',
	nodeName: node.nodeName || node.name || '',
	nodeType: node.nodeType || node.type || '',
	status,
	lastUpdateTime: node.time,
	track: node.track
})

const syncWorkflowTraceNode = (
	message: BubbleMessage,
	node: Record<string, any> | undefined,
	partial: Partial<RunnerNodeState>
) => {
	if (!node) return
	const nodeId = node.nodeId || node.id
	if (!nodeId) return

	message.workflowTraceVisible = true
	const nodes: RunnerNodeState[] = Array.isArray(message.workflowTraceNodes)
		? message.workflowTraceNodes
		: []
	const index = nodes.findIndex((item) => item.nodeId === nodeId)
	const existing = index >= 0 ? nodes[index] : undefined
	const next: RunnerNodeState = {
		...createRunnerNodeState(node, existing?.status || 'idle'),
		...existing,
		...partial,
		nodeId
	}

	if (index >= 0) {
		nodes[index] = next
	} else {
		nodes.push(next)
	}

	message.workflowTraceNodes = nodes
}

const applyWorkflowRunnerTraceEvent = (message: BubbleMessage, event: ChatSseMessage) => {
	const raw = event as ChatSseMessage & {
		cmd?: string
		time?: string
		node?: Record<string, any>
		track?: Record<string, any>
	}

	switch (raw.cmd) {
		case 'CMD_AGENT_RUNNING_MSG':
			message.workflowTraceVisible = true
			break
		case 'CMD_NODE_RUNNING_MSG':
		case 'CMD_NODE_ITERATION_RUNNING_MSG':
		case 'CMD_NODE_ITERATION_STEP_MSG':
			syncWorkflowTraceNode(message, raw.node, {
				status: 'running',
				lastUpdateTime: raw.time
			})
			break
		case 'CMD_NODE_FINISH_MSG':
			syncWorkflowTraceNode(message, raw.node, {
				status: raw.track?.is_success ? 'success' : 'failed',
				lastUpdateTime: raw.time,
				track: raw.track
			})
			break
		case 'CMD_NODE_ITERATION_FINISH_MSG':
			syncWorkflowTraceNode(message, raw.node, {
				status: 'success',
				lastUpdateTime: raw.time
			})
			break
		case 'CMD_AGENT_SUSPEND_MSG':
			message.workflowTraceVisible = true
			message.workflowTraceNodes = (message.workflowTraceNodes || []).map(
				(node: RunnerNodeState) =>
					node.status === 'running'
						? {
								...node,
								status: 'suspended',
								lastUpdateTime: raw.time || node.lastUpdateTime
							}
						: node
			)
			break
		default:
			break
	}
}

/**
 * 将结构化事件应用到消息对象上
 * 根据事件类型更新消息的不同字段（如回答、思考、工具调用等）
 * @param message - 消息对象
 * @param event - SSE 事件对象
 */
const applyStructuredEventToMessage = (message: BubbleMessage, event: ChatSseMessage) => {
	const data = event.data || {}
	switch (event.response_type) {
		case 'agent_query':
			syncMessageIdentity(message, event)
			if (event.content) {
				message.answerText = `${message.answerText || ''}${event.content}`
			}
			break
		case 'answer':
		case 'message':
		case 'delta':
			if (event.content) {
				message.answerText = `${message.answerText || ''}${event.content}`
			}
			break
		case 'thinking': {
			const thought = event.content || data.thought || data.content
			appendThinking(message, thought)
			message.thinkingOpen = true
			break
		}
		case 'tool_call':
			message.toolCalls = [...(message.toolCalls || []), normalizeToolCall(event)]
			break
		case 'tool_result': {
			message.toolResults = [...(message.toolResults || []), normalizeToolResult(event)]
			appendThinking(message, data.thought)
			break
		}
		case 'references':
			message.references = [...(message.references || []), ...normalizeReferences(event)]
			break
		case 'error':
			syncMessageIdentity(message, event)
			appendInlineError(
				message,
				event.content || data.error || data.message || t('pages.chat.unknownError')
			)
			message.streamCompleted = true
			break
		case 'session_title': {
			const title = data.title || event.content
			if (title) {
				const conv = conversations.value.find((item) => item.id === activeConversationId.value)
				if (conv) {
					conv.title = title
				}
			}
			break
		}
		case 'complete':
			message.streamCompleted = true
			message.total_duration_ms = data?.total_duration_ms
			message.total_steps = data?.total_steps
			break
		default:
			if (event.content) {
				message.answerText = `${message.answerText || ''}${event.content}`
			}
			break
	}
	updateMessageContent(message)
	message.loading =
		!message.streamCompleted &&
		!event.done &&
		!hasRenderableContent(message) &&
		event.response_type !== 'complete'
	message.typing = false
	message.isFog = false
}

/**
 * 解析历史记录条目为消息对象数组
 * @param item - 历史记录条目
 * @returns 消息对象数组
 */
const parseHistoryRecord = (item: Record<string, any>): BubbleMessage[] => {
	const updateTime = item.updateTime || item.creationTime
	const messagesForRow: BubbleMessage[] = []
	const attachmentFiles = (item?.message_files || '').split(',')

	if (item.query) {
		const userMessage = createUserMessage(item.query, updateTime)
		if (attachmentFiles.length) {
			userMessage.message_files = attachmentFiles
		}
		messagesForRow.push(userMessage)
	}

	const hasStructuredData =
		item.response_type ||
		item.thinking ||
		item.tool_call ||
		item.tool_result ||
		item.knowledge_references ||
		item.references ||
		item.answer ||
		item.content

	if (!hasStructuredData) {
		return messagesForRow
	}

	const aiMessage = createAiMessage(updateTime)
	aiMessage.id = item.id || item.msgId || aiMessage.id
	aiMessage.key = aiMessage.id
	aiMessage.msgId = item.msgId || item.id || ''
	aiMessage.assistantMessageId = item.assistant_message_id || item.assistantMessageId || ''
	aiMessage.answerText = item.answer || item.content || item.output
	aiMessage.thinking = item.thinking || item.thought
	aiMessage.toolCalls = Array.isArray(item.tool_calls)
		? item.tool_calls
		: Array.isArray(item.toolCalls)
			? item.toolCalls
			: []
	aiMessage.toolResults = Array.isArray(item.tool_results)
		? item.tool_results
		: Array.isArray(item.toolResults)
			? item.toolResults
			: []
	aiMessage.references = Array.isArray(item.knowledge_references)
		? normalizeReferences({ data: { knowledge_references: item.knowledge_references } })
		: Array.isArray(item.references)
			? normalizeReferences({ data: { references: item.references } })
			: []

	const answerParts = parseAnswerThinkState(aiMessage.answerText)
	if (!aiMessage.thinking && answerParts.thinking) {
		aiMessage.thinking = answerParts.thinking
	}
	aiMessage.answerText = answerParts.answer
	aiMessage.thinkingOpen = false
	updateMessageContent(aiMessage)
	aiMessage.loading = false
	aiMessage.isFog = false
	aiMessage.streamCompleted = true
	messagesForRow.push(aiMessage)

	return messagesForRow
}

/**
 * 加载会话列表
 * @param isRefresh - 是否为刷新操作，如果是则重置分页和列表
 */
const loadConversations = async (isRefresh = false) => {
	if (isRefresh) {
		currentPage.value = 1
		conversations.value = []
		hasMore.value = true
	}

	try {
		const res = await getSessionList(currentPage.value, pageSize.value)
		if (res.isSuccess && res.result?.model) {
			const newList = res.result.model.map((item) => {
				const config = sessionConfigMap[item.id]
				return {
					id: item.id,
					sessionId: item.sessionId,
					title: item.name,
					updatedAt: new Date(item.updateTime).getTime(),
					targetType: config?.type,
					targetConfig: config ? cloneConfig(config) : undefined
				}
			})

			conversations.value = isRefresh ? newList : [...conversations.value, ...newList]

			if (newList.length < pageSize.value) {
				hasMore.value = false
			} else {
				currentPage.value += 1
			}
		} else {
			hasMore.value = false
		}
	} catch (error) {
		console.error('Failed to load conversations', error)
		ElMessage.error(t('common.error.network'))
	}
}

/**
 * 加载更多会话
 */
const loadMoreConversations = async () => {
	if (isLoadingMore.value || !hasMore.value) return
	isLoadingMore.value = true
	try {
		await loadConversations(false)
	} finally {
		isLoadingMore.value = false
	}
}

/**
 * 加载指定会话的消息历史
 * @param conversationId - 会话 ID
 */
const loadConversationMessages = async (conversationId: string) => {
	if (!conversationId) {
		messages.value = []
		return
	}

	try {
		const res = await getSessionMessages(conversationId)
		if (res.isSuccess && res.result?.model) {
			messages.value = res.result.model.flatMap((item) =>
				parseHistoryRecord(item as Record<string, any>)
			)
		} else {
			messages.value = []
		}
	} catch (error) {
		console.error('Failed to load messages', error)
		messages.value = []
	}
}

/**
 * 处理选择会话事件
 * @param bizId - 会话业务 ID
 */
const handleSelectConversation = async (bizId: string) => {
	activeConversationId.value = bizId
	messages.value = []

	const targetConv = conversations.value.find((c) => c.id === bizId)
	if (!targetConv) {
		console.warn('Selected conversation not found in local list')
		return
	}

	if (presetConfig.value) {
		sessionConfigMap[targetConv.id] = cloneConfig(presetConfig.value)
	}
	await loadConversationMessages(targetConv.id)
	await scrollToBottom()
}

const ensureActiveConversation = async () => {
	if (activeConversationId.value) {
		sessionConfigMap[activeConversationId.value] = cloneConfig(currentChatConfig.value)
		return true
	}

	try {
		const config = cloneConfig(currentChatConfig.value)
		const res = await createSession()
		if (!res.isSuccess || !res.result) {
			ElMessage.error(t('common.error.network'))
			return false
		}
		await loadConversations(true)
		const targetConv = conversations.value.find((c) => c.id === res.result)
		if (targetConv) {
			sessionConfigMap[targetConv.id] = config
			activeConversationId.value = targetConv.id
			messages.value = []
			return true
		}
		ElMessage.error(t('common.error.network'))
		return false
	} catch (error) {
		console.error(error)
		ElMessage.error(t('common.error.network'))
		return false
	}
}

/**
 * 处理新建会话事件
 */
const handleNewChat = async () => {
	try {
		// const defaultName = `${t('pages.chat.newConversation')} ${new Date().toLocaleTimeString()}`
		const res = await createSession()
		if (res.isSuccess && res.result) {
			await loadConversations(true)
			const targetConv = conversations.value.find((c) => c.id === res.result)
			if (targetConv) {
				sessionConfigMap[targetConv.id] = presetConfig.value
					? cloneConfig(presetConfig.value)
					: cloneConfig(currentChatConfig.value)
				await handleSelectConversation(targetConv.id)
				ElMessage.success(t('pages.chat.createSuccess'))
			} else if (conversations.value.length > 0) {
				await handleSelectConversation(conversations.value?.[0]?.id!)
			}
		}
	} catch (error) {
		console.error(error)
		ElMessage.error(t('common.error.network'))
	}
}

/**
 * 处理会话命令（重命名、删除等）
 * @param command - 命令名称
 * @param bizId - 会话业务 ID
 */
const handleConvCommand = (command: string | number, bizId: string) => {
	const cmd = String(command)
	if (cmd === 'rename') {
		renamingConvId.value = bizId
		const conv = conversations.value.find((c) => c.id === bizId)
		renameInput.value = conv?.title || ''
		renameDialogVisible.value = true
		return
	}

	if (cmd === 'delete') {
		ElMessageBox.confirm(t('pages.chat.deleteConfirm'), t('common.dialog.tip'), {
			confirmButtonText: t('common.confirm'),
			cancelButtonText: t('common.cancel'),
			type: 'warning'
		})
			.then(async () => {
				try {
					await deleteSession(bizId)
					ElMessage.success(t('pages.chat.deleteSuccess'))
					if (activeConversationId.value === bizId) {
						activeConversationId.value = ''
						messages.value = []
					}
					await loadConversations(true)
					if (conversations.value.length > 0 && !activeConversationId.value) {
						activeConversationId.value = conversations.value?.[0]?.id!
						await loadConversationMessages(activeConversationId.value)
						await scrollToBottom()
					}
				} catch (error) {
					ElMessage.error(t('common.error.network'))
				}
			})
			.catch(() => {})
	}
}

/**
 * 根据选中的知识库 ID 获取知识选项
 * @param baseIds - 知识库 ID 数组
 */
const fetchKnowledgeOptions = async (baseIds: string[]) => {
	if (!baseIds.length) {
		knowledgeOptions.value = []
		return
	}

	const results = await Promise.all(
		baseIds.map((knowledgeBaseId) =>
			knowledge.postAiKnowledgeSelectList({
				knowledge_base_id: knowledgeBaseId,
				title: ''
			})
		)
	)

	const optionMap: Record<string, { label: string; value: string }> = {}
	results.forEach((res: any) => {
		if (!res?.isSuccess) return
		;(res.result || []).forEach((item: any) => {
			if (!item.id) return
			optionMap[item.id] = {
				label: item.title!,
				value: item.id
			}
		})
	})

	knowledgeOptions.value = Object.values(optionMap)
}

/**
 * 处理重命名会话
 */
const handleRename = async () => {
	if (!renameInput.value.trim()) {
		ElMessage.warning(t('pages.chat.renameEmpty'))
		return
	}

	try {
		await updateSessionName(renamingConvId.value, renameInput.value)
		renameDialogVisible.value = false
		ElMessage.success(t('pages.chat.renameSuccess'))
		const conv = conversations.value.find((c) => c.id === renamingConvId.value)
		if (conv) conv.title = renameInput.value
	} catch (error) {
		ElMessage.error(t('common.error.network'))
	}
}

/**
 * 处理发送消息
 * @param content - 可选的消息内容，若不提供则使用 senderValue
 */
const handleSend = async (content?: string) => {
	if (!content || !content.trim()) {
		ElMessage.warning(t('pages.chat.inputRequired'))
		return
	}

	if (activeTargetType.value === 'agent' && !settingsDraft.agentId) {
		ElMessage.warning(t('pages.chat.selectAgentFirst'))
		return
	}
	if (activeTargetType.value === 'flow' && !settingsDraft.agentId) {
		ElMessage.warning(t('pages.chat.selectWorkflowFirst'))
		return
	}

	const workflowParams = buildWorkflowChatParams()
	if (workflowParams === false) return

	const hasConversation = await ensureActiveConversation()
	if (!hasConversation) return

	senderValue.value = ''

	const userMsg = createUserMessageWithAttachments(content)
	messages.value.push(userMsg)
	await scrollToBottom()

	const aiMsg = createAiMessage()
	messages.value.push(aiMsg)
	const aiMessageKey = aiMsg.key as string
	const streamToken = `${aiMessageKey}-${Date.now()}`
	activeStreamToken.value = streamToken
	await scrollToBottom()

	const getAiMessage = () => messages.value.find((item) => item.key === aiMessageKey)
	const isActiveStream = () => activeStreamToken.value === streamToken

	const requestBody = buildChatRequestBody(
		activeTargetType.value,
		currentChatConfig.value,
		activeConversationId.value,
		content,
		workflowParams
	)

	const url = getChatUrl(activeTargetType.value)
	currentAttachments.value = []

	streamChat(
		url,
		requestBody,
		// onChunk
		(event: ChatSseMessage) => {
			if (!isActiveStream()) return
			const msg = getAiMessage()
			if (!msg) return
			applyWorkflowRunnerTraceEvent(msg, event)
			applyStructuredEventToMessage(msg, normalizeWorkflowRunnerEvent(event))
			scrollToBottomIfNearBottom()
		},
		// onComplete
		() => {
			if (!isActiveStream()) return
			const msg = getAiMessage()
			if (msg) {
				msg.loading = false
				msg.isFog = false
				msg.typing = false
				msg.streamCompleted = true
				updateMessageContent(msg)
			}
		},
		// onError
		(err) => {
			if (!isActiveStream()) return
			console.error('Stream chat error:', err)
			const msg = getAiMessage()
			if (msg) {
				msg.loading = false
				msg.isFog = false
				msg.typing = false
				msg.streamCompleted = true
				msg.inlineErrors = [
					...(msg.inlineErrors || []),
					err?.message || err?.name || t('common.error.network')
				]
				updateMessageContent(msg)
			}
		}
	)
}

/**
 * 发送消息的包装方法
 * @param content - 消息内容
 */
const sendMessage = async (content: string) => {
	await handleSend(content)
}

/**
 * 处理重试消息
 * @param message - 要重试的消息对象
 */
const handleRetry = async (message: BubbleMessage) => {
	if (isLoading.value) {
		ElMessage.warning(t('pages.chat.requestInProgress'))
		return
	}

	const messageIndex = messages.value.findIndex((item) => item.id === message.id)
	if (messageIndex <= 0) return

	const userMessage = [...messages.value.slice(0, messageIndex)]
		.reverse()
		.find((item) => item.role === 'user')
	const query = userMessage?.rawText || userMessage?.content
	if (!query) {
		ElMessage.warning(t('pages.chat.retrySourceNotFound'))
		return
	}

	messages.value = messages.value.slice(0, messageIndex)
	senderValue.value = query
	await sendMessage(query)
}

/**
 * 处理取消请求
 */
const handleCancel = async () => {
	cancelRequest()
	activeStreamToken.value = ''

	const msg = [...messages.value]
		.reverse()
		.find((item) => item.role === 'ai' && !item.streamCompleted)
	if (!msg) return

	msg.loading = false
	msg.isFog = false
	msg.typing = false
	msg.streamCompleted = true
	msg.stopped = true
	updateMessageContent(msg)
	if (msg.assistantMessageId) {
		try {
			await aiChat.postChatStopAnswer({
				session_id: activeConversationId.value,
				msgId: msg.assistantMessageId
			})
		} catch (error) {
			console.error('stop answer error', error)
		}
	}
}
</script>

<style lang="less" scoped>
.chat-container {
	display: flex;
	height: 100vh;
	background: var(--bg-page);

	.chat-main {
		flex: 1;
		display: flex;
		flex-direction: column;
		min-height: 0;
		overflow: hidden;
		background: var(--bg-page);
	}
}

.title {
	font-size: 12px;
	color: var(--text-secondary);
	flex-shrink: 0;
}

:deep(.el-bubble-end .el-bubble-content) {
	background-color: var(--el-color-primary-light-9);
	border-radius: 8px 0px 8px 8px;
	color: var(--text-primary);
	border: none;
}

:deep(.el-bubble-start .el-bubble-content) {
	background-color: var(--bg-container);
	border: none;
	color: var(--text-primary);
}

.chat-target-select {
	width: 132px;
}

.settings-switches {
	display: flex;
	align-items: center;
	flex-wrap: wrap;
	gap: 18px;
}

.settings-note {
	margin-top: 10px;
}

.workflow-form {
	width: 100%;
	max-width: 100%;
	max-height: min(360px, 42vh);
	margin: 0;
	padding: 16px;
	overflow-y: auto;
	overflow-x: hidden;
	border: 1px solid var(--border-light);
	border-radius: 8px;
	background: var(--bg-base);
	box-sizing: border-box;
}

.workflow-form__title {
	margin-bottom: 12px;
	font-size: 15px;
	font-weight: 600;
	color: var(--text-primary);
}

:global(.workflow-form-popper) {
	max-width: calc(100vw - 24px);
	border-radius: 20px !important;
}

:global(.workflow-form-popper .el-popover__inner) {
	padding: 0;
}

.workflow-form :deep(.input-form),
.workflow-form :deep(.el-form),
.workflow-form :deep(.el-form-item),
.workflow-form :deep(.el-form-item__content),
.workflow-form :deep(.el-input),
.workflow-form :deep(.el-select),
.workflow-form :deep(.el-textarea) {
	min-width: 0;
	max-width: 100%;
}

/* Prompts 组件暗黑模式适配 */
:deep(.el-prompts-title) {
	color: var(--text-tertiary) !important;
}

:deep(.el-prompts-item) {
	background: var(--bg-container) !important;
	border-color: var(--border-light) !important;
}

:deep(.el-prompts-item.hovered) {
	background: var(--bg-overlay) !important;
}

:deep(.el-prompts-item.actived) {
	background: var(--bg-page) !important;
}

:deep(.el-prompts-item-label) {
	color: var(--text-primary) !important;
}

:deep(.el-prompts-item-description) {
	color: var(--text-tertiary) !important;
}

:global(.target-popper .el-cascader-node) {
	gap: 8px;
}

/* 隐藏级联选择器默认的标签 chip，保留自定义 #tag slot 内容 */
.chat-target-cascader :deep(.el-cascader__tags > .el-tag) {
	display: none;
}

.chat-target-tag {
	display: inline-flex;
	align-items: center;
	white-space: nowrap;
}

.chat-target-tag__name {
	font-size: 13px;
	color: var(--text-primary);
	max-width: 120px;
	overflow: hidden;
	text-overflow: ellipsis;
}
</style>
