<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" />

			<ChatInput v-model="senderValue" :loading="isLoading" :attachments="currentAttachments" @submit="handleSend"
				@cancel="handleCancel">
				<template #prefix-extra>
					<!-- 切换对话模式 -->
					<el-select v-model="activeTargetType" class="chat-target-select" size="small"
						@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-button :icon="Setting" type="text" plain @click="openSettingsDialog" />
					<!-- 选择图片 -->
					<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>
			</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="settingsDialogVisible" :title="t('pages.chat.settingsTitle')" width="720px">
			<el-form label-position="top" :model="settingsDraft">
				<el-form-item :label="t('pages.chat.settingsKnowledgeBase')">
					<el-select v-model="settingsDraft.knowledgeBaseIds" multiple filterable allow-create default-first-option
						:placeholder="t('pages.chat.selectPlaceholder')" style="width: 100%">
						<el-option v-for="item in knowledgeBaseOptions" :key="item.value" :label="item.label" :value="item.value" />
					</el-select>
				</el-form-item>
				<el-form-item :label="t('pages.chat.settingsKnowledge')">
					<el-select v-model="settingsDraft.knowledgeIds" multiple filterable allow-create default-first-option
						:placeholder="t('pages.chat.selectPlaceholder')" style="width: 100%" />
				</el-form-item>
				<el-form-item v-if="activeTargetType === 'agent'" :label="t('pages.chat.settingsAgent')">
					<el-select v-model="settingsDraft.agentId" filterable :placeholder="t('pages.chat.selectAgentPlaceholder')"
						style="width: 100%" @change="handleAgentSelectChange">
						<el-option v-for="item in agentOptions" :key="item.value" :label="item.label" :value="item.value" />
					</el-select>
				</el-form-item>
				<el-form-item :label="t('pages.chat.settingsSummaryModel')">
					<el-select v-model="settingsDraft.summaryModelId" filterable
						:placeholder="t('pages.chat.selectSummaryModelPlaceholder')" style="width: 100%">
						<el-option v-for="item in modelOptions" :key="item.value" :label="item.label" :value="item.value" />
					</el-select>
				</el-form-item>
				<el-form-item :label="t('pages.chat.settingsSessionParams')">
					<div class="settings-switches">
						<el-switch v-model="settingsDraft.enableMemory" :active-text="t('pages.chat.enableMemory')" />
						<el-switch v-model="settingsDraft.disableTitle" :active-text="t('pages.chat.disableTitle')" />
					</div>
				</el-form-item>
				<el-form-item v-if="activeTargetType === 'agent'" :label="t('pages.chat.settingsAgentSwitches')">
					<div class="settings-switches">
						<el-switch v-model="settingsDraft.agentEnabled" :active-text="t('pages.chat.enableAgentMode')" />
						<el-switch v-model="settingsDraft.webSearchEnabled" :active-text="t('pages.chat.enableWebSearch')" />
					</div>
				</el-form-item>
				<div v-if="activeTargetType === 'agent'" class="settings-note">
					<el-tag :type="allowImageUpload ? 'success' : 'info'" effect="light">
						{{ allowImageUpload ? t('pages.chat.agentImageUploadEnabled') : t('pages.chat.agentImageUploadDisabled') }}
					</el-tag>
				</div>
			</el-form>
			<template #footer>
				<el-button @click="settingsDialogVisible = false">{{ t('pages.chat.cancel') }}</el-button>
				<el-button type="primary" :loading="settingsSaving" @click="handleSaveSettings">{{ t('pages.chat.save')
				}}</el-button>
			</template>
		</el-dialog>

		<el-dialog v-model="imageUploadDialogVisible" :title="t('pages.chat.imageUploadTitle')" width="560px">
			<FileUploadInput v-model="currentAttachments" :multiple="true" :file-types="['image']" :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 } 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 {
	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 type {
	BubbleMessage,
	ChatReference,
	ChatSseMessage,
	ChatTargetConfig,
	ChatTargetType,
	ChatToolCall,
	ChatToolResult,
	Conversation
} from './types'
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 settingsDialogVisible = ref(false)
const settingsSaving = ref(false)
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 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)
	}
}

function openSettingsDialog() {
	syncSettingsDraft(getConversationConfig(activeConversationId.value))
	settingsDialogVisible.value = true
}

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

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

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

async function handleSaveSettings() {
	settingsSaving.value = true
	try {
		settingsDraft.type = activeTargetType.value
		if (activeTargetType.value === 'agent') {
			await refreshAgentUploadCapability(settingsDraft.agentId)
		}
		persistConversationConfig()
		settingsDialogVisible.value = false
	} finally {
		settingsSaving.value = false
	}
}

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

function extractThinkParts(text?: string) {
	const raw = `${text || ''}`
	if (!raw) return { thinking: '', answer: '' }
	const thinkRegex = /<think>([\s\S]*?)<\/think>/gi
	const thinking: string[] = []
	let answer = raw
	answer = answer.replace(thinkRegex, (_match, inner: string) => {
		const piece = normalizeText(inner)
		if (piece) thinking.push(piece)
		return ''
	})
	return {
		thinking: thinking.join('\n\n'),
		answer: normalizeText(answer)
	}
}

function updateMessageContent(message: BubbleMessage) {
	message.content = normalizeText(message.answerText || message.output)
}

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: '',
		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)
			if (thought) {
				message.thinking = `${message.thinking || ''}${message.thinking ? '\n\n' : ''}${thought}`
			}
			break
		}
		case 'tool_call':
			message.toolCalls = [...(message.toolCalls || []), normalizeToolCall(event)]
			break
		case 'tool_result': {
			message.toolResults = [...(message.toolResults || []), normalizeToolResult(event)]
			const thought = normalizeText(data.thought)
			if (thought) {
				message.thinking = `${message.thinking || ''}${message.thinking ? '\n\n' : ''}${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 = extractThinkParts(aiMessage.answerText)
	if (!aiMessage.thinking && answerParts.thinking) {
		aiMessage.thinking = answerParts.thinking
	}
	aiMessage.answerText = answerParts.answer
	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);
	}
}

: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>
