<template>
	<view class="page">
		<!-- Fixed custom navbar -->
		<view class="custom-navbar" :style="{ paddingTop: statusBarHeight + 'px' }">
			<view class="hero-bg hero-bg-left"></view>
			<view class="hero-bg hero-bg-right"></view>
			<view class="navbar">
				<view class="navbar-inner">
					<view class="navbar-left">
						<view class="nav-icon-btn" @tap="openHistoryPanel">
							<view class="icon-list">
								<view class="icon-list-line"></view>
								<view class="icon-list-line"></view>
								<view class="icon-list-line icon-list-line--sm"></view>
							</view>
						</view>
						<view class="nav-icon-btn" @tap="startNewChat">
							<view class="icon-compose">
								<view class="icon-compose-h"></view>
								<view class="icon-compose-v"></view>
							</view>
						</view>
					</view>
					<view class="navbar-center">
						<view class="navbar-title">AI 助手</view>
						<view class="navbar-sub">
							{{ activeChatTitle }}<text v-if="userName"> · {{ userName }}</text>
						</view>
					</view>
					<view class="navbar-placeholder"></view>
				</view>
			</view>
		</view>

		<!-- Spacer matching navbar height -->
		<view class="navbar-spacer" :style="{ height: navbarTotalHeight + 'px' }"></view>

		<view class="assistant-shell">

			<scroll-view
				scroll-y
				scroll-with-animation
				class="message-scroll"
				:scroll-into-view="scrollIntoView"
			>
				<view v-if="!currentUser.userId" class="notice-card">
					<view class="notice-title">请先登录后使用 AI 助手</view>
					<view class="notice-desc">当前页面已接入真实会话逻辑，发送消息和历史会话依赖登录态。</view>
				</view>

				<view v-else-if="!messageList.length" class="empty-card">
					<view class="empty-title">你好，我是 AI 团委助手</view>
					<view class="empty-desc">
						你可以直接提问团务流程问题，我会通过实时消息回复。
					</view>
				</view>

				<view
					v-for="item in messageList"
					:key="item.id"
					:id="item.anchorId"
					class="message-row"
					:class="{ 'message-row--user': isUserMessage(item) }"
				>
					<view class="avatar" :class="{ 'avatar--user': isUserMessage(item) }">
						{{ isUserMessage(item) ? '我' : '助' }}
					</view>
					<view class="message-main">
						<view class="bubble" :class="{ 'bubble--user': isUserMessage(item), 'bubble--pending': item.loading }">
							<view
								v-if="!isUserMessage(item) && shouldShowAssistantThinking(item)"
								class="thinking-row"
							>
								<view class="thinking-dot"></view>
								<text>{{ ASSISTANT_PENDING_TEXT }}</text>
							</view>
							<view
								v-if="isUserMessage(item)"
								class="bubble-text"
							>{{ item.content }}</view>
							<zero-markdown-view
								v-else-if="hasAssistantDisplayContent(item.content)"
								class="bubble-text bubble-markdown"
								:markdown="getAssistantDisplayContent(item.content)"
							/>
						</view>
						<view class="bubble-actions">
							<view class="bubble-action" @tap="copyMessage(item)">复制</view>
							<view
								v-if="isUserMessage(item)"
								class="bubble-action"
								@tap="fillContent(item.content || '')"
							>
								重用
							</view>
						</view>
					</view>
				</view>

				<view :id="bottomAnchorId" class="scroll-anchor"></view>
			</scroll-view>

			<view class="composer">
				<view class="mode-tags">
					<view
						v-for="item in chatModeOptions"
						:key="item.value"
						class="mode-tag"
						:class="{ 'mode-tag--active': activeChatMode === item.value, 'mode-tag--disabled': loading }"
						@tap="switchChatMode(item.value)"
					>
						{{ item.label }}
					</view>
				</view>
				<view class="composer-row">
					<textarea
						v-model="draft"
						class="composer-input"
						placeholder-style="color:#a59a94;"
						:placeholder="inputPlaceholder"
						:disabled="loading || !currentUser.userId"
						confirm-type="send"
						auto-height
						maxlength="-1"
						show-confirm-bar="false"
						@confirm="onSend"
					/>
					<view
						class="composer-send"
						:class="{ 'composer-send--disabled': !canSend }"
						@tap="onSend"
					>{{ loading ? '···' : '发送' }}</view>
				</view>
			</view>
		</view>

		<view v-if="showHistoryPanel" class="history-mask" @tap="closeHistoryPanel">
			<view class="history-panel" @tap.stop>
				<view class="history-head">
					<view>
						<view class="history-title">历史会话</view>
						<view class="history-subtitle">按最近更新时间展示</view>
					</view>
					<view class="history-close" @tap="closeHistoryPanel">关闭</view>
				</view>

				<scroll-view scroll-y class="history-scroll">
					<view v-if="historySections.length">
						<view v-for="section in historySections" :key="section.label" class="history-section">
							<view class="history-label">{{ section.label }}</view>
							<view
								v-for="item in section.items"
								:key="getHistoryKey(item)"
								class="history-item"
								:class="{ 'history-item--active': activeHistoryKey === getHistoryKey(item) }"
							>
								<view class="history-item-main" @tap="selectChat(item)">
									<view class="history-item-title">{{ getHistoryTitle(item) }}</view>
									<view class="history-item-time">
										{{ formatHistoryItemTime(item.createTime) }}
									</view>
								</view>
								<view
									class="history-delete"
									@tap.stop="removeChat(item)"
								>
									{{ deletingChatId === item.id ? '删除中' : '删除' }}
								</view>
							</view>
						</view>
					</view>
					<view v-else class="history-empty">暂无历史对话</view>
				</scroll-view>
			</view>
		</view>
	</view>
</template>

<script>
	import dayjs from '../../uni_modules/uv-ui-tools/libs/util/dayjs.js'
	import {
		activityOutlineGen,
		buildWebSocketUrl,
		creativeAssistant,
		getCurrentUser,
		listAiChatHistory,
		listAiChatList,
		manuscriptGen,
		removeAiChatList,
		sendMessage as sendChatMessage
	} from './chat-service'

	const ASSISTANT_PENDING_TEXT = '正在思考中...'
	const THINK_OPEN_TAG = '<think>'
	const THINK_CLOSE_TAG = '</think>'
	const CHAT_MODE_OPTIONS = [
		{ label: '聊天', value: 'chat' },
		{ label: '稿件', value: 'manuscript' },
		{ label: '创意', value: 'creative' },
		{ label: '策划', value: 'outline' }
	]

	export default {
		data() {
			return {
				ASSISTANT_PENDING_TEXT,
				chatModeOptions: CHAT_MODE_OPTIONS,
				statusBarHeight: 0,
				draft: '',
				activeChatMode: 'chat',
				loading: false,
				wsConnected: false,
				scrollIntoView: '',
				bottomAnchorId: `chat-bottom-${Date.now()}`,
				promptList: [
					'帮我生成团日活动策划大纲',
					'写一篇活动新闻稿开头',
					'优化竞赛申报书摘要',
					'整理五四评优申报材料清单'
				],
				messageList: [],
				historyList: [],
				activeChatId: null,
				activeConversationId: '',
				streamingMessageId: '',
				showHistoryPanel: false,
				deletingChatId: null,
				currentUser: {
					userId: null,
					nickname: '',
					realName: '',
					token: ''
				},
				socketTask: null,
				socketConnectPromise: null,
				initialized: false
			}
		},
		computed: {
			navbarTotalHeight() {
				return this.statusBarHeight + 44
			},
			canSend() {
				return !!this.currentUser.userId && !!this.draft.trim() && !this.loading
			},
			inputPlaceholder() {
				if (!this.currentUser.userId) {
					return '请先登录后使用 AI 助手'
				}
				if (this.activeChatMode === 'manuscript') {
					return '请输入稿件标题、活动时间地点、参与人员和亮点内容，可直接用自然语言描述'
				}
				if (this.activeChatMode === 'creative') {
					return '请输入创意生成指令，例如短视频脚本、海报排版、推文封面等需求'
				}
				if (this.activeChatMode === 'outline') {
					return '请输入活动策划需求，建议包含主题、受众、时间、经费、地点等信息'
				}
				return '说说你想问的问题'
			},
			userName() {
				return (
					this.currentUser.nickname ||
					this.currentUser.realName ||
					(this.currentUser.userId ? `用户${this.currentUser.userId}` : '')
				)
			},
			chatHeaderText() {
				if (this.loading) {
					return this.wsConnected ? 'AI 助手在线答疑' : 'AI 助手连接中...'
				}
				return 'AI 助手待命中'
			},
			activeHistoryKey() {
				if (this.activeChatId) {
					return `chat-${this.activeChatId}`
				}
				if (this.activeConversationId) {
					return `conversation-${this.activeConversationId}`
				}
				return ''
			},
			activeChatTitle() {
				if (!this.activeChatId && !this.activeConversationId) {
					return '新对话'
				}
				const target = this.historyList.find(
					(item) =>
						item.id === this.activeChatId ||
						(item.conversationId && item.conversationId === this.activeConversationId)
				)
				return target ? this.getHistoryTitle(target) : '当前会话'
			},
			historySections() {
				const groupMap = {}
				;[...this.historyList]
					.sort((left, right) => this.getTimestamp(right.updateTime || right.createTime) - this.getTimestamp(left.updateTime || left.createTime))
					.forEach((item) => {
						const label = this.getHistoryDateLabel(item.createTime || item.updateTime)
						if (!groupMap[label]) {
							groupMap[label] = []
						}
						groupMap[label].push(item)
					})
        console.log(Object.keys(groupMap).map((label) => ({
          label,
          items: groupMap[label]
        })))
				return Object.keys(groupMap).map((label) => ({
					label,
					items: groupMap[label]
				}))
			}
		},
		onLoad() {
			const { statusBarHeight } = uni.getSystemInfoSync()
			this.statusBarHeight = statusBarHeight || 0
		},
		onShow() {
			this.refreshUser()
			if (!this.initialized) {
				this.initAssistant()
				return
			}
			if (this.currentUser.userId) {
				this.loadHistoryList(false).catch((error) => {
					this.showError(error, '刷新历史会话失败')
				})
			}
		},
		onUnload() {
			this.disconnectWebSocket()
		},
		methods: {
			refreshUser() {
				const storedUser = uni.getStorageSync('user_info') || {}
				this.currentUser = {
					userId: Number(storedUser.userId || 0) || null,
					nickname: storedUser.nickname || storedUser.nickName || '',
					realName: storedUser.realName || storedUser.name || '',
					token: uni.getStorageSync('token') || null
				}
				return this.currentUser
			},
			async initAssistant() {
				try {
					this.refreshUser()
					if (!this.currentUser.userId) {
						return
					}
					await this.loadHistoryList(true)
				} catch (error) {
					this.showError(error, '初始化聊天页失败')
				} finally {
					this.initialized = true
				}
			},
			showToast(title) {
				uni.showToast({
					title,
					icon: 'none'
				})
			},
			showError(error, fallback) {
				this.showToast((error && error.message) || fallback)
			},
			ensureLogin(showToast = true) {
				this.refreshUser()
				if (this.currentUser.userId) {
					return true
				}
				if (showToast) {
					this.showToast('请先登录后使用 AI 助手')
				}
				return false
			},
			createMessageId(prefix) {
				return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`
			},
			isUserMessage(item) {
				return item.role === 'user'
			},
			getTimestamp(value) {
				if (!value) {
					return 0
				}
				const normalized = String(value).replace(/-/g, '/')
				const result = new Date(normalized).getTime()
				return Number.isNaN(result) ? 0 : result
			},
			getTimeLabel(value) {
				const source = value ? new Date(String(value).replace(/-/g, '/')) : new Date()
				if (Number.isNaN(source.getTime())) {
					return ''
				}
				const hours = `${source.getHours()}`.padStart(2, '0')
				const minutes = `${source.getMinutes()}`.padStart(2, '0')
				return `${hours}:${minutes}`
			},
			formatHistoryItemTime(value) {
				if (!value) {
					return ''
				}
				const date = dayjs(String(value).replace(/-/g, '/'))
				if (!date.isValid()) {
					return String(value || '')
				}
				return date.format('YYYY-MM-DD HH:mm')
			},
			isSameDate(left, right) {
				return (
					left.getFullYear() === right.getFullYear() &&
					left.getMonth() === right.getMonth() &&
					left.getDate() === right.getDate()
				)
			},
			getHistoryDateLabel(value) {
				if (!value) {
					return '更早'
				}
				const date = new Date(String(value).replace(/-/g, '/'))
				if (Number.isNaN(date.getTime())) {
					return '更早'
				}
				const now = new Date()
				if (this.isSameDate(date, now)) {
					return '今天'
				}
				const yesterday = new Date()
				yesterday.setDate(yesterday.getDate() - 1)
				if (this.isSameDate(date, yesterday)) {
					return '昨天'
				}
				return `${date.getFullYear()}年${date.getMonth() + 1}月${date.getDate()}日`
			},
			getHistoryKey(item) {
				if (item.id) {
					return `chat-${item.id}`
				}
				return `conversation-${item.conversationId || item.createTime || this.createMessageId('history')}`
			},
			getHistoryTitle(item) {
				const title = (item.title || '').trim()
				if (title) {
					return title
				}
				if (item.conversationId) {
					return `会话 ${item.conversationId.slice(0, 8)}`
				}
				return '未命名对话'
			},
			parseAssistantContent(value) {
				if (!value || value === ASSISTANT_PENDING_TEXT) {
					return {
						content: '',
						thinking: value === ASSISTANT_PENDING_TEXT
					}
				}

				let cursor = 0
				let content = ''
				let thinking = false

				while (cursor < value.length) {
					if (!thinking) {
						const openIndex = value.indexOf(THINK_OPEN_TAG, cursor)
						const closeIndex = value.indexOf(THINK_CLOSE_TAG, cursor)

						if (closeIndex !== -1 && (openIndex === -1 || closeIndex < openIndex)) {
							cursor = closeIndex + THINK_CLOSE_TAG.length
							continue
						}
						if (openIndex === -1) {
							content += value.slice(cursor)
							break
						}
						content += value.slice(cursor, openIndex)
						cursor = openIndex + THINK_OPEN_TAG.length
						thinking = true
						continue
					}

					const closeIndex = value.indexOf(THINK_CLOSE_TAG, cursor)
					if (closeIndex === -1) {
						break
					}
					cursor = closeIndex + THINK_CLOSE_TAG.length
					thinking = false
				}

				return {
					content,
					thinking
				}
			},
			getAssistantDisplayContent(value) {
				return this.parseAssistantContent(value).content
			},
			hasAssistantDisplayContent(value) {
				return !!this.getAssistantDisplayContent(value).trim()
			},
			shouldShowAssistantThinking(item) {
				if (item.content === ASSISTANT_PENDING_TEXT) {
					return true
				}
				if (!item.loading) {
					return false
				}
				return this.parseAssistantContent(item.content).thinking
			},
			getMessageCopyContent(item) {
				return this.isUserMessage(item)
					? item.content || ''
					: this.getAssistantDisplayContent(item.content).trim()
			},
			copyMessage(item) {
				const text = this.getMessageCopyContent(item)
				if (!text) {
					this.showToast('暂无可复制内容')
					return
				}
				uni.setClipboardData({
					data: text,
					success: () => {
						this.showToast('已复制')
					}
				})
			},
			fillContent(value) {
				if (this.loading) {
					return
				}
				this.draft = value
			},
			switchChatMode(mode) {
				if (this.loading || this.activeChatMode === mode) {
					return
				}
				this.activeChatMode = mode
			},
			scrollToBottom() {
				this.bottomAnchorId = `chat-bottom-${Date.now()}`
				this.$nextTick(() => {
					this.scrollIntoView = this.bottomAnchorId
				})
			},
			formatHistoryMessages(list) {
				const result = []
				;[...(list || [])]
					.sort((left, right) => this.getTimestamp(left.createTime) - this.getTimestamp(right.createTime))
					.forEach((item) => {
						const time = this.getTimeLabel(item.createTime)
						if (item.content) {
							result.push({
								id: this.createMessageId('user'),
								anchorId: this.createMessageId('anchor'),
								role: 'user',
								content: item.content,
								time
							})
						}
						if (item.reply) {
							result.push({
								id: this.createMessageId('assistant'),
								anchorId: this.createMessageId('anchor'),
								role: 'assistant',
								content: item.reply,
								time
							})
						}
					})
				return result
			},
			syncActiveChat() {
				if (!this.activeConversationId) {
					return
				}
				const current = this.historyList.find((item) => item.conversationId === this.activeConversationId)
				if (current && current.id) {
					this.activeChatId = current.id
				}
			},
			appendAssistantChunk(answer, conversationId) {
				if (conversationId) {
					this.activeConversationId = conversationId
				}
				if (!this.streamingMessageId) {
					const messageId = this.createMessageId('assistant')
					this.streamingMessageId = messageId
					this.messageList.push({
						id: messageId,
						anchorId: this.createMessageId('anchor'),
						role: 'assistant',
						content: answer,
						loading: true,
						time: this.getTimeLabel()
					})
					this.scrollToBottom()
					return
				}
				const target = this.messageList.find((item) => item.id === this.streamingMessageId)
				if (!target) {
					return
				}
				target.content =
					target.content === ASSISTANT_PENDING_TEXT ? answer : `${target.content}${answer}`
				this.scrollToBottom()
			},
			async finishAssistantMessage(conversationId) {
				if (conversationId) {
					this.activeConversationId = conversationId
				}
				const target = this.messageList.find((item) => item.id === this.streamingMessageId)
				if (target) {
					target.loading = false
					if (target.content === ASSISTANT_PENDING_TEXT) {
						target.content = ''
					}
				}
				this.streamingMessageId = ''
				this.loading = false
				await this.loadHistoryList(false)
				this.syncActiveChat()
			},
			disconnectWebSocket() {
				this.socketConnectPromise = null
				this.wsConnected = false
				const socketTask = this.socketTask
				this.socketTask = null
				if (socketTask && typeof socketTask.close === 'function') {
					try {
						socketTask.close({})
					} catch (error) {
						console.warn('socket close failed', error)
					}
				}
			},
			async handleSocketMessage(raw) {
				if (!raw) {
					return
				}
				try {
					const data = JSON.parse(raw)
					if (data && data.answer === '__END__') {
						await this.finishAssistantMessage(data.conversationId)
						this.disconnectWebSocket()
						return
					}
					if (data && (data.loading === 'true' || data.loading === true)) {
						return
					}
					const notice = (data && (data.notice || data.answer)) || ''
					if (notice) {
						this.appendAssistantChunk(String(notice), data.conversationId)
					}
				} catch (error) {
					console.warn('websocket message parse failed', error)
					this.appendAssistantChunk(String(raw))
				}
			},
			connectWebSocket() {
				const { userId } = this.refreshUser()
				if (!userId) {
					return Promise.reject(new Error('未获取到当前登录用户'))
				}
				const currentSocket = this.socketTask
				if (currentSocket && this.wsConnected) {
					return Promise.resolve()
				}
				if (currentSocket && this.socketConnectPromise) {
					return this.socketConnectPromise
				}
				if (currentSocket) {
					this.disconnectWebSocket()
				}

				let cleanupPending = () => undefined
				const connectPromise = new Promise((resolve, reject) => {
					let settled = false
					const socketTask = uni.connectSocket({
						url: buildWebSocketUrl(userId),
						complete: () => {}
					})

					cleanupPending = () => {
						if (this.socketConnectPromise === connectPromise) {
							this.socketConnectPromise = null
						}
					}

					socketTask.onOpen(() => {
						this.wsConnected = false
					})
					socketTask.onMessage((event) => {
						const raw = String((event && event.data) || '')
						if (raw === '连接成功') {
							this.wsConnected = true
							if (!settled) {
								settled = true
								cleanupPending()
								resolve()
							}
							return
						}
						this.handleSocketMessage(raw)
					})
					socketTask.onClose(() => {
						this.wsConnected = false
						if (this.socketTask === socketTask) {
							this.socketTask = null
						}
						if (!settled) {
							settled = true
							cleanupPending()
							reject(new Error('聊天连接已关闭'))
							return
						}
						if (this.loading && this.streamingMessageId) {
							const target = this.messageList.find((item) => item.id === this.streamingMessageId)
							if (target) {
								target.loading = false
								if (target.content === ASSISTANT_PENDING_TEXT) {
									target.content = '连接已断开，请稍后重试。'
								}
							}
							this.streamingMessageId = ''
							this.loading = false
						}
						cleanupPending()
					})
					socketTask.onError(() => {
						this.wsConnected = false
						if (!settled) {
							settled = true
							cleanupPending()
							reject(new Error('聊天连接失败'))
						}
					})
					this.socketTask = socketTask
				})

				this.socketConnectPromise = connectPromise
				return connectPromise
			},
			async loadHistoryList(autoSelect = true) {
				const { userId } = this.refreshUser()
				if (!userId) {
					this.historyList = []
					return
				}
				const response = await listAiChatList({ userId })
				this.historyList = Array.isArray(response) ? response : []
				this.syncActiveChat()
				if (
					autoSelect &&
					!this.activeChatId &&
					!this.activeConversationId &&
					this.historyList.length
				) {
					await this.selectChat(this.historyList[0], true)
				}
			},
			async loadChatHistory(item) {
				const { userId } = this.refreshUser()
				const response = await listAiChatHistory({
					chatId: item.id,
					conversationId: item.conversationId,
					userId
				})
				this.messageList = this.formatHistoryMessages(Array.isArray(response) ? response : [])
				this.scrollToBottom()
			},
			async selectChat(item, silent = false) {
				if (this.loading && !silent) {
					this.showToast('当前正在生成回复，请稍后再切换会话')
					return
				}
				this.activeChatId = item.id || null
				this.activeConversationId = item.conversationId || ''
				this.showHistoryPanel = false
				await this.loadChatHistory(item)
			},
			startNewChat() {
				if (this.loading) {
					this.showToast('当前正在生成回复，请稍后再开启新对话')
					return
				}
				this.disconnectWebSocket()
				this.activeChatId = null
				this.activeConversationId = ''
				this.streamingMessageId = ''
				this.messageList = []
				this.draft = ''
				this.showHistoryPanel = false
			},
			stringifyUnknown(value) {
				try {
					return JSON.stringify(value, null, 2)
				} catch (error) {
					return String(value ?? '')
				}
			},
			pickString(...values) {
				for (let index = 0; index < values.length; index += 1) {
					const value = values[index]
					if (typeof value === 'string' && value.trim()) {
						return value.trim()
					}
				}
				return ''
			},
			parseKeyValueInput(value) {
				const result = {}
				String(value || '')
					.split(/\n+/)
					.map((item) => item.trim())
					.filter(Boolean)
					.forEach((line) => {
						const match = line.match(/^([^:：]+)[:：]\s*(.*)$/)
						if (match) {
							result[match[1].trim()] = match[2].trim()
						}
					})
				return result
			},
			buildManuscriptPayload(query) {
				const fields = this.parseKeyValueInput(query)
				return {
					docType: fields['稿件类型'] || fields['类型'] || '新闻稿',
					eventType: fields['活动类型'] || '校园活动',
					theme: fields['稿件标题'] || fields['标题'] || fields['主题'] || query,
					time: fields['活动时间'] || fields['时间'] || '',
					location: fields['活动地点'] || fields['地点'] || '',
					participants: fields['参与人员'] || fields['人员'] || '',
					highlights: fields['亮点内容'] || fields['亮点'] || fields['内容'] || query,
					number: Number(fields['建议篇幅'] || fields['字数'] || 800),
					files: []
				}
			},
			formatManuscriptResult(payload, query) {
				if (typeof payload === 'string') {
					return payload
				}
				const data = payload || {}
				const title = this.pickString(
					data.title,
					data.theme,
					data.name,
					data.subject,
					data.data && data.data.title
				)
				const body = this.pickString(
					data.content,
					data.text,
					data.result,
					data.output,
					data.answer,
					data.manuscript,
					data.article,
					data.message,
					data.data && data.data.content,
					data.data && data.data.text,
					data.data && data.data.result,
					data.data && data.data.output,
					data.choices && data.choices[0] && data.choices[0].message && data.choices[0].message.content
				)
				if (title || body) {
					return `${title || this.buildManuscriptPayload(query).theme}\n\n${body}`
				}
				return this.stringifyUnknown(payload)
			},
			buildOutlinePayload(query) {
				const fields = this.parseKeyValueInput(query)
				return {
					event_theme: fields['活动主题'] || fields['主题'] || query,
					people: fields['面向受众'] || fields['受众'] || fields['参与人员'] || '团员青年',
					event_time: fields['活动时间'] || fields['时间'] || '待定',
					event_funding: fields['活动资金'] || fields['资金'] || fields['经费'] || '待定',
					event_type: fields['活动形式'] || fields['形式'] || '',
					event_location: fields['活动地点'] || fields['地点'] || '',
					event_meme: fields['备注'] || fields['补充'] || query
				}
			},
			formatOutlineResult(payload) {
				const data = payload || {}
				return (
					this.pickString(
						data.answer,
						data.raw && data.raw.answer,
						data.raw && data.raw.data && data.raw.data.answer,
						data.raw && data.raw.output,
						data.raw && data.raw.result,
						data.raw && data.raw.content,
						data.raw && data.raw.message
					) || this.stringifyUnknown(payload)
				)
			},
			getCreativeOutputs(payload) {
				if (payload && payload.outputs && typeof payload.outputs === 'object') {
					return payload.outputs
				}
				if (payload && payload.data && payload.data.outputs && typeof payload.data.outputs === 'object') {
					return payload.data.outputs
				}
				return payload || {}
			},
			getCreativeFileUrl(item) {
				return String(
					(item && (item.url || item.remote_url || item.downloadUrl || item.download_url || item.path)) || ''
				).trim()
			},
			getCreativeFileName(item, fallback) {
				return String((item && (item.name || item.filename)) || fallback).trim()
			},
			isCreativeImageFile(item) {
				const url = this.getCreativeFileUrl(item).toLowerCase()
				const contentType = String(
					(item && (item.contentType || item.content_type || item.mime_type)) || ''
				).toLowerCase()
				const fileType = String((item && item.type) || '').toLowerCase()
				const extension = String((item && item.extension) || '').toLowerCase()
				return (
					contentType.indexOf('image/') === 0 ||
					fileType === 'image' ||
					/\.(png|jpe?g|gif|webp|bmp|svg)(\?.*)?$/.test(url) ||
					['.png', '.jpg', '.jpeg', '.gif', '.webp', '.bmp', '.svg'].indexOf(extension) > -1
				)
			},
			formatFileList(title, list = []) {
				if (!Array.isArray(list) || !list.length) {
					return ''
				}
				return `\n\n### ${title}\n${list
					.map((item, index) => {
						const url = this.getCreativeFileUrl(item)
						const name = this.getCreativeFileName(item, `结果文件${index + 1}`)
						if (!url) {
							return ''
						}
						return `${index + 1}. [${name}](${url})`
					})
					.filter(Boolean)
					.join('\n')}`
			},
			formatImageList(title, list = []) {
				if (!Array.isArray(list) || !list.length) {
					return ''
				}
				const lines = list
					.map((item) => {
						const url = this.getCreativeFileUrl(item)
						if (!url) {
							return ''
						}
						return `![](${url})`
					})
					.filter(Boolean)
				if (!lines.length) {
					return ''
				}
				return `\n\n### ${title}\n${lines.join('\n\n')}`
			},
			formatCreativeResult(payload) {
				const outputs = this.getCreativeOutputs(payload)
				const script = this.pickString(outputs.Script_result, outputs.script, outputs.text)
				const videoMessage = this.pickString(outputs.video_message)
				const photoMessage = this.pickString(outputs.photo_message)
				const photoFiles = Array.isArray(outputs.photo_files)
					? outputs.photo_files
					: Array.isArray(outputs.photo_file)
					? outputs.photo_file
					: []
				const imageFiles = photoFiles.filter((item) => this.isCreativeImageFile(item))
				const otherPhotoFiles = photoFiles.filter((item) => imageFiles.indexOf(item) === -1)
				const files = [
					this.formatFileList('视频生成结果', outputs.video_result || []),
					this.formatImageList('图像 / 排版结果', imageFiles),
					this.formatFileList('图像 / 排版结果文件', otherPhotoFiles)
				].join('')
				const text = [
					videoMessage,
					photoMessage,
					script ? `### 脚本创作结果\n${script}` : '',
					files
				]
					.filter(Boolean)
					.join('\n\n')
				return text || this.stringifyUnknown(payload)
			},
			async sendModeMessage(query, assistantId) {
				const target = this.messageList.find((item) => item.id === assistantId)
				const setResult = (value) => {
					if (target) {
						target.content = value || '接口已返回，但未解析到内容。'
						target.loading = false
					}
				}
				if (this.activeChatMode === 'manuscript') {
					const result = await manuscriptGen(this.buildManuscriptPayload(query))
					setResult(this.formatManuscriptResult(result, query))
					return
				}
				if (this.activeChatMode === 'creative') {
					const result = await creativeAssistant({
						user_content: query,
						user_contentString: query,
						user_files: []
					})
					setResult(this.formatCreativeResult(result))
					return
				}
				if (this.activeChatMode === 'outline') {
					const result = await activityOutlineGen(this.buildOutlinePayload(query))
					setResult(this.formatOutlineResult(result))
				}
			},
			openHistoryPanel() {
				this.showHistoryPanel = true
				if (this.currentUser.userId) {
					this.loadHistoryList(false).catch((error) => {
						this.showError(error, '加载历史会话失败')
					})
				}
			},
			closeHistoryPanel() {
				this.showHistoryPanel = false
			},
			async removeChat(item) {
				if (this.loading) {
					this.showToast('当前正在生成回复，请稍后再删除会话')
					return
				}
				if (!item.id) {
					this.showToast('当前会话缺少标识，无法删除')
					return
				}

				const result = await new Promise((resolve) => {
					uni.showModal({
						title: '删除会话',
						content: `确定删除“${this.getHistoryTitle(item)}”吗？`,
						success: resolve,
						fail: () => resolve({ confirm: false })
					})
				})
				if (!result.confirm) {
					return
				}

				this.deletingChatId = item.id
				try {
					const isActive =
						this.activeChatId === item.id ||
						(item.conversationId && this.activeConversationId === item.conversationId)
					await removeAiChatList(item.id)
					await this.loadHistoryList(false)
					if (isActive) {
						const nextChat = this.historyList[0]
						if (nextChat) {
							this.activeChatId = nextChat.id || null
							this.activeConversationId = nextChat.conversationId || ''
							await this.loadChatHistory(nextChat)
						} else {
							this.startNewChat()
						}
					}
					this.showToast('删除成功')
				} catch (error) {
					this.showError(error, '删除失败')
				} finally {
					this.deletingChatId = null
				}
			},
			usePrompt(text) {
				this.fillContent(text)
			},
			async onSend() {
				if (!this.canSend) {
					if (!this.currentUser.userId) {
						this.ensureLogin(true)
					}
					return
				}
				const query = this.draft.trim()
				this.loading = true
				try {
					const assistantId = this.createMessageId('assistant')
					const userTime = this.getTimeLabel()
					this.messageList.push({
						id: this.createMessageId('user'),
						anchorId: this.createMessageId('anchor'),
						role: 'user',
						content: query,
						time: userTime
					})
					this.messageList.push({
						id: assistantId,
						anchorId: this.createMessageId('anchor'),
						role: 'assistant',
						content: ASSISTANT_PENDING_TEXT,
						loading: true,
						time: this.getTimeLabel()
					})
					this.streamingMessageId = assistantId
					this.draft = ''
					this.scrollToBottom()
					if (this.activeChatMode === 'chat') {
						await this.connectWebSocket()
						await sendChatMessage({
							query,
							conversationId: this.activeConversationId || undefined
						})
						return
					}
					await this.sendModeMessage(query, assistantId)
					this.streamingMessageId = ''
					this.loading = false
					this.scrollToBottom()
				} catch (error) {
					const target = this.streamingMessageId
						? this.messageList.find((item) => item.id === this.streamingMessageId)
						: null
					if (target) {
						target.loading = false
						target.content = '出错了，请稍后再试。'
					}
					this.streamingMessageId = ''
					this.loading = false
					this.disconnectWebSocket()
					this.showError(error, '发送失败')
				}
			}
		}
	}
</script>

<style lang="scss" scoped>
	.page {
		height: 100vh;
		padding: 0 24rpx 24rpx;
		box-sizing: border-box;
		display: flex;
		flex-direction: column;
		background:
			radial-gradient(circle at top left, rgba(20, 150, 242, 0.1), transparent 26%),
			//linear-gradient(180deg, #f9f4ef 0%, #f4ede7 100%);
      rgba(20, 150, 242, 0.1)
	}

	.custom-navbar {
		position: fixed;
		top: 0;
		left: 0;
		right: 0;
		z-index: 100;
		overflow: hidden;
		background: linear-gradient(180deg, #f7fbff 0%, #d9f0ff 44%, #f4fbff 100%);
		box-shadow: 0 10rpx 28rpx rgba(65, 105, 135, 0.12);
	}

	.hero-bg {
		position: absolute;
		border-radius: 50%;
		filter: blur(8rpx);
		opacity: 0.9;
	}

	.hero-bg-left {
		top: 84rpx;
		left: -30rpx;
		width: 300rpx;
		height: 200rpx;
		background: radial-gradient(circle, rgba(197, 237, 255, 0.95) 0%, rgba(197, 237, 255, 0) 72%);
	}

	.hero-bg-right {
		top: 46rpx;
		right: -40rpx;
		width: 360rpx;
		height: 260rpx;
		background: radial-gradient(circle, rgba(184, 231, 255, 0.92) 0%, rgba(184, 231, 255, 0) 72%);
	}

	.navbar {
		position: relative;
		z-index: 2;
		padding-left: 28rpx;
		padding-right: 20rpx;
	}

	.navbar-inner {
		display: flex;
		align-items: center;
		height: 88rpx;
		justify-content: center;
		position: relative;
	}

	.navbar-left {
		display: flex;
		align-items: center;
		gap: 8rpx;
		position: absolute;
		left: 0;
		top: 50%;
		transform: translateY(-50%);
	}

	.nav-icon-btn {
		display: flex;
		align-items: center;
		justify-content: center;
		width: 72rpx;
		height: 72rpx;
		border-radius: 24rpx;
		background: rgba(255, 255, 255, 0.72);
		box-shadow: 0 8rpx 18rpx rgba(115, 164, 196, 0.14);
	}

	/* List / history icon */
	.icon-list {
		display: flex;
		flex-direction: column;
		gap: 7rpx;
		width: 38rpx;
	}

	.icon-list-line {
		height: 4rpx;
		border-radius: 4rpx;
		background: #101214;
	}

	.icon-list-line--sm {
		width: 60%;
	}

	/* Plus / new-chat icon */
	.icon-compose {
		position: relative;
		width: 36rpx;
		height: 36rpx;
	}

	.icon-compose-h {
		position: absolute;
		top: 50%;
		left: 0;
		right: 0;
		height: 4rpx;
		border-radius: 4rpx;
		background: #101214;
		transform: translateY(-50%);
	}

	.icon-compose-v {
		position: absolute;
		top: 0;
		bottom: 0;
		left: 50%;
		width: 4rpx;
		border-radius: 4rpx;
		background: #101214;
		transform: translateX(-50%);
	}

	.navbar-center {
		min-width: 0;
		text-align: center;
		padding: 0 168rpx;
	}

	.navbar-title {
		font-size: 28rpx;
		font-weight: 700;
		color: #101214;
		line-height: 1.3;
		letter-spacing: 2rpx;
	}

	.navbar-sub {
		font-size: 22rpx;
		color: #5f6570;
		margin-top: 2rpx;
		overflow: hidden;
		text-overflow: ellipsis;
		white-space: nowrap;
	}

	.navbar-placeholder {
		width: 152rpx;
		flex-shrink: 0;
	}

	.navbar-spacer {
		flex-shrink: 0;
	}

	.assistant-shell {
		flex: 1;
		min-height: 0;
		display: flex;
		flex-direction: column;
		gap: 20rpx;
	}

	.message-scroll {
		flex: 1;
		min-height: 0;
		padding: 4rpx 2rpx 0;
	}

	.prompt-panel,
	.empty-card,
	.notice-card {
		padding: 24rpx;
		border-radius: 28rpx;
		background: rgba(255, 255, 255, 0.92);
		box-shadow: 0 10rpx 28rpx rgba(76, 49, 35, 0.08);
	}

	.prompt-title,
	.notice-title,
	.empty-title {
		font-size: 28rpx;
		font-weight: 700;
		color: #23272f;
	}

	.prompt-list {
		display: flex;
		flex-wrap: wrap;
		gap: 16rpx;
		margin-top: 18rpx;
	}

	.prompt-chip {
		padding: 14rpx 22rpx;
		border-radius: 999rpx;
		background: #fbebea;
		color: #1496f2;
		font-size: 24rpx;
	}

	.notice-card,
	.empty-card {
		margin-top: 22rpx;
	}

	.notice-desc,
	.empty-desc {
		margin-top: 12rpx;
		font-size: 24rpx;
		line-height: 1.7;
		color: #7d726d;
	}

	.message-row {
		display: flex;
		align-items: flex-start;
		margin-top: 22rpx;
	}

	.message-row--user {
		flex-direction: row-reverse;
	}

	.avatar {
		flex-shrink: 0;
		display: flex;
		align-items: center;
		justify-content: center;
		width: 68rpx;
		height: 68rpx;
		border-radius: 22rpx;
		background: rgba(20, 150, 242, 0.12);
		color: #1496f2;
		font-size: 26rpx;
		font-weight: 700;
	}

	.avatar--user {
		background: linear-gradient(145deg, #0f7dd1 0%, #1496f2 100%);
		color: #fff;
	}

	.message-main {
		display: flex;
		flex-direction: column;
		align-items: flex-start;
		max-width: calc(100% - 96rpx);
		margin-left: 16rpx;
	}

	.message-row--user .message-main {
		align-items: flex-end;
		margin-left: 0;
		margin-right: 16rpx;
	}

	.bubble {
		max-width: 100%;
		padding: 22rpx 24rpx;
		border-radius: 26rpx 26rpx 26rpx 8rpx;
		background: rgba(255, 255, 255, 0.92);
		box-shadow: 0 10rpx 28rpx rgba(76, 49, 35, 0.08);
	}

	.bubble--user {
		border-radius: 26rpx 26rpx 8rpx 26rpx;
		background: linear-gradient(145deg, #0f7dd1 0%, #1496f2 100%);
	}

	.bubble--pending {
		border: 1rpx solid rgba(240, 122, 61, 0.22);
	}

	.bubble-text {
		font-size: 28rpx;
		line-height: 1.75;
		color: #27303a;
		word-break: break-all;
		white-space: pre-wrap;
	}

	.bubble--user .bubble-text,
	.bubble--user .bubble-time {
		color: #fff;
	}

	.bubble-time {
		margin-top: 12rpx;
		font-size: 20rpx;
		color: #918781;
	}

	.thinking-row {
		display: flex;
		align-items: center;
		gap: 12rpx;
		margin-bottom: 10rpx;
		font-size: 24rpx;
		color: #c1652d;
	}

	.thinking-dot {
		width: 16rpx;
		height: 16rpx;
		border-radius: 50%;
		background: #f07a3d;
		box-shadow: 0 0 0 10rpx rgba(240, 122, 61, 0.12);
	}

	.bubble-actions {
		display: flex;
		gap: 14rpx;
		margin-top: 12rpx;
	}

	.bubble-action {
		padding: 8rpx 16rpx;
		border-radius: 999rpx;
		background: rgba(20, 150, 242, 0.08);
		color: #1496f2;
		font-size: 22rpx;
	}

	.composer {
		padding: 16rpx 16rpx 16rpx 24rpx;
		border-radius: 30rpx;
		background: rgba(255, 255, 255, 0.96);
		box-shadow: 0 12rpx 30rpx rgba(76, 49, 35, 0.08);
	}

	.mode-tags {
		display: flex;
		flex-wrap: wrap;
		gap: 14rpx;
		margin-bottom: 16rpx;
	}

	.mode-tag {
		padding: 10rpx 22rpx;
		border-radius: 999rpx;
		background: rgba(20, 150, 242, 0.08);
		color: #1496f2;
		font-size: 24rpx;
		line-height: 1.4;
	}

	.mode-tag--active {
		background: linear-gradient(145deg, #0f7dd1 0%, #1496f2 100%);
		color: #fff;
		box-shadow: 0 10rpx 20rpx rgba(20, 150, 242, 0.18);
	}

	.mode-tag--disabled {
		opacity: 0.6;
	}

	.composer-row {
		display: flex;
		align-items: flex-end;
		gap: 12rpx;
	}

	.composer-input {
		flex: 1;
		min-height: 72rpx;
		max-height: 240rpx;
		padding: 18rpx 0;
		font-size: 28rpx;
		line-height: 1.6;
		color: #27303a;
	}

	.composer-send {
		flex-shrink: 0;
		height: 72rpx;
		padding: 0 30rpx;
		border-radius: 20rpx;
		background: linear-gradient(145deg, #0f7dd1 0%, #1496f2 100%);
		color: #fff;
		font-size: 28rpx;
		font-weight: 700;
		display: flex;
		align-items: center;
		justify-content: center;
	}

	.composer-send--disabled {
		background: #d8cfcb;
	}

	.history-mask {
		position: fixed;
		inset: 0;
		z-index: 99;
		background: rgba(44, 28, 23, 0.36);
		display: flex;
		justify-content: flex-end;
	}

	.history-panel {
		width: 84%;
		height: 100vh;
		padding: 30rpx 24rpx 24rpx;
		background: #fff7f4;
		box-shadow: -12rpx 0 30rpx rgba(44, 28, 23, 0.12);
		display: flex;
		flex-direction: column;
	}

	.history-head {
		display: flex;
		align-items: flex-start;
		justify-content: space-between;
		gap: 16rpx;
	}

	.history-title {
		font-size: 32rpx;
		font-weight: 700;
		color: #2b2c31;
	}

	.history-subtitle {
		margin-top: 10rpx;
		font-size: 22rpx;
		color: #8a7f79;
	}

	.history-close {
		padding: 10rpx 18rpx;
		border-radius: 18rpx;
		background: rgba(20, 150, 242, 0.08);
		color: #1496f2;
		font-size: 24rpx;
	}

	.history-scroll {
		flex: 1;
		min-height: 0;
		margin-top: 24rpx;
	}

	.history-section + .history-section {
		margin-top: 24rpx;
	}

	.history-label {
		margin-bottom: 14rpx;
		padding-left: 6rpx;
		font-size: 22rpx;
		color: #8b817b;
	}

	.history-item {
		display: flex;
		align-items: center;
		gap: 16rpx;
		padding: 20rpx;
		border-radius: 24rpx;
		background: rgba(255, 255, 255, 0.92);
		border: 1rpx solid rgba(20, 150, 242, 0.06);
		box-shadow: 0 10rpx 28rpx rgba(76, 49, 35, 0.06);
	}

	.history-item + .history-item {
		margin-top: 14rpx;
	}

	.history-item--active {
		border-color: rgba(20, 150, 242, 0.2);
		background: #fff1ec;
	}

	.history-item-main {
		flex: 1;
		min-width: 0;
	}

	.history-item-title {
		font-size: 26rpx;
		font-weight: 700;
		color: #2f3136;
		line-height: 1.5;
	}

	.history-item-time {
		margin-top: 10rpx;
		font-size: 22rpx;
		color: #8e837d;
	}

	.history-delete {
		flex-shrink: 0;
		padding: 10rpx 16rpx;
		border-radius: 18rpx;
		background: rgba(20, 150, 242, 0.08);
		color: #1496f2;
		font-size: 22rpx;
	}

	.history-empty {
		padding: 40rpx 20rpx;
		text-align: center;
		font-size: 24rpx;
		color: #978b84;
	}

	.scroll-anchor {
		height: 2rpx;
	}

	/* Markdown rendered inside assistant bubbles */
	.bubble-markdown {
		display: block;
		font-size: 28rpx;
		line-height: 1.75;
		color: #27303a;
		word-break: break-all;

		p { margin: 0 0 16rpx; }
		p:last-child { margin-bottom: 0; }

		h1, h2, h3, h4, h5, h6 {
			font-weight: 700;
			margin: 20rpx 0 10rpx;
			color: #1a1f27;
		}
		h1 { font-size: 38rpx; }
		h2 { font-size: 34rpx; }
		h3 { font-size: 30rpx; }

		ul, ol {
			padding-left: 36rpx;
			margin: 10rpx 0;
		}
		li { margin-bottom: 8rpx; }

		code {
			display: inline;
			padding: 2rpx 10rpx;
			border-radius: 8rpx;
			background: rgba(20, 150, 242, 0.08);
			color: #1496f2;
			font-size: 24rpx;
			font-family: monospace;
		}

		pre {
			padding: 22rpx;
			border-radius: 16rpx;
			background: #f3ede8;
			overflow-x: auto;
			margin: 16rpx 0;
		}
		pre code {
			padding: 0;
			background: transparent;
			color: #3d2e27;
			font-size: 24rpx;
			white-space: pre;
		}

		blockquote {
			margin: 12rpx 0;
			padding: 10rpx 20rpx;
			border-left: 6rpx solid rgba(20, 150, 242, 0.3);
			color: #6b5f59;
			background: rgba(20, 150, 242, 0.04);
			border-radius: 0 12rpx 12rpx 0;
		}

		hr {
			margin: 20rpx 0;
			border: none;
			border-top: 1rpx solid rgba(20, 150, 242, 0.15);
		}

		strong { font-weight: 700; }
		em { font-style: italic; }
	}
</style>
