<template>
	<div class="chat-content">
		<div v-if="messages.length > 0" ref="scrollContainerRef" class="item-list">
			<Bubble
				v-for="item in messages"
				:key="item.id || item.key"
				:content="item.content"
				:placement="item.placement"
				:loading="item.loading"
				:shape="item.shape"
				:variant="item.variant"
				:is-markdown="item.isMarkdown"
				:is-fog="item.isFog"
				:typing="item.typing"
				class="message-bubble"
				maxWidth="800px"
			>
				<template #avatar>
					<div class="avatar-wrapper">
						<Icon v-if="item.role === 'ai'" icon="mingcute:ai-line" :width="22" />
						<Icon v-if="item.role === 'user'" icon="ri:user-line" :width="22" />
					</div>
				</template>

				<template #header>
					<div class="header-wrapper">
						<div class="header-name"></div>
					</div>
				</template>

				<template #content>
					<div class="msg-content-wrapper">
						<div v-if="item.role === 'ai' && hasStructuredBlocks(item)" class="structured-blocks">
							<Thinking
								v-if="shouldShowThinking(item)"
								:content="getThinkingText(item)"
								:status="getThinkingStatus(item)"
								:model-value="isThinkingOpen(item)"
								auto-collapse
								max-width="100%"
								background-color="var(--bg-container)"
								color="var(--text-primary)"
							/>

							<div v-if="getToolCalls(item).length" class="structured-block">
								<div class="structured-block__head">
									<span class="structured-block__title">{{ t('pages.chat.toolCallTitle') }}</span>
									<el-tag size="small" type="warning" effect="plain">{{
										getToolCalls(item).length
									}}</el-tag>
								</div>
								<div class="tool-list">
									<div
										v-for="(toolCall, index) in getToolCalls(item)"
										:key="`${item.id || item.key}-tool-call-${index}`"
										class="tool-card"
									>
										<div class="tool-card__head">
											<div class="tool-card__meta">
												<el-tag size="small" type="warning" effect="dark">
													{{ getToolName(toolCall.toolName) }}
												</el-tag>
												<span v-if="toolCall.toolCallId" class="tool-card__id">{{
													toolCall.toolCallId
												}}</span>
											</div>
										</div>
										<pre class="tool-card__content">{{
											formatStructuredValue(toolCall.arguments || toolCall.content)
										}}</pre>
									</div>
								</div>
							</div>

							<div v-if="getToolResults(item).length" class="structured-block">
								<div class="structured-block__head">
									<span class="structured-block__title">{{ t('pages.chat.toolResultTitle') }}</span>
									<el-tag size="small" type="success" effect="plain">{{
										getToolResults(item).length
									}}</el-tag>
								</div>
								<div class="tool-list">
									<div
										v-for="(toolResult, index) in getToolResults(item)"
										:key="`${item.id || item.key}-tool-result-${index}`"
										class="tool-card"
									>
										<div class="tool-card__head">
											<div class="tool-card__meta">
												<el-tag
													:type="toolResult.success === false ? 'danger' : 'success'"
													size="small"
													effect="dark"
												>
													{{ toolResult.success === false ? t('pages.chat.failedTag') : 'OK' }}
												</el-tag>
												<span class="tool-card__name">{{ getToolName(toolResult.toolName) }}</span>
												<span v-if="toolResult.toolCallId" class="tool-card__id">{{
													toolResult.toolCallId
												}}</span>
											</div>
											<div v-if="toolResult.durationMs !== undefined" class="tool-card__duration">
												{{ toolResult.durationMs }}ms
											</div>
										</div>
										<div v-if="hasToolResultThought(toolResult)" class="tool-card__section">
											<div class="tool-card__label">{{ t('pages.chat.thinkTitle') }}</div>
											<pre
												class="tool-card__content"
											><XMarkdown :markdown="getToolResultThought(toolResult)"/></pre>
										</div>
										<div v-if="hasToolResultOutput(toolResult)" class="tool-card__section">
											<div class="tool-card__label">{{ t('pages.chat.toolResultTitle') }}</div>
											<pre class="tool-card__content">
												<XMarkdown :markdown="getToolResultOutput(toolResult)"/>
											</pre>
										</div>
										<div v-if="hasToolResultItems(toolResult)" class="tool-card__section">
											<div class="tool-card__label">content_items</div>
											<pre class="tool-card__content">
												<XMarkdown :markdown="formatStructuredValue(getToolResultItems(toolResult))"/>
											</pre>
										</div>
										<div v-if="toolResult.error" class="tool-card__section">
											<div class="tool-card__label">{{ t('pages.chat.failedTag') }}</div>
											<pre class="tool-card__content tool-card__content--error">{{
												toolResult.error
											}}</pre>
										</div>
									</div>
								</div>
							</div>
						</div>

						<XMarkdown
							v-if="getDisplayContent(item)"
							class="msg-content-text"
							:markdown="getDisplayContent(item)"
							enableLatex
							enableBreaks
						/>
					</div>
				</template>

				<template #footer>
					<div v-if="getMessageImages(item).length" class="file-wrap">
						<el-image
							v-for="(src, index) in getMessageImages(item)"
							:key="`${item.id || item.key}-file-${index}`"
							:src="src"
							class="file-image"
							fit="cover"
							preview-teleported
							hide-on-click-modal
							:preview-src-list="getMessageImages(item)"
						/>
					</div>
					<div class="footer-wrapper" v-if="item.role === 'ai' && item.streamCompleted">
						<div class="footer-container">
							<el-button
								type="info"
								:icon="Refresh"
								size="small"
								circle
								@click="handleRetry(item)"
							/>
							<el-button
								color="#626aef"
								:icon="DocumentCopy"
								size="small"
								circle
								@click="handleCopy(item)"
							/>
						</div>
						<div class="footer-time">
							{{ item.updateTime }}
						</div>
					</div>
				</template>
			</Bubble>
		</div>

		<div v-else class="empty-state">
			<div class="empty-icon">
				<SvgIcon name="sparkle" size="60" />
			</div>
			<div class="empty-text">{{ t('pages.chat.emptyTitle') }}</div>
			<div class="empty-subtext">{{ t('pages.chat.emptySubtitle') }}</div>
			<slot />
		</div>
	</div>
</template>

<script setup lang="ts">
import { nextTick, ref } from 'vue'
import { Bubble, Thinking, XMarkdown } from 'vue-element-plus-x'
import { useI18n } from '@/composables/useI18n'
import { ElMessage } from 'element-plus'
import { DocumentCopy, Refresh } from '@element-plus/icons-vue'
import { Icon } from '@repo/ui'
import type { BubbleMessage, ChatToolResult } from '../../views/chat/types'

const { t } = useI18n()

defineProps<{
	messages: BubbleMessage[]
	loading: boolean
}>()

const emit = defineEmits<{
	retry: [message: BubbleMessage]
}>()

const getMessageImages = (message: BubbleMessage) => {
	const files = message.message_files
	const ids = Array.isArray(files) ? files : files ? [files] : []
	return ids.filter(Boolean).map((fileId) => `/File/GetImage?fileId=${fileId}`)
}

const THINK_TAG_RE = /<think\b[^>]*>([\s\S]*?)(?:<\/think>|$)/gi

const parseThinkContent = (content?: string) => {
	const text = `${content || ''}`
	const thinkingParts: string[] = []
	const displayContent = text
		.replace(THINK_TAG_RE, (_match, thinkingText) => {
			const trimmed = `${thinkingText || ''}`.trim()
			if (trimmed) thinkingParts.push(trimmed)
			return ''
		})
		.trim()

	return {
		displayContent,
		thinkingText: thinkingParts.join('\n\n')
	}
}

const getEmbeddedThinkingText = (message: BubbleMessage) => parseThinkContent(message.content).thinkingText

const getDisplayContent = (message: BubbleMessage) => parseThinkContent(message.content).displayContent

const getThinkingText = (message: BubbleMessage) => {
	const parts = [`${message.thinking || ''}`.trim(), getEmbeddedThinkingText(message)].filter(Boolean)
	return parts.join('\n\n')
}

const shouldShowThinking = (message: BubbleMessage) => !!getThinkingText(message)

const isThinkingOpen = (message: BubbleMessage) => message.thinkingOpen ?? !message.streamCompleted

const getThinkingStatus = (message: BubbleMessage) => {
	if (message.error) return 'error'
	return message.streamCompleted ? 'end' : 'thinking'
}

const getToolCalls = (message: BubbleMessage) => message.toolCalls || []

const getToolResults = (message: BubbleMessage) => message.toolResults || []

const getToolName = (name?: string) => `${name || 'tool'}`

const getToolResultThought = (toolResult: ChatToolResult) => `${toolResult.thought || ''}`.trim()

const hasToolResultThought = (toolResult: ChatToolResult) => !!getToolResultThought(toolResult)

const getToolResultOutput = (toolResult: ChatToolResult) => formatStructuredValue(toolResult.output)

const hasToolResultOutput = (toolResult: ChatToolResult) => {
	const output = toolResult.output
	return output !== null && output !== undefined && `${output}`.trim() !== ''
}

const getToolResultItems = (toolResult: ChatToolResult) =>
	Array.isArray(toolResult.contentItems) ? toolResult.contentItems : []

const hasToolResultItems = (toolResult: ChatToolResult) => getToolResultItems(toolResult).length > 0

const hasStructuredBlocks = (message: BubbleMessage) =>
	!!(getThinkingText(message) || getToolCalls(message).length || getToolResults(message).length)

const formatStructuredValue = (value: any) => {
	if (value === null || value === undefined || value === '') return '-'
	if (typeof value === 'string') return value
	try {
		return JSON.stringify(value, null, 2)
	} catch {
		return `${value}`
	}
}

const scrollContainerRef = ref<HTMLDivElement>()

defineExpose({
	scrollToBottom: () => {
		nextTick(() => {
			const container = scrollContainerRef.value
			if (!container) return
			container.scrollTop = container.scrollHeight
		})
	}
})

const handleRetry = (message: BubbleMessage) => {
	emit('retry', message)
}

const handleCopy = (message: BubbleMessage) => {
	const text = `${message.rawText || message.answerText || message.content || ''}`
	window.navigator.clipboard.writeText(text)
	ElMessage.success(t('pages.chat.copySuccess'))
}
</script>

<style lang="less" scoped>
:deep(.el-thinking) {
	margin: 0;
}
.chat-content {
	flex: 1;
	display: flex;
	flex-direction: column;
	overflow: hidden;
	padding: 18px;

	.item-list {
		flex: 1;
		overflow-y: auto;
		padding-right: 4px;
	}

	.message-bubble + .message-bubble {
		margin-top: 16px;
	}

	.empty-state {
		flex: 1;
		display: flex;
		flex-direction: column;
		align-items: center;
		justify-content: center;
		color: var(--text-tertiary);
	}
}

.avatar-wrapper {
	width: 40px;
	height: 40px;

	img {
		width: 100%;
		height: 100%;
		border-radius: 50%;
	}
}

.header-wrapper {
	.header-name {
		font-size: 14px;
		color: #979797;
	}
}

.msg-content-wrapper {
	display: flex;
	flex-direction: column;
	gap: 12px;

	.msg-content-text {
		font-size: 14px;
		line-height: 1.7;
		color: var(--text-primary);
		word-break: break-word;
	}

	.msg-content-text :deep(h3) {
		margin: 0 0 10px;
		font-size: 12px;
		font-weight: 700;
		color: var(--text-secondary);
	}

	.msg-content-text :deep(p) {
		margin: 0 0 10px;
		white-space: pre-wrap;
	}

	.msg-content-text :deep(blockquote) {
		margin: 8px 0;
		padding: 8px 12px;
		border-left: 4px solid var(--el-color-primary);
		background: var(--bg-container);
		border-radius: 0 8px 8px 0;
		color: var(--text-secondary);
		white-space: pre-wrap;
	}

	.msg-content-text :deep(pre) {
		margin: 8px 0;
		padding: 10px 12px;
		border-radius: 8px;
		background: var(--bg-container);
		white-space: pre-wrap;
		overflow: auto;
	}

	.msg-content-text :deep(code) {
		font-size: 13px;
	}

	.msg-content-text :deep(ul),
	.msg-content-text :deep(ol) {
		margin: 0 0 10px 20px;
		padding: 0;
	}

	.msg-content-text :deep(li) {
		margin: 4px 0;
	}
}

.structured-blocks {
	display: flex;
	flex-direction: column;
	gap: 12px;
}

.structured-block {
	padding: 12px;
	border: 1px solid var(--border-light);
	border-radius: 8px;
	background: var(--bg-container);
}

.structured-block__head {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 12px;
	margin-bottom: 10px;
}

.structured-block__title {
	font-size: 13px;
	font-weight: 600;
	color: var(--text-secondary);
}

.structured-block__body {
	font-size: 13px;
	line-height: 1.7;
	color: var(--text-primary);
}

.structured-block__body--thinking {
	white-space: pre-wrap;
}

.structured-block__markdown :deep(p) {
	margin: 0 0 8px;
}

.tool-list {
	display: flex;
	flex-direction: column;
	gap: 10px;
}

.tool-card {
	padding: 10px;
	border-radius: 8px;
	border: 1px solid var(--border-light);
	background: var(--bg-page);
}

.tool-card__head {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 12px;
	margin-bottom: 8px;
}

.tool-card__meta {
	display: flex;
	align-items: center;
	flex-wrap: wrap;
	gap: 8px;
	min-width: 0;
}

.tool-card__name,
.tool-card__id,
.tool-card__duration {
	font-size: 12px;
	color: var(--text-secondary);
	word-break: break-all;
}

.tool-card__section + .tool-card__section {
	margin-top: 8px;
}

.tool-card__label {
	margin-bottom: 4px;
	font-size: 12px;
	font-weight: 600;
	color: var(--text-secondary);
}

.tool-card__content {
	margin: 0;
	padding: 10px 12px;
	border-radius: 8px;
	background: var(--bg-container);
	color: var(--text-primary);
	font-size: 12px;
	line-height: 1.6;
	white-space: pre-wrap;
	word-break: break-word;
	overflow: auto;
}

.tool-card__content--error {
	color: var(--el-color-danger);
}

.footer-wrapper {
	display: flex;
	align-items: center;
	gap: 10px;

	.footer-time {
		font-size: 12px;
		margin-top: 3px;
	}
}

.file-wrap {
	display: flex;
	flex-wrap: wrap;
	margin-top: 8px;
}

.file-image {
	width: 96px;
	height: 96px;
	border-radius: 8px;
	object-fit: cover;
	margin-right: 8px;
	margin-bottom: 8px;
	border: 1px solid var(--border-light);
	cursor: zoom-in;
}

.footer-container {
	:deep(.el-button + .el-button) {
		margin-left: 8px;
	}
}

.loading-container {
	font-size: 14px;
	color: #333;
	padding: 12px;
	background: linear-gradient(to right, #fdfcfb 0%, #ffd1ab 100%);
	border-radius: 15px;
	box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

.loading-container span {
	display: inline-block;
	margin-left: 8px;
}

@keyframes bounce {
	0%,
	100% {
		transform: translateY(5px);
	}

	50% {
		transform: translateY(-5px);
	}
}

.loading-container span:nth-child(4n) {
	animation: bounce 1.2s ease infinite;
}

.loading-container span:nth-child(4n + 1) {
	animation: bounce 1.2s ease infinite;
	animation-delay: 0.3s;
}

.loading-container span:nth-child(4n + 2) {
	animation: bounce 1.2s ease infinite;
	animation-delay: 0.6s;
}

.loading-container span:nth-child(4n + 3) {
	animation: bounce 1.2s ease infinite;
	animation-delay: 0.9s;
}
</style>
