<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"
			>
				<div
					v-if="settingsDraft.type === 'agent' && agentPromptsItems?.length"
					style="margin-top: 12px; display: flex; flex-direction: column; gap: 12px"
				>
					<Prompts
						title="快速开始："
						:items="agentPromptsItems"
						@item-click="handlePromptItemClick"
						vertical
					/>
				</div>
			</MessageList>

			<ChatInput
				v-model="senderValue"
				ref="chatInputRef"
				:loading="isLoading"
				:attachments="currentAttachments"
				@submit="handleSend"
				@cancel="handleCancel"
			>
				<template #header>
					<div class="px-8px py-4px flex items-center gap-4px">
						<div v-if="settingsDraft.type === 'agent'" class="flex items-center gap-4px">
							<div class="title">智能体：</div>
							<el-select
								class="w-180px"
								placeholder="选择智能体"
								v-model="settingsDraft.agentId"
								:options="agentOptions"
								@change="handleAgentSelectChange"
							/>
						</div>
						<div v-if="settingsDraft.type === 'knowledge'" class="flex items-center gap-4px">
							<div class="title">知识库：</div>
							<el-select
								class="w-180px"
								placeholder="选择知识库"
								v-model="settingsDraft.knowledgeBaseIds"
								:options="knowledgeBaseOptions"
								multiple
							/>
						</div>
						<div v-if="settingsDraft.type === 'knowledge'" class="flex items-center gap-4px">
							<div class="title">知识：</div>
							<el-select
								class="w-180px"
								placeholder="选择智能体"
								v-model="settingsDraft.knowledgeIds"
								:options="knowledgeBaseOptions"
								multiple
							/>
						</div>
					</div>
				</template>
				<template #prefix-extra>
					<el-select
						v-model="activeTargetType"
						class="chat-target-select"
						@change="handleTargetTypeChange"
					>
						<el-option :label="t('pages.chat.targetKnowledge')" value="knowledge" />
						<el-option :label="t('pages.chat.targetAgent')" value="agent" />
						<el-option :label="t('pages.chat.targetModel')" value="model" />
					</el-select>
					<!-- 附件 -->
					<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="选择模型"
						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>
</template>

<script setup lang="ts">
import { computed, nextTick, onMounted, reactive, ref, watch } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { PictureFilled, Setting } 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 { agentApplication } 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 modelOptions = ref<ChatOptionItem[]>([])
const settingsDraft = reactive<ChatTargetConfig>(createDefaultConfig('agent'))
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 activeConversationTitle = computed(() => {
	const conv = conversations.value.find((c) => c.id === activeConversationId.value)
	return conv ? conv.title : t('pages.chat.newConversation')
})

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)
}))

function 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!)
	}
})

function createDefaultConfig(type: ChatTargetType = 'agent'): ChatTargetConfig {
	return {
		type,
		knowledgeBaseIds: [],
		knowledgeIds: [],
		summaryModelId: '',
		disableTitle: false,
		enableMemory: true,
		agentId: type === 'agent' ? getDefaultAgentId() : '',
		agentEnabled: true,
		webSearchEnabled: false,
		images: []
	}
}

function 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]
	}
}

function getConversationConfig(conversationId: string) {
	const cachedConfig = sessionConfigMap[conversationId]
	if (cachedConfig) return cachedConfig

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

	return createDefaultConfig(activeTargetType.value)
}

function syncSettingsDraft(config: ChatTargetConfig) {
	const next = createDefaultConfig(config.type)
	Object.assign(next, config)
	activeTargetType.value = next.type
	Object.assign(settingsDraft, next)
}

function persistConversationConfig(conversationId = activeConversationId.value) {
	if (!conversationId) return
	sessionConfigMap[conversationId] = cloneConfig(currentChatConfig.value)
	const conv = conversations.value.find((item) => item.id === conversationId)
	if (conv) {
		conv.targetType = activeTargetType.value
		conv.targetConfig = cloneConfig(currentChatConfig.value)
	}
}

async function loadChatOptions() {
	try {
		const [agents, knowledgeBases, models] = await Promise.all([
			getAgentOptions(),
			getKnowledgeBaseOptions(),
			getModelOptions()
		])
		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)
	}
}

async function refreshAgentUploadCapability(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)
	}
}

/**
 * 选择对话类型
 * @param value
 */
async function handleTargetTypeChange(value: ChatTargetType) {
	settingsDraft.type = value
	activeTargetType.value = value

	if (value !== 'agent') {
		allowImageUpload.value = false
		currentAttachments.value = []
	} else {
		await refreshAgentUploadCapability(settingsDraft.agentId)
	}
	persistConversationConfig()
}

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
	}
)

async function handleAgentSelectChange(value: string) {
	settingsDraft.agentId = value
	await refreshAgentUploadCapability(value)
}

async function getSuggestQuestion() {
	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'
				}
			}
		})
	}
}

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

function handlePromptItemClick(item: PromptsItemsProps) {
	handleSend(item.label)
}

function normalizeText(value?: string) {
	return `${value || ''}`.trim()
}

function appendThinking(message: BubbleMessage, text?: string) {
	const thought = normalizeText(text)
	if (!thought) return
	message.eventThinkingText = `${message.eventThinkingText || ''}${message.eventThinkingText ? '\n\n' : ''}${thought}`
	updateMessageThinking(message)
}

function 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 = normalizeText(inner)
			if (piece) thinking.push(piece)
			return ''
		})
	}

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

function updateMessageThinking(message: BubbleMessage, answerThinking = '') {
	message.answerThinkingText = answerThinking
	message.thinking = [message.eventThinkingText, message.answerThinkingText]
		.map((item) => normalizeText(item))
		.filter(Boolean)
		.join('\n\n')
}

function 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
	}
}

function hasRenderableContent(message: BubbleMessage) {
	return !!(
		normalizeText(message.content) ||
		normalizeText(message.answerText) ||
		normalizeText(message.thinking) ||
		(message.toolCalls || []).length ||
		(message.toolResults || []).length ||
		(message.references || []).length ||
		normalizeText(message.error)
	)
}

function 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
	}
}

function getAttachmentFileIds() {
	return currentAttachments.value
		.map((item) => item.path || item.id || item.name)
		.filter(Boolean) as string[]
}

function createUserMessageWithAttachments(content: string, updateTime?: string): BubbleMessage {
	const message = createUserMessage(content, updateTime)
	const fileIds = getAttachmentFileIds()
	if (fileIds.length) {
		message.message_files = fileIds
	}
	return message
}

function createAiMessage(updateTime?: string): BubbleMessage {
	const id = `ai-${Date.now()}-${Math.random()}`
	return {
		id,
		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
	}
}

function 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
	}
}

function 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
	}
}

function 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
	}))
}

function applyStructuredEventToMessage(message: BubbleMessage, event: ChatSseMessage) {
	const data = event.data || {}
	switch (event.response_type) {
		case 'agent_query':
			message.assistantMessageId =
				data.assistant_message_id || data.assistantMessageId || message.assistantMessageId
			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 = normalizeText(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':
			message.error = normalizeText(event.content || data.error || data.message)
			break
		case 'session_title': {
			const title = normalizeText(data.title || event.content)
			if (title) {
				const conv = conversations.value.find((item) => item.id === activeConversationId.value)
				if (conv) {
					conv.title = title
				}
			}
			break
		}
		case 'complete':
			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
	if (event.done) {
		message.streamCompleted = true
	}
}

function 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.assistantMessageId =
		item.assistant_message_id || item.assistantMessageId || item.msgId || ''
	aiMessage.answerText = normalizeText(item.answer || item.content || item.output)
	aiMessage.thinking = normalizeText(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
}

async function loadConversations(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'))
	}
}

async function loadMoreConversations() {
	if (isLoadingMore.value || !hasMore.value) return
	isLoadingMore.value = true
	try {
		await loadConversations(false)
	} finally {
		isLoadingMore.value = false
	}
}

async function loadConversationMessages(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>)
			)
			await nextTick()
			messageListRef.value?.scrollToBottom?.()
		} else {
			messages.value = []
		}
	} catch (error) {
		console.error('Failed to load messages', error)
		messages.value = []
	}
}

async function handleSelectConversation(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)
}

async function handleNewChat() {
	try {
		const defaultName = `${t('pages.chat.newConversation')} ${new Date().toLocaleTimeString()}`
		const res = await createSession(defaultName)
		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'))
	}
}

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(() => {})
	}
}

async function handleRename() {
	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'))
	}
}

async function handleSend(content?: string) {
	if (!content || !content.trim() || !activeConversationId.value) {
		ElMessage.warning(t('pages.chat.selectConversationFirst'))
		return
	}

	if (activeTargetType.value === 'agent' && !settingsDraft.agentId) {
		ElMessage.warning(t('pages.chat.selectAgentFirst'))
		return
	}
	if (activeTargetType.value === 'knowledge' && !settingsDraft.knowledgeBaseIds.length) {
		ElMessage.warning(t('pages.chat.selectKnowledgeBaseFirst'))
		return
	}
	if (activeTargetType.value === 'model' && !settingsDraft.summaryModelId) {
		ElMessage.warning(t('pages.chat.selectSummaryModelFirst'))
		return
	}

	senderValue.value = ''

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

	const aiMsg = createAiMessage()
	messages.value.push(aiMsg)
	await nextTick()
	messageListRef.value?.scrollToBottom?.()
	const aiMessageId = aiMsg.id as string
	const streamToken = `${aiMessageId}-${Date.now()}`
	activeStreamToken.value = streamToken

	const getAiMessage = () => messages.value.find((item) => item.id === aiMessageId)
	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,
		(event: ChatSseMessage) => {
			if (!isActiveStream()) return
			const msg = getAiMessage()
			if (!msg) return
			applyStructuredEventToMessage(msg, event)
			nextTick(() => {
				messageListRef.value?.scrollToBottom?.()
			})
		},
		() => {
			if (!isActiveStream()) return
			const msg = getAiMessage()
			if (msg) {
				msg.loading = false
				msg.isFog = false
				msg.typing = false
				msg.streamCompleted = true
				updateMessageContent(msg)
			}
			nextTick(() => {
				messageListRef.value?.scrollToBottom?.()
			})
		},
		(err) => {
			if (!isActiveStream()) return
			console.error('Stream chat error:', err)
			const msg = getAiMessage()
			let errorText = t('common.error.network')
			if (msg) {
				msg.loading = false
				msg.isFog = false
				msg.typing = false
				msg.streamCompleted = true
				msg.error = normalizeText(err?.message || err?.name || t('common.error.network'))
				errorText = msg.error || errorText
				updateMessageContent(msg)
			}
			ElMessage.error(errorText)
			nextTick(() => {
				messageListRef.value?.scrollToBottom?.()
			})
		}
	)
}

async function sendMessage(content: string) {
	await handleSend(content)
}

async function handleRetry(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 = normalizeText(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)
}

function handleCancel() {
	cancelRequest()
}
</script>

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

	.chat-main {
		flex: 1;
		display: flex;
		flex-direction: column;
		background: var(--bg-page);
	}
}

.title {
	font-size: 12px;
	color: var(--text-primary);
	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;
}
</style>
