<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> </template>
			</ChatHeader>

			<MessageList
				ref="messageListRef"
				:messages="messages"
				:loading="isLoading"
				@retry="handleRetry"
				@add-to-kb="handleAddKb"
				@card-submit="handleSend"
			>
				<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>
					<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-select
						class="w-180px"
						:placeholder="t('pages.chat.selectAgentPlaceholder')"
						v-model="settingsDraft.agentId"
						:options="agentOptions"
						@change="handleAgentSelectChange"
					/>
					<!-- 附件 -->
					<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
						: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>
	</div>

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

<script setup lang="ts">
import { computed, nextTick, onMounted, reactive, ref, watch } from 'vue'
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 { useChatStream } from './composables/useChatStream'
import { Prompts } from 'vue-element-plus-x'
import {
	buildChatRequestBody,
	createSession,
	deleteSession,
	getAgentInfo,
	getAgentOptions,
	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 { 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'

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

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 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>>()
let scrollToBottomPending = false

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 且允许上传图片时显示
 */
const showImageUploadButton = computed(
	() => activeTargetType.value === 'agent' && allowImageUpload.value
)

/**
 * 计算当前聊天配置对象，用于发送消息时使用
 */
const currentChatConfig = computed<ChatTargetConfig>(() => ({
	type: activeTargetType.value,
	knowledgeBaseIds: [...settingsDraft.knowledgeBaseIds],
	knowledgeIds: [...settingsDraft.knowledgeIds],
	summaryModelId: 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 || ''
}

onMounted(async () => {
	await loadChatOptions()
	await loadConversations()
	if (conversations.value.length > 0 && !activeConversationId.value) {
		await handleSelectConversation(conversations.value?.[0]?.id!)
	}
})

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

const settingsDraft = reactive<ChatTargetConfig>(createDefaultConfig('agent'))

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

/**
 * 获取指定会话的配置
 * 优先从缓存中获取，其次从会话历史中恢复，最后使用默认配置
 * @param conversationId - 会话 ID
 * @returns 聊天目标配置对象
 */
const getConversationConfig = (conversationId: string) => {
	const cachedConfig = sessionConfigMap[conversationId]
	if (cachedConfig) return cachedConfig

	const conversation = conversations.value.find((item) => item.id === conversationId)
	if (conversation?.targetConfig) {
		const config = {
			...createDefaultConfig(conversation.targetType || activeTargetType.value),
			...cloneConfig(conversation.targetConfig as ChatTargetConfig)
		}

		return config
	}

	return createDefaultConfig(activeTargetType.value)
}

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

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

		if (activeTargetType.value === 'agent' && !settingsDraft.agentId) {
			settingsDraft.agentId = getDefaultAgentId()
		}
	} 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') {
				chat?.openHeader()
			} else {
				chat?.closeHeader()
			}
		}
	},
	{
		immediate: true
	}
)

/**
 * 处理智能体选择变更事件
 * @param value - 选中的智能体 ID
 */
const handleAgentSelectChange = async (value: string) => {
	settingsDraft.agentId = 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
	)
}

/**
 * 创建用户消息对象
 * @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
	}
}

/**
 * 同步消息身份
 * @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
	}))
}

/**
 * 将结构化事件应用到消息对象上
 * 根据事件类型更新消息的不同字段（如回答、思考、工具调用等）
 * @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':
			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)
			message.inlineErrors = [
				...(message.inlineErrors || []),
				event.content || data.error || data.message || '未知错误'
			]

			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 = !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
	}

	const config = getConversationConfig(targetConv.id)
	syncSettingsDraft(config)
	await refreshAgentUploadCapability(config.agentId)
	await loadConversationMessages(targetConv.id)
	await scrollToBottom()
}

/**
 * 处理新建会话事件
 */
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] = createDefaultConfig(activeTargetType.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) {
						await handleSelectConversation(conversations.value?.[0]?.id!)
					}
				} 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() || !activeConversationId.value) {
		ElMessage.warning('请输入对话内容')
		return
	}

	if (activeTargetType.value === 'agent' && !settingsDraft.agentId) {
		ElMessage.warning(t('pages.chat.selectAgentFirst'))
		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
	)

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

	streamChat(
		url,
		requestBody,
		// onChunk
		(event: ChatSseMessage) => {
			if (!isActiveStream()) return
			const msg = getAiMessage()
			if (!msg) return
			applyStructuredEventToMessage(msg, 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;
}

/* 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;
}
</style>
