<template>
  <div class="chat-page">
    <!-- 顶部标签栏 -->
    <TabsBar
      :tabs="qaModuleTabs"
      v-model="activeTab"
    />

    <div class="chat-layout">
      <!-- 侧边栏（只在智能对话时显示） -->
      <div v-show="activeTab === 'chat'" class="sidebar">
        <div class="sidebar-header">
          <div class="app-logo">
            <RobotOutlined class="logo-icon" />
            <span class="logo-text">AI智能助手</span>
          </div>
          <button class="new-chat-btn" @click="handleNewSession">
            <PlusOutlined class="btn-icon" />
            <span>新建对话</span>
          </button>
        </div>
        
        <div class="sidebar-content">
          <div class="session-list-title">最近对话</div>
          <div class="session-list">
            <div v-if="isLoadingSessions" class="session-loading">
              <LoadingOutlined class="loading-spinner" />
              <span>加载中...</span>
            </div>
            <div v-else-if="sessions.length === 0" class="session-empty">
              <span>暂无对话记录</span>
            </div>
            <template v-else>
              <div 
                v-for="session in sessions" 
                :key="session.id" 
                :class="['session-item', { active: activeSessionId === session.id }]"
                @click="handleSessionClick(session)"
              >
                <div class="session-icon">
                  <MessageOutlined v-if="activeSessionId === session.id" />
                  <EditOutlined v-else />
                </div>
                <div class="session-content">
                  <div class="session-title">{{ session.title }}</div>
                  <div class="session-date">{{ formatDate(session.createdAt) }}</div>
                </div>
                <div class="session-actions" @click.stop>
                  <button class="action-btn delete-btn" @click="handleDeleteSession(session)" title="删除">
                    <DeleteOutlined />
                  </button>
                </div>
              </div>
            </template>
          </div>
        </div>
        
        <div class="sidebar-footer">
        </div>
      </div>
      
      <!-- 主内容区 -->
      <div :class="['main-area', { 'full-width': activeTab !== 'chat' }]">
        <!-- 智能对话 -->
        <div v-show="activeTab === 'chat'" class="tab-content">
          <div class="chat-messages" ref="chatMessagesRef">
            <div v-if="messages.length === 0" class="empty-state">
              <BulbOutlined class="empty-icon" />
              <div class="empty-title">开始新对话</div>
              <div class="empty-desc">向AI助手提问，获得专业解答</div>
              <div class="quick-questions">
                <button 
                  v-for="(question, index) in quickQuestions" 
                  :key="index"
                  class="quick-question-btn"
                  @click="sendQuickQuestion(question)"
                >
                  {{ question }}
                </button>
              </div>
            </div>
            
            <div v-for="(message, index) in messages" :key="message.id" :class="['chat-message', message.type, { 'is-streaming': message.isStreaming }]">
              <div class="message-avatar">
                <UserOutlined v-if="message.type === 'user'" />
                <RobotOutlined v-else />
              </div>
              <div class="message-main">
                <div class="message-header">
                  <span class="message-role">{{ message.type === 'user' ? '你' : 'AI助手' }}</span>
                  <span class="message-time">{{ formatTime(message.timestamp) }}</span>
                </div>
                <div class="message-content">
                  <template v-if="message.type === 'ai'">
                    <template v-if="message.isStreaming">
                      <template v-if="message.status === 'thinking'">
                        <div class="ai-status thinking-ai">
                          <div class="thinking-ai-visualizer">
                            <div class="ai-orb">
                              <div class="ai-orb-core"></div>
                              <div class="ai-orb-glow"></div>
                              <div class="ai-orb-ring"></div>
                            </div>
                            <div class="ai-wave-container">
                              <span></span><span></span><span></span><span></span><span></span>
                            </div>
                          </div>
                          <span class="thinking-ai-text">{{ message.thinkingContent || '正在思考...' }}</span>
                        </div>
                      </template>
                      
                      <template v-else-if="message.status === 'searching'">
                        <div class="ai-status searching-mini">
                          <div class="searching-mini-row">
                            <div class="searching-mini-spinner">
                              <div></div><div></div><div></div><div></div>
                            </div>
                            <div class="searching-mini-info">
                              <span class="searching-mini-text">正在检索知识库</span>
                              <span class="searching-mini-collection" v-if="message.searchingCollection">{{ message.searchingCollection }}</span>
                            </div>
                          </div>
                          <div class="searching-mini-bars">
                            <div :class="{ active: message.searchProgress >= 1 }"></div>
                            <div :class="{ active: message.searchProgress >= 2 }"></div>
                            <div :class="{ active: message.searchProgress >= 3 }"></div>
                          </div>
                        </div>
                      </template>
                      
                      <template v-else-if="message.status === 'streaming' || message.content">
                        <template v-for="(segment, segIdx) in getContentWithImages(message.content, message.images)" :key="segIdx">
                          <div v-if="segment.type === 'text'" class="streaming-output">
                            <span v-html="formatMessage(segment.content)"></span>
                            <span class="streaming-cursor" v-if="message.isStreaming && segIdx === getContentWithImages(message.content, message.images).length - 1"></span>
                          </div>
                          <div v-else-if="segment.type === 'image'" class="chat-image-item" @click="previewImage(segment.image)">
                            <img
                              :src="getImageDisplayUrl(segment.image)"
                              :alt="segment.image.description || segment.image.alt || '相关图片'"
                              :title="segment.image.description || segment.image.source || ''"
                              loading="lazy"
                              @error="handleImageError"
                              @load="handleImageLoad(segment.image)"
                            />
                          </div>
                        </template>
                      </template>
                    </template>
                    
                    <template v-else>
                      <template v-for="(segment, segIdx) in getContentWithImages(message.content, message.images)" :key="segIdx">
                        <div v-if="segment.type === 'text'" class="content-paragraph" v-html="formatMessage(segment.content)"></div>
                        <div v-else-if="segment.type === 'image'" class="chat-image-item" @click="previewImage(segment.image)">
                          <img
                            :src="getImageDisplayUrl(segment.image)"
                            :alt="segment.image.description || segment.image.alt || '相关图片'"
                            :title="segment.image.description || segment.image.source || ''"
                            loading="lazy"
                            @error="handleImageError"
                            @load="handleImageLoad(segment.image)"
                          />
                        </div>
                      </template>
                      
                      <div 
                        v-if="getMessageReferences(message.id).length > 0" 
                        class="references-card"
                        :class="{ 'expanded': expandedReferences[message.id] }"
                      >
                        <div 
                          class="references-header"
                          @click="toggleReferences(message.id)"
                        >
                          <BookOutlined class="ref-icon" />
                          <span class="ref-title">引用来源</span>
                          <span class="ref-count">{{ getMessageReferences(message.id).length }} 处引用</span>
                          <UpOutlined v-if="expandedReferences[message.id]" class="toggle-icon" />
                          <DownOutlined v-else class="toggle-icon" />
                        </div>
                        
                        <transition name="expand">
                          <div v-if="expandedReferences[message.id]" class="references-list">
                            <template v-for="(group, groupIndex) in getGroupedReferences(message.id)" :key="groupIndex">
                              <div class="ref-group" :class="{ 'group-collapsed': collapsedGroups[group.filename] }">
                                <div class="ref-group-header" @click="toggleGroupCollapse(group.filename)">
                                  <RightOutlined v-if="collapsedGroups[group.filename]" class="group-collapse-icon" />
                                  <DownOutlined v-else class="group-collapse-icon" />
                                  <FileTextOutlined class="ref-file-icon" />
                                  <span class="ref-group-filename">{{ group.filename }}</span>
                                  <span class="ref-group-count">{{ group.items.length }} 处</span>
                                </div>
                                <transition name="group-expand">
                                  <div v-show="!collapsedGroups[group.filename]" class="ref-group-body">
                                    <div
                                      v-for="(ref, refIndex) in group.items"
                                      :key="refIndex"
                                      class="reference-item"
                                      :class="{ 'highlighted': highlightedRef === ref.id }"
                                      @click="navigateToFileReader(ref)"
                                    >
                                      <div class="ref-item-header">
                                        <span class="ref-location" v-if="ref.location"><EnvironmentOutlined /> {{ ref.location }}</span>
                                        <span class="ref-score" v-if="ref.score">{{ ref.score.toFixed(1) }}%</span>
                                      </div>
                                      <div class="ref-context" v-if="ref.context">
                                        "{{ ref.context }}"
                                      </div>
                                    </div>
                                  </div>
                                </transition>
                              </div>
                            </template>
                          </div>
                        </transition>
                      </div>
                      
                      <div v-if="message.tables && message.tables.length > 0" class="media-gallery tables-gallery">
                        <h4 class="gallery-title"><TableOutlined /> 数据表格</h4>
                        <div 
                          v-for="(table, tableIdx) in message.tables" 
                          :key="tableIdx"
                          class="table-container"
                        >
                          <div v-if="table.title" class="table-title">{{ table.title }}</div>
                          <div class="table-wrapper">
                            <table class="data-table">
                              <thead v-if="table.headers">
                                <tr>
                                  <th v-for="(header, hIdx) in table.headers" :key="hIdx">{{ header }}</th>
                                </tr>
                              </thead>
                              <tbody>
                                <tr v-for="(row, rIdx) in (table.rows || table.data || [])" :key="rIdx">
                                  <td v-for="(cell, cIdx) in row" :key="cIdx">{{ cell }}</td>
                                </tr>
                              </tbody>
                            </table>
                          </div>
                        </div>
                      </div>
                    </template>
                    
                    <div v-if="message.isError" class="error-hint">
                      <WarningOutlined class="error-icon" />
                      <span class="error-text">回答生成失败，请重试</span>
                    </div>
                  </template>
                  
                  <template v-else>
                    <span v-html="formatMessage(message.content)"></span>
                  </template>
                </div>
                
                <div v-if="message.type === 'ai' && !message.isStreaming && message.responseTime > 0" class="response-time-outside">
                  <ClockCircleOutlined class="response-time-icon" />
                  <span>{{ formatResponseTime(message.responseTime) }}</span>
                </div>
                
                <div v-if="message.type === 'ai' && !message.isStreaming" class="message-actions">
                  <button class="message-action-btn" @click="copyMessage(message.content)" title="复制">
                    <FileTextOutlined />
                  </button>
                  <button class="message-action-btn" @click="regenerateMessage(index)" title="重新生成">
                    <ReloadOutlined />
                  </button>
                  <button v-if="!message.isError" class="message-action-btn" @click="likeMessage(index)" title="点赞">
                    <LikeOutlined />
                  </button>
                </div>
              </div>
            </div>
          </div>

          <div class="chat-input-area">
            <div class="input-wrapper">
              <textarea
                v-model="inputValue"
                placeholder="输入您的问题..."
                class="chat-textarea"
                ref="textareaRef"
                @keydown="handleKeydown"
                @input="autoResize"
                :disabled="isLoading"
                rows="1"
              />
              <div class="input-actions">
                <button
                  v-if="isLoading"
                  class="stop-btn"
                  @click="handleCancelRequest"
                  title="停止生成"
                >
                  <StopOutlined />
                </button>
                <button
                  v-else
                  class="send-btn"
                  :class="{ 'has-text': inputValue.trim() }"
                  @click="handleSend"
                  :disabled="!inputValue.trim()"
                  title="发送"
                >
                  <ArrowUpOutlined />
                </button>
              </div>
            </div>
          </div>
        </div>

        <!-- 问答历史 -->
        <div v-show="activeTab === 'history'" class="tab-content">
          <div class="history-page">
            <div class="page-header">
              <h1><HistoryOutlined /> 问答历史</h1>
              <div class="header-stats">
                <div class="stat-item">
                  <span class="stat-value">{{ sessions.length }}</span>
                  <span class="stat-label">会话总数</span>
                </div>
                <div class="stat-item">
                  <span class="stat-value">{{ Object.values(sessionMessageCount).reduce((sum, count) => sum + count, 0) }}</span>
                  <span class="stat-label">消息总数</span>
                </div>
                <div class="stat-item">
                  <span class="stat-value">{{ sessions.filter(s => sessionMessageCount[s.id] > 0).length }}</span>
                  <span class="stat-label">有记录会话</span>
                </div>
              </div>
            </div>

            <div class="page-content">
              <!-- 加载状态 -->
              <div v-if="isLoadingSessions" class="loading-container">
                <a-spin size="large" />
                <p style="margin-top: var(--space-md); color: var(--text-secondary);">正在加载会话列表...</p>
              </div>
              
              <!-- 空状态 -->
              <div v-else-if="sessions.length === 0" class="empty-state">
                <a-empty description="暂无历史对话记录" />
                <button class="btn btn-primary" @click="handleNewSession" style="margin-top: var(--space-md);">
                  开始新对话
                </button>
              </div>
              
              <!-- 会话列表卡片 -->
              <div v-else class="content-card">
                <div class="card-header" @click="sessionListCollapsed = !sessionListCollapsed">
                  <h2><FileTextOutlined /> 会话列表</h2>
                  <DownOutlined v-if="!sessionListCollapsed" class="collapse-icon" />
                  <DownOutlined v-else class="collapse-icon collapsed" />
                </div>
                <div class="card-content" v-show="!sessionListCollapsed">
                  <div class="session-card-list">
                    <div 
                      v-for="session in sessions" 
                      :key="session.id" 
                      :class="['session-card', { active: activeSessionId === session.id }]"
                    >
                      <div class="session-card-main">
                        <div class="session-card-info">
                          <h3>{{ session.title }}</h3>
                          <div class="session-card-meta">
                            <span><ClockCircleOutlined /> {{ formatDate(session.createdAt) }}</span>
                            <span><MessageOutlined /> {{ sessionMessageCount[session.id] !== undefined ? sessionMessageCount[session.id] + ' 条消息' : '加载中...' }}</span>
                          </div>
                        </div>
                        <div class="session-card-actions">
                          <button 
                            class="btn btn-primary btn-sm" 
                            @click="viewSessionDetail(session)"
                            :disabled="isLoading"
                          >
                            查看
                          </button>
                          <button 
                            class="btn btn-danger btn-sm" 
                            @click="handleDeleteSession(session)"
                            :disabled="sessions.length <= 1"
                          >
                            删除
                          </button>
                        </div>
                      </div>
                    </div>
                  </div>
                </div>
              </div>

              <div class="content-card">
                <div class="card-header" @click="reportCollapsed = !reportCollapsed">
                  <h2><BarChartOutlined /> 学习报告</h2>
                  <DownOutlined v-if="!reportCollapsed" class="collapse-icon" />
                  <DownOutlined v-else class="collapse-icon collapsed" />
                </div>
                <div class="card-content" v-show="!reportCollapsed">
                  <div class="report-highlights">
                    <div class="report-highlight">
                      <div class="highlight-value">12</div>
                      <div class="highlight-label">提问总数</div>
                    </div>
                    <div class="report-highlight">
                      <div class="highlight-value">85%</div>
                      <div class="highlight-label">满意度</div>
                    </div>
                    <div class="report-highlight">
                      <div class="highlight-value">3</div>
                      <div class="highlight-label">高频知识点</div>
                    </div>
                  </div>

                  <div style="margin-top: var(--space-xl);">
                    <h3 style="margin-bottom: var(--space-md);">高频知识点</h3>
                    <div class="tag-group">
                      <span class="tag-item">考勤制度</span>
                      <span class="tag-item">请假流程</span>
                      <span class="tag-item">加班管理</span>
                    </div>
                  </div>

                  <div style="margin-top: var(--space-xl);">
                    <h3 style="margin-bottom: var(--space-md);">推荐学习内容</h3>
                    <div class="recommend-list">
                      <div class="recommend-item">考勤异常处理流程</div>
                      <div class="recommend-item">请假制度实施细则</div>
                      <div class="recommend-item">加班申请管理办法</div>
                    </div>
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>

        <!-- 质量分析 -->
        <div v-show="activeTab === 'quality'" class="tab-content">
          <div class="quality-page">
            <div class="page-header">
              <h1><BarChartOutlined /> 质量分析</h1>
              <div class="header-badge">问答质量闭环</div>
            </div>

            <div class="page-content">
              <div class="content-card">
                <div class="card-header" @click="qualityReportCollapsed = !qualityReportCollapsed">
                  <h2><LineChartOutlined /> 质量报告</h2>
                  <div class="header-right">
                    <span class="status-badge status-info">2026年3月</span>
                    <DownOutlined v-if="!qualityReportCollapsed" class="collapse-icon" />
                    <DownOutlined v-else class="collapse-icon collapsed" />
                  </div>
                </div>
                <div class="card-content" v-show="!qualityReportCollapsed">
                  <div style="margin-bottom: 24px;">
                    <h3>热门问题 TOP3</h3>
                    <div class="rank-list">
                      <div v-for="(item, index) in qualityReport.topQuestions" :key="item.id" class="rank-item">
                        <span class="rank-number">{{ index + 1 }}</span>
                        <div class="rank-content">
                          <div class="rank-question">{{ item.question }}</div>
                          <div class="rank-count">提问 {{ item.count }} 次</div>
                        </div>
                      </div>
                    </div>
                  </div>

                  <div style="margin-bottom: 24px;">
                    <h3>低分回答</h3>
                    <div class="low-score-list">
                      <div v-for="item in qualityReport.lowScoreAnswers" :key="item.id" class="low-score-item">
                        <div class="low-score-question">{{ item.question }}</div>
                        <div class="low-score-score">
                          <StarFilled class="score-star" />
                          <span>{{ item.score }} 分</span>
                        </div>
                      </div>
                    </div>
                  </div>
                </div>
              </div>

              <div class="content-card">
                <div class="card-header" @click="faqCollapsed = !faqCollapsed">
                  <h2><BookOutlined /> FAQ管理</h2>
                  <div class="header-right">
                    <div class="header-actions">
                      <button class="btn btn-primary btn-sm" @click="handleAddFaq"><PlusOutlined /> 新增FAQ</button>
                    </div>
                    <DownOutlined v-if="!faqCollapsed" class="collapse-icon" />
                    <DownOutlined v-else class="collapse-icon collapsed" />
                  </div>
                </div>
                <div class="card-content" v-show="!faqCollapsed">
                  <div class="faq-list">
                    <div v-for="item in faqList" :key="item.id" class="faq-item">
                      <div class="faq-question">Q: {{ item.question }}</div>
                      <div class="faq-answer">A: {{ item.answer }}</div>
                      <div class="faq-actions">
                        <button class="btn btn-primary btn-xs" @click="handleEditFaq(item)">编辑</button>
                        <button class="btn btn-danger btn-xs" @click="handleDeleteFaq(item)">删除</button>
                      </div>
                    </div>
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>

    <a-modal
      title="编辑FAQ"
      v-model:open="showEditFaqModal"
      :width="600"
      @ok="handleSaveFaq"
      @cancel="showEditFaqModal = false"
    >
      <div class="form-group">
        <label class="form-label">问题</label>
        <input class="form-input" v-model="editQuestion" placeholder="请输入问题" />
      </div>
      <div class="form-group">
        <label class="form-label">答案</label>
        <textarea class="form-input" v-model="editAnswer" placeholder="请输入答案" rows="4" />
      </div>
    </a-modal>
    
    <!-- 会话详情模态框 -->
    <a-modal
      v-model:open="showSessionDetailModal"
      :title="null"
      :footer="null"
      :width="900"
      centered
      :closable="false"
      class="session-detail-modal"
      @cancel="showSessionDetailModal = false"
    >
      <div class="session-detail-container">
        <!-- 模态框头部 -->
        <div class="detail-header">
          <div class="detail-title">
            <MessageOutlined class="title-icon" />
            <h2>{{ currentSession?.title || '对话详情' }}</h2>
          </div>
          <div class="detail-meta">
            <span class="meta-item">
              <ClockCircleOutlined /> {{ currentSession ? formatDate(currentSession.createdAt) : '' }}
            </span>
            <span class="meta-item">
              <MessageOutlined /> {{ sessionDetailMessages.length }} 条消息
            </span>
          </div>
        </div>
        
        <!-- 加载状态 -->
        <div v-if="isLoadingDetail" class="detail-loading">
          <a-spin size="large" />
          <p>正在加载对话记录...</p>
        </div>
        
        <!-- 消息列表（带滚动条） -->
        <div v-else class="detail-messages" ref="detailMessagesRef">
          <template v-if="sessionDetailMessages.length > 0">
            <div 
              v-for="(msg, index) in sessionDetailMessages" 
              :key="msg.id" 
              :class="['detail-message', msg.type]"
            >
              <div class="message-avatar-small">
                <UserOutlined v-if="msg.type === 'user'" />
                <RobotOutlined v-else />
              </div>
              <div class="message-body">
                <div class="message-role-label">
                  {{ msg.type === 'user' ? '你' : 'AI助手' }}
                  <span class="message-time-small">{{ formatTime(msg.timestamp) }}</span>
                </div>
                <div class="message-content-detail" v-html="formatMessage(msg.content)"></div>
              </div>
            </div>
          </template>
          
          <!-- 空状态 -->
          <div v-else class="detail-empty">
            <a-empty description="该会话暂无对话记录" />
          </div>
        </div>
        
        <!-- 模态框底部操作栏 -->
        <div class="detail-footer">
          <button class="btn btn-secondary" @click="showSessionDetailModal = false">
            关闭
          </button>
          <button 
            class="btn btn-primary" 
            @click="goToChatFromDetail"
            v-if="currentSession?.sessionId"
          >
            在对话中查看 →
          </button>
        </div>
      </div>
    </a-modal>
    
    <!-- 图片预览模态框 -->
    <div v-if="showImageModal && previewImageData" class="image-modal-overlay" @click="showImageModal = false">
      <div class="image-modal-content" @click.stop>
        <div class="image-modal-header">
          <h3>图片预览</h3>
          <button class="modal-close-btn" @click="showImageModal = false">
            <CloseOutlined />
          </button>
        </div>
        <div class="image-modal-body">
          <img 
            :src="getImageDisplayUrl(previewImageData)" 
            :alt="previewImageData.alt || previewImageData.caption || previewImageData.description"
            class="preview-image"
          />
        </div>
        <div v-if="previewImageData.source || previewImageData.page" class="image-modal-footer">
          <div class="image-meta-row">
            <span v-if="previewImageData.source"><FileTextOutlined /> {{ previewImageData.source }}</span>
            <span v-if="previewImageData.page" class="detail-item">第{{ previewImageData.page }}页</span>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, computed, watch, onMounted, onUnmounted, nextTick } from 'vue'
import { message, Modal } from 'ant-design-vue'
import {
  RobotOutlined,
  MessageOutlined,
  EditOutlined,
  DeleteOutlined,
  BulbOutlined,
  UserOutlined,
  BarChartOutlined,
  LineChartOutlined,
  StarFilled,
  BookOutlined,
  FileTextOutlined,
  WarningOutlined,
  ClockCircleOutlined,
  HistoryOutlined,
  PlusOutlined,
  StopOutlined,
  ArrowRightOutlined,
  ArrowUpOutlined,
  UpOutlined,
  DownOutlined,
  LoadingOutlined,
  ReloadOutlined,
  QuestionCircleOutlined,
  ThunderboltOutlined,
  TrophyOutlined,
  FileSearchOutlined,
  LikeOutlined,
  EnvironmentOutlined,
  PictureOutlined,
  TableOutlined,
  EyeOutlined,
  DownloadOutlined,
  CloseOutlined
} from '@ant-design/icons-vue'
import { getDirectPermission } from '../utils/permission.js'
import { useRouter } from 'vue-router'
import aiAPI from '@/api/ai'
import { fileAPI } from '@/api/file.js'
import imageAPI from '@/api/image.js'
import TabsBar from './common/TabsBar.vue'

const router = useRouter()

const tabs = [
  { id: 'chat', name: '智能对话', icon: MessageOutlined, permission: 'qa:chat' },
  { id: 'history', name: '问答历史', icon: HistoryOutlined, permission: 'qa:faq' }
]

const qaModuleTabs = computed(() => tabs.filter(tab => getDirectPermission(tab.permission)).map(tab => ({
  key: tab.id,
  label: tab.name,
  icon: tab.icon
})))

const activeTab = ref('chat')

const getFirstAccessibleTab = () => {
  const firstTab = tabs.find(tab => getDirectPermission(tab.permission))
  return firstTab ? firstTab.id : null
}

watch(
  qaModuleTabs,
  () => {
    const accessibleIds = qaModuleTabs.value.map(t => t.key)
    if (accessibleIds.length > 0 && !accessibleIds.includes(activeTab.value)) {
      activeTab.value = accessibleIds[0]
    }
  },
  { immediate: true }
)

const quickQuestions = [
  '如何学习本系统？',
  '公司的考勤制度是怎样的？',
  '财务报销流程是什么？',
  '如何申请加班？'
]

const chatMessagesRef = ref(null)
const textareaRef = ref(null)
const detailMessagesRef = ref(null)
const inputValue = ref('')
const isLoading = ref(false)
const abortController = ref(null)

const activeSessionId = ref(null)
const isTemporarySession = ref(false)
const isCreatingSession = ref(false)
const sessionListCollapsed = ref(false)
const reportCollapsed = ref(false)
const qualityReportCollapsed = ref(false)
const faqCollapsed = ref(false)

const activeTooltip = ref(null)
const expandedReferences = ref({})
const collapsedGroups = ref({})  // 按文档名折叠：{ "1.docx": true }
const highlightedRef = ref(null)
const messageReferences = ref({})

const currentSources = ref([])
const currentImages = ref([])
const currentTables = ref([])
const imageBlobUrls = ref({})
const imageLoadingMap = ref({})
const responseTime = ref(0)
const showImageModal = ref(false)
const previewImageData = ref(null)
const startTime = ref(0)
const chatHistory = ref([])
const sessionMessageCount = ref({})

const typewriterTimer = ref(null)
const typewriterBuffer = ref('')
const TYPING_SPEED = 40
const TYPING_BATCH_SIZE = 3
const TYPING_MAX_BATCH_SIZE = 20
const TYPING_MIN_SPEED = 15
const TYPING_ACCELERATION_THRESHOLD = 200
let typewriterActive = false

let navigateRef = null
if (typeof window !== 'undefined') {
  // 按文档规范：点击 [引用] 链接时，查找对应引用信息并导航到文件阅读器
  window.handleReferenceClick = (element) => {
    const chunkId = element.dataset.chunkId
    if (!chunkId) return
    
    // 从 chunkId 解析文档名和索引（格式：{docName}_{chunkIndex}）
    const lastUnderscore = chunkId.lastIndexOf('_')
    const docName = lastUnderscore > 0 ? chunkId.substring(0, lastUnderscore) : chunkId
    const chunkIndex = lastUnderscore > 0 ? chunkId.substring(lastUnderscore + 1) : ''
    
    // 查找引用信息
    let refInfo = null
    for (const msgId of Object.keys(messageReferences.value)) {
      const refs = messageReferences.value[msgId]
      if (!refs) continue
      const found = refs.find(r => {
        const rChunkId = r.chunkId || r.id || ''
        return rChunkId === chunkId || rChunkId.endsWith(chunkId) || r.filename === docName
      })
      if (found) {
        refInfo = found
        break
      }
    }
    
    if (navigateRef) {
      navigateRef({
        filename: refInfo?.filename || docName,
        location: refInfo?.location || (chunkIndex ? `第${chunkIndex}页` : ''),
        context: refInfo?.context || refInfo?.preview || refInfo?.excerpt || ''
      })
    }
  }
}

const sessions = ref([])
const isLoadingSessions = ref(false)

const messages = ref([
  {
    id: 1,
    type: 'ai',
    content: '您好！我是制度文件管理学习AI智能体，有什么可以帮助您的吗？',
    timestamp: new Date().toISOString()
  }
])

const showSessionDetailModal = ref(false)
const currentSession = ref(null)
const sessionDetailMessages = ref([])  // 当前查看的会话消息列表
const isLoadingDetail = ref(false)  // 加载详情的状态
const showEditFaqModal = ref(false)
const editingFaq = ref(null)
const editQuestion = ref('')
const editAnswer = ref('')

const faqList = ref([
  { id: 1, question: '迟到多少分钟算旷工？', answer: '根据公司考勤制度第三章规定，迟到30分钟以上视为旷工。' },
  { id: 2, question: '请假需要提前多久申请？', answer: '事假需要提前1天申请，病假需要在当天上班前申请。' },
  { id: 3, question: '加班如何计算加班费？', answer: '平时加班按1.5倍工资计算，周末加班按2倍工资计算，法定节假日加班按3倍工资计算。' }
])

const qualityReport = ref({
  period: '2026年3月',
  topQuestions: [
    { id: 1, question: '迟到多少分钟算旷工？', count: 25 },
    { id: 2, question: '请假需要提前多久申请？', count: 18 },
    { id: 3, question: '加班如何计算加班费？', count: 15 }
  ],
  lowScoreAnswers: [
    { id: 1, question: '员工福利有哪些？', score: 2.5 },
    { id: 2, question: '年终奖发放标准是什么？', score: 2.8 }
  ]
})

const getCurrentSessionTitle = computed(() => {
  if (isTemporarySession.value) {
    return '新对话'
  }
  const session = sessions.value.find(s => s.id === activeSessionId.value)
  return session ? session.title : '新对话'
})

const formatDate = (dateStr) => {
  const date = new Date(dateStr)
  const now = new Date()
  const diff = now - date
  
  if (diff < 60000) {
    return '刚刚'
  } else if (diff < 3600000) {
    return Math.floor(diff / 60000) + '分钟前'
  } else if (diff < 86400000) {
    return Math.floor(diff / 3600000) + '小时前'
  } else if (diff < 604800000) {
    return Math.floor(diff / 86400000) + '天前'
  } else {
    return date.toLocaleDateString('zh-CN')
  }
}

const formatTime = (dateStr) => {
  const date = new Date(dateStr)
  return date.toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' })
}

const formatResponseTime = (ms) => {
  if (ms < 1000) {
    return `${ms}ms`
  } else if (ms < 60000) {
    return `${(ms / 1000).toFixed(1)}s`
  } else {
    return `${Math.floor(ms / 60000)}m ${Math.floor((ms % 60000) / 1000)}s`
  }
}

const previewImage = (image) => {
  console.log('🖼️ [DEBUG] 预览图片:', image)
  const imageUrl = getImageDisplayUrl(image)
  if (imageUrl) {
    previewImageData.value = { ...image, _displayUrl: imageUrl }
    showImageModal.value = true
  }
}

const downloadImage = (image) => {
  const imageUrl = getImageDisplayUrl(image)
  if (imageUrl) {
    const link = document.createElement('a')
    link.href = imageUrl
    link.download = image.id || `image-${Date.now()}`
    document.body.appendChild(link)
    link.click()
    document.body.removeChild(link)
  }
}

// 获取图片显示 URL（优先使用带认证的 blob URL，回退到原始 URL）
const getImageDisplayUrl = (image) => {
  if (!image) return ''
  const imageId = image.id || image.imageId || image.image_id
  // 优先使用 blob URL（带认证加载的缓存）
  if (imageId && imageBlobUrls.value[imageId]) {
    return imageBlobUrls.value[imageId]
  }
  // 如果有 URL 字段且 imageId 不包含文件扩展名，直接解析
  const rawUrl = image.url || image.src || ''
  if (rawUrl) {
    // 若 imageId 带扩展名（如 49d2910b148d.jpg），说明后端返回的 url 路径格式有误，跳过
    if (!imageId || !/\.\w+$/.test(String(imageId))) {
      return resolveImageUrl(rawUrl)
    }
  }
  // 没有 URL 字段或 URL 路径有误，但有 imageId，尝试 Nginx 静态资源路径
  if (imageId) {
    // 清理可能的文件扩展名
    const cleanId = String(imageId).replace(/\.\w+$/, '')
    const ext = String(imageId).includes('.') ? String(imageId).match(/\.(\w+)$/)?.[1] : 'jpg'
    return `/files/img/${cleanId}.${ext}`
  }
  return ''
}

// 解析图片URL
const safeParseImages = (raw) => {
  if (!raw) return []
  if (Array.isArray(raw)) return raw
  if (typeof raw === 'string') {
    try {
      const parsed = JSON.parse(raw)
      return Array.isArray(parsed) ? parsed : []
    } catch {
      return []
    }
  }
  return []
}

const getContentWithImages = (content, images) => {
  if (!content) return []
  const parsedImages = safeParseImages(images)
  if (!parsedImages || parsedImages.length === 0) {
    return [{ type: 'text', content }]
  }
  const paragraphs = content.split(/\n{2,}/)
  const nonEmptyParagraphs = paragraphs.filter(p => p.trim())
  if (nonEmptyParagraphs.length === 0) {
    return [{ type: 'text', content }]
  }
  const segments = []
  const imageRelevanceScores = parsedImages.map(img => {
    // 优先使用后端返回的 preview（包含图片周围文档上下文），回退到 description / alt / source
    const desc = (img.preview || img.description || img.alt || img.source || '').toLowerCase()
    return { image: img, desc, scores: [] }
  })
  nonEmptyParagraphs.forEach((para, pIdx) => {
    const paraLower = para.toLowerCase()
    imageRelevanceScores.forEach(imgInfo => {
      let score = 0
      if (imgInfo.desc) {
        const descWords = imgInfo.desc.split(/[\s,，.。;；:：、]+/).filter(w => w.length > 1)
        descWords.forEach(word => {
          if (paraLower.includes(word)) score += 3
        })
      }
      const source = (imgInfo.image.source || '').toLowerCase().replace(/\.\w+$/, '')
      if (source && paraLower.includes(source)) score += 5
      score += (pIdx + 1) / (nonEmptyParagraphs.length + 1) * 0.5
      imgInfo.scores.push({ pIdx, score })
    })
  })
  const imageAssignments = new Map()
  imageRelevanceScores.forEach(imgInfo => {
    const best = imgInfo.scores.reduce((a, b) => a.score > b.score ? a : b, { pIdx: -1, score: -1 })
    if (best.score >= 2) {
      imageAssignments.set(imgInfo.image, best.pIdx)
    } else {
      const evenIdx = Math.min(
        Math.floor((parsedImages.indexOf(imgInfo.image) + 1) * nonEmptyParagraphs.length / (parsedImages.length + 1)),
        nonEmptyParagraphs.length - 1
      )
      imageAssignments.set(imgInfo.image, evenIdx)
    }
  })
  nonEmptyParagraphs.forEach((para, pIdx) => {
    segments.push({ type: 'text', content: para })
    const imagesForPara = parsedImages.filter(img => imageAssignments.get(img) === pIdx)
    imagesForPara.forEach(img => {
      segments.push({ type: 'image', image: img })
    })
  })
  return segments
}

const resolveImageUrl = (image) => {
  if (!image) return ''

  if (typeof image === 'string') {
    if (image.startsWith('/files/') || image.startsWith('http://') || image.startsWith('https://') || image.startsWith('data:image') || image.startsWith('blob:')) {
      return image
    }
    if (image.startsWith('/images/')) {
      return `/api${image}`
    }
    if (image.startsWith('/api/')) {
      return image
    }
    return image
  }

  const url = image.url || image.src || ''
  if (!url) return ''

  if (url.startsWith('/files/') || url.startsWith('http://') || url.startsWith('https://') || url.startsWith('data:image') || url.startsWith('blob:')) {
    return url
  }
  if (url.startsWith('/images/')) {
    return `/api${url}`
  }
  if (url.startsWith('/api/')) {
    return url
  }

  return url
}

// 图片加载错误处理
const handleImageError = (event) => {
  console.error('❌ [DEBUG] 图片加载失败:', event.target.src)
  const wrapper = event.target.closest('.image-wrapper')
  if (wrapper) {
    wrapper.innerHTML = '<div class="image-error-placeholder"><span>图片加载失败</span></div>'
  }
}

// 图片加载成功处理
const handleImageLoad = (image) => {
  console.log('✅ [DEBUG] 图片加载成功:', image)
}

// 带认证的图片加载（解决 <img> 标签无法携带 Authorization 头的问题）
const loadImageWithAuth = async (image) => {
  let imageId = image.id || image.imageId
  if (!imageId || imageBlobUrls.value[imageId] || imageLoadingMap.value[imageId]) return
  // 清理可能的文件扩展名（后端返回的 id 可能带 .jpg）
  const rawId = String(imageId)
  const cleanId = rawId.replace(/\.\w+$/, '')
  imageLoadingMap.value[rawId] = true
  try {
    const response = await imageAPI.getImageData(cleanId)
    const blob = response.data
    const blobUrl = URL.createObjectURL(blob)
    imageBlobUrls.value[rawId] = blobUrl
  } catch (e) {
    console.error('❌ 加载图片失败:', rawId, e)
  } finally {
    imageLoadingMap.value[rawId] = false
  }
}

// 清理所有 blob URL
const revokeImageBlobUrls = () => {
  Object.values(imageBlobUrls.value).forEach(url => {
    URL.revokeObjectURL(url)
  })
  imageBlobUrls.value = {}
}

const startTypewriter = (aiMessageId) => {
  if (typewriterActive) return
  typewriterActive = true
  
  const processBuffer = () => {
    if (!typewriterActive) return
    
    const msgIndex = messages.value.findIndex(m => m.id === aiMessageId)
    if (msgIndex === -1) {
      stopTypewriter()
      return
    }
    
    if (typewriterBuffer.value.length > 0) {
      // 智能自适应加速：根据缓冲区剩余长度动态调整速度
      const remainingLength = typewriterBuffer.value.length
      let batchSize = TYPING_BATCH_SIZE
      let speed = TYPING_SPEED
      
      if (remainingLength > TYPING_ACCELERATION_THRESHOLD) {
        // 剩余内容较多，加速处理
        const accelerationRatio = Math.min(remainingLength / TYPING_ACCELERATION_THRESHOLD, 4)
        batchSize = Math.min(
          Math.floor(TYPING_BATCH_SIZE + accelerationRatio * 5),
          TYPING_MAX_BATCH_SIZE
        )
        speed = Math.max(
          Math.floor(TYPING_SPEED - accelerationRatio * 8),
          TYPING_MIN_SPEED
        )
      }
      
      const chunk = typewriterBuffer.value.substring(0, batchSize)
      typewriterBuffer.value = typewriterBuffer.value.substring(batchSize)
      
      messages.value[msgIndex] = {
        ...messages.value[msgIndex],
        content: (messages.value[msgIndex].content || '') + chunk,
        status: 'streaming'
      }
      throttleScrollToBottom()
      typewriterTimer.value = setTimeout(processBuffer, speed)
    } else {
      typewriterActive = false
      typewriterTimer.value = null
    }
  }
  
  processBuffer()
}

const stopTypewriter = () => {
  typewriterActive = false
  if (typewriterTimer.value) {
    clearTimeout(typewriterTimer.value)
    typewriterTimer.value = null
  }
  typewriterBuffer.value = ''
}

const finalizeMessage = (aiMessageId, result, displayContent, trimmedInput) => {
  const msgIndex = messages.value.findIndex(m => m.id === aiMessageId)
  if (msgIndex !== -1) {
    messages.value[msgIndex] = {
      ...messages.value[msgIndex],
      content: displayContent,
      isStreaming: false,
      isFinished: true,
      status: result.interrupted ? 'interrupted' : 'complete',
      isInterrupted: result.interrupted || false,
      canRetry: result.interrupted || false,
      sources: [...currentSources.value],
      images: [...currentImages.value],
      tables: [...currentTables.value],
      responseTime: responseTime.value,
      sessionId: result.sessionId || result.session_id,
      mode: result.mode,
      sections: result.sections || [],
      metadata: {
        duration_ms: result.duration_ms || responseTime.value,
        ...result.metadata
      }
    }
  }
  processSessionAndFinish(result, trimmedInput, displayContent)
}

// 从会话 API 异步加载消息图片（流式结束时 SSE 可能未携带 images）
const loadMessageImagesFromSession = async (aiMessageId, sessionId) => {
  try {
    const result = await aiAPI.getSessionDetail(sessionId, { page: 1, pageSize: 100 })
    if (result.data && Array.isArray(result.data)) {
      // 找最后一条 AI 回复消息
      const aiMessages = result.data.filter(msg => msg.role === 'assistant')
      const lastAi = aiMessages[aiMessages.length - 1]
      if (lastAi && lastAi.images) {
        const sessionImages = safeParseImages(lastAi.images)
        if (sessionImages.length > 0) {
          const msgIndex = messages.value.findIndex(m => m.id === aiMessageId)
          if (msgIndex !== -1) {
            const msg = messages.value[msgIndex]
            if (!msg.images || msg.images.length === 0) {
              messages.value[msgIndex] = { ...msg, images: sessionImages }
              sessionImages.forEach(img => loadImageWithAuth(img))
            }
          }
        }
      }
    }
  } catch (e) {
    console.warn('⚠️ 从会话加载图片失败:', e)
  }
}

const cleanSearchKeyword = (raw) => {
  if (!raw || typeof raw !== 'string') return ''
  let text = raw.trim()
  console.log('🔍 keyword 原始值:', text.substring(0, 80))
  
  // 极简清理：只去除格式标记，保留原始文本
  let cleaned = text
    .replace(/<[^>]+>/g, '')                    // 去HTML标签
    .replace(/&nbsp;/g, ' ')                   // 空格实体
    .replace(/\[ref:[^\]]+\]/g, '')            // 去引用标签
    .replace(/\[\d+\]/g, '')                   // 去编号
    .replace(/\*{1,2}/g, '')                   // 去加粗
    .replace(/\s+/g, ' ')                      // 合并空白
    .trim()
  
  // 长度限制：80字符（足够包含关键信息）
  if (cleaned.length > 80) {
    cleaned = cleaned.substring(0, 80).trim()
  }
  
  // 如果太短(<4字符)，尝试提取中文片段
  if (cleaned.length < 4) {
    const chineseMatch = text.match(/[\u4e00-\u9fa5]{2,}/g)
    if (chineseMatch && chineseMatch.length > 0) {
      cleaned = chineseMatch[0].substring(0, 30)
    }
  }
  
  console.log('✅ keyword 清洗结果:', cleaned.substring(0, 80))
  return cleaned
}

const navigateToFileReader = async (ref) => {
  let pageNum = 1
  if (ref.location) {
    const match = ref.location.match(/(\d+)/)
    if (match) pageNum = parseInt(match[1]) || 1
  }
  if (ref.rawData && ref.rawData.page) {
    pageNum = ref.rawData.page
  }

  try {
    message.loading({ content: `正在查找文件：${ref.filename}...`, key: 'navigateToReader' })

    const response = await fileAPI.getFiles({ size: 1000, current: 1 })

    if (response.data.code === 200 && response.data.data?.records) {
      const files = response.data.data.records

      const targetFile = files.find(file => {
        const fileName = file.fileName || file.file_name || ''
        return fileName === ref.filename ||
               fileName.includes(ref.filename) ||
               ref.filename.includes(fileName)
      })

      if (targetFile) {
        const fileId = targetFile.id || targetFile.fileId
        const fileTitle = targetFile.fileName || targetFile.file_name || ref.filename
        const filePath = targetFile.filePath || targetFile.file_path || ''
        const extension = filePath.substring(filePath.lastIndexOf('.')) || '.pdf'

        message.success({ content: `正在打开《${fileTitle}》第${pageNum}页...`, key: 'navigateToReader' })

        // ===== 优化后的关键词提取逻辑 =====
        let keyword = ''

        // 优先级1: ref.context（引用上下文，最可靠）
        if (ref.context) {
          keyword = ref.context.substring(0, 150)
        }

        // 优先级2: ref.rawData中的多个字段（扩展检查范围）
        else if (ref.rawData) {
          const raw = ref.rawData
          keyword = raw.content ||
                    raw.context ||
                    raw.preview ||
                    raw.excerpt ||
                    raw.query ||
                    raw.question ||
                    raw.text ||
                    ''
          if (keyword) keyword = keyword.substring(0, 150)
        }

        // 优先级3: ref.location中的文本描述（去除页码信息）
        if (!keyword && ref.location && ref.location.length > 4) {
          const locText = ref.location
            .replace(/第\d+页|page\s*\d+/gi, '')
            .replace(/P\d+/gi, '')
            .trim()

          if (locText.length >= 4) {
            keyword = locText.substring(0, 120)
          }
        }

        // 清洗关键词（提升匹配成功率）
        if (keyword) {
          keyword = cleanSearchKeyword(keyword)

          if (keyword.length < 4) keyword = ''
        }

        // ===== 新增：保存当前对话状态到sessionStorage =====
        saveNavigationState(ref)

        const currentSessionId = activeSessionId.value || ''

        router.push({
          name: 'Reader',
          query: {
            id: fileId,
            title: fileTitle,
            extension: extension,
            page: pageNum,
            from: 'qa',
            sid: currentSessionId,
            ...(keyword && { keyword })
          }
        })
      } else {
        message.warning({ content: `未找到文件"${ref.filename}"，可能已被删除或移动`, key: 'navigateToReader' })
        console.warn('未找到匹配文件:', {
          搜索的文件名: ref.filename,
          可用文件列表: files.map(f => f.fileName || f.file_name)
        })
      }
    } else {
      message.error({ content: '获取文件列表失败', key: 'navigateToReader' })
    }
  } catch (error) {
    console.error('跳转到文件阅读器失败:', error)
    message.error({ content: '跳转失败：' + (error.message || '未知错误'), key: 'navigateToReader' })
  }
}

/**
 * 保存导航前的对话状态（用于浏览器后退恢复）
 */
function saveNavigationState(ref) {
  try {
    const state = {
      scrollPosition: chatMessagesRef.value ? chatMessagesRef.value.scrollTop : 0,
      sessionId: activeSessionId.value || '',
      timestamp: Date.now(),
      refInfo: {
        filename: ref.filename || '',
        context: ref.context || '',
        location: ref.location || ''
      }
    }

    sessionStorage.setItem('qa_navigation_state', JSON.stringify(state))

    console.log('[QA-NAV] 💾 已保存对话导航状态:', {
      滚动位置: `${state.scrollPosition}px`,
      会话ID: state.sessionId,
      引用文件: state.refInfo.filename
    })
  } catch (err) {
    console.warn('[QA-NAV] 保存导航状态失败:', err)
  }
}

/**
 * 恢复之前保存的对话状态（从文件阅读器返回时调用）
 */
async function restoreNavigationState() {
  try {
    const savedStateStr = sessionStorage.getItem('qa_navigation_state')

    if (!savedStateStr) {
      console.log('[QA-NAV] 无保存的导航状态')
      return false
    }

    const state = JSON.parse(savedStateStr)

    // 检查是否过期（超过30分钟则不恢复）
    const ageMinutes = (Date.now() - state.timestamp) / (1000 * 60)
    if (ageMinutes > 30) {
      console.log(`[QA-NAV] 导航状态已过期 (${ageMinutes.toFixed(1)}分钟前)，清除`)
      sessionStorage.removeItem('qa_navigation_state')
      return false
    }

    console.log('[QA-NAV] 🔄 恢复对话导航状态:', {
      滚动位置: `${state.scrollPosition}px`,
      会话ID: state.sessionId,
      状态年龄: `${ageMinutes.toFixed(1)}分钟`
    })

    // 恢复会话（如果需要切换）
    if (state.sessionId && state.sessionId !== activeSessionId.value) {
      console.log(`[QA-NAV] 切换回会话: ${state.sessionId}`)
      // 这里可以根据需要激活对应的会话
    }

    // 等待DOM更新后恢复滚动位置
    await nextTick()
    await new Promise(resolve => setTimeout(resolve, 100))  // 额外等待渲染完成

    if (chatMessagesRef.value && state.scrollPosition > 0) {
      // 平滑滚动到之前的位置
      chatMessagesRef.value.scrollTo({
        top: state.scrollPosition,
        behavior: 'smooth'
      })

      console.log(`[QA-NAV] ✅ 已恢复滚动位置: ${state.scrollPosition}px`)

      // 高亮提示（可选：短暂显示一个toast）
      message.info({
        content: `已返回之前的对话位置（《${state.refInfo.filename}》引用处）`,
        duration: 2,
        key: 'navRestore'
      })
    }

    // 清除已使用的状态
    sessionStorage.removeItem('qa_navigation_state')

    return true
  } catch (err) {
    console.error('[QA-NAV] 恢复导航状态失败:', err)
    sessionStorage.removeItem('qa_navigation_state')
    return false
  }
}

navigateRef = navigateToFileReader

const processSessionAndFinish = (result, trimmedInput, displayContent) => {
  scrollToBottom()
  
  if (isCreatingSession.value && result.sessionId) {
    const newSessionId = Date.now()
    const newSession = {
      id: newSessionId,
      sessionId: result.sessionId,
      title: trimmedInput.length > 20 ? trimmedInput.substring(0, 20) + '...' : trimmedInput,
      createdAt: new Date().toISOString(),
      messagesData: []
    }
    
    sessions.value = [newSession, ...sessions.value]
    activeSessionId.value = newSessionId
    isCreatingSession.value = false
    isTemporarySession.value = false
  } else if (result.sessionId) {
    const sessionIndex = sessions.value.findIndex(s => s.id === activeSessionId.value)
    if (sessionIndex !== -1) {
      const currentSession = sessions.value[sessionIndex]
      if (!currentSession.sessionId) {
        sessions.value[sessionIndex] = {
          ...currentSession,
          sessionId: result.sessionId,
          title: trimmedInput.length > 20 ? trimmedInput.substring(0, 20) + '...' : trimmedInput
        }
      }
    }
  }
  
  if (displayContent && displayContent !== '（空回复）') {
    chatHistory.value.push({
      role: 'assistant',
      content: displayContent
    })
  }
  
  abortController.value = null
}

// ✨ 内容质量评分函数：识别引用原文 vs 引导语
const scoreContextQuality = (text) => {
  if (!text || text.length < 2) return -100
  
  let score = 0
  
  // ✅ 加分项：引用原文特征
  
  // 包含书名号《...》（通常是文件名/制度名称）
  if (/[《「][^》」]+[》」]/.test(text)) {
    score += 30
  }
  
  // 包含条款编号：（一）（二）（三）... 或 第X条
  if ( /[（(][一二三四五六七八九十\d][）)]|第\d+条/.test(text) ) {
    score += 20
  }
  
  // 包含实质性动词
  const actionVerbs = ['要', '应当', '需要', '落实', '执行', '明确', '制定', '规定', '按照', '遵守']
  actionVerbs.forEach(verb => {
    if (text.includes(verb)) {
      score += 10
    }
  })
  
  // 文本长度适中（15-80字符为最佳范围）
  if (text.length >= 15 && text.length <= 80) {
    score += 15
  } else if (text.length > 80 && text.length <= 120) {
    score += 10
  } else if (text.length > 5 && text.length < 15) {
    score += 5
  }
  
  // ❌ 扣分项：引导语特征
  
  // 以过渡性词汇开头（这些词表明是引导语而非引用原文）
  const transitionPrefixes = [
    '具体如下', '具体包括', '包括以下', '主要内容有',
    '相关规定', '根据规定', '按照要求', '应当遵守',
    '需要落实', '要认真', '明确规定', '详细说明',
    '原文如下', '参考内容', '原文为', '详见',
    '相关内容在', '参考资料中明确提及', '根据资料',
    '在参考资料中', '明确提及'
  ]
  
  transitionPrefixes.forEach(prefix => {
    if (text.startsWith(prefix) || text.includes(prefix)) {
      score -= 25
    }
  })
  
  // 包含解释性短语（非实质性内容）
  const explanatoryPhrases = [
    '相关内容', '明确提及', '具体如下', '包括以下',
    '主要内容', '参考资料', '根据查询'
  ]
  
  explanatoryPhrases.forEach(phrase => {
    if (text.includes(phrase) && !/[《「][^》」]+[》」]/.test(text)) {
      score -= 15
    }
  })
  
  // 文本太短且无实质内容
  if (text.length < 8 && !/[《「][^》」]+[》」]/.test(text)) {
    score -= 20
  }
  
  return score
}

// ✨ 极简引用文本提取器（遵循KISS原则，参考ChatGPT/Claude方案）
const extractBestContext = (formatted, matchIndex, matchLength) => {
  
  // 核心思路：直接提取[ref:]标签前紧邻的原始文本（不做复杂分析）
  // 主流产品都是这样做的：简单、直接、可靠
  
  const beforeEnd = matchIndex
  const beforeStart = Math.max(0, beforeEnd - 80)  // 向前取80字符
  
  let context = formatted.substring(beforeStart, beforeEnd)
    .replace(/<br>/g, ' ')
    .replace(/<[^>]+>/g, '')           // 去HTML标签
    .replace(/&nbsp;/g, ' ')           // 空格实体
    .replace(/\s+/g, ' ')              // 合并空白
    .trim()
  
  // 如果前置内容太短(<10字符)，尝试取后置内容
  if (context.length < 10) {
    const afterStart = matchIndex + matchLength
    const afterEnd = Math.min(formatted.length, afterStart + 80)
    
    context = formatted.substring(afterStart, afterEnd)
      .replace(/<br>/g, ' ')
      .replace(/<[^>]+>/g, '')
      .replace(/&nbsp;/g, ' ')
      .replace(/\s+/g, ' ')
      .trim()
  }
  
  // 长度限制：保留60字符（足够包含一个完整短句）
  if (context.length > 60) {
    context = context.substring(0, 60).trim()
  }
  
  console.log(`📝 极简提取: "${context}"`)
  
  return context || '引用内容'
}

const formatMessage = (content) => {
  if (!content) return ''
  
  let formatted = content.replace(/\n/g, '<br>')
  
  // 按文档规范：将 [ref:chunk_id] 替换为可点击的引用链接
  // chunk_id 格式为 {docName}_{chunkIndex}，如 "文档名_86"
  formatted = formatted.replace(
    /\[ref:([^\]]+)\]/g,
    (match, chunkId) => {
      return `<a class="ref-link" data-chunk-id="${chunkId.replace(/"/g, '&quot;')}" onclick="window.handleReferenceClick(this)">[引用]</a>`
    }
  )
  
  formatted = formatted.replace(/```([\s\S]*?)```/g, '<pre class="code-block">$1</pre>')
  formatted = formatted.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
  
  // 将 URL 转换为可点击链接
  formatted = formatted.replace(
    /(https?:\/\/[^\s<>"')）]+)/g,
    '<a href="$1" target="_blank" rel="noopener noreferrer">$1</a>'
  )
  
  return formatted
}

const parseReferencesFromContent = (content) => {
  if (!content) return []
  
  const references = []
  const regex = /\[ref:([^\]]+)\]/g
  let match
  
  while ((match = regex.exec(content)) !== null) {
    const chunkId = match[1].trim()
    // chunk_id 格式为 {docName}_{chunkIndex}，使用 lastIndexOf 正确分割
    const lastUnderscore = chunkId.lastIndexOf('_')
    const filename = lastUnderscore > 0 ? chunkId.substring(0, lastUnderscore) : chunkId
    const location = lastUnderscore > 0 ? chunkId.substring(lastUnderscore + 1) : ''
    
    // ✨ 智能提取引用上下文
    const matchIndex = match.index
    const context = extractBestContext(content, matchIndex, match[0].length)
    
    references.push({
      id: `ref_${references.length + 1}`,
      chunkId: chunkId,
      filename: filename || '未知文档',
      location: location ? `第${location}页` : '',
      context: context.trim(),
      fullRef: match[0]
    })
  }
  
  return references
}

const toggleReferences = (messageId) => {
  expandedReferences.value[messageId] = !expandedReferences.value[messageId]
}

const getTooltipPosition = () => {
  if (!activeTooltip.value?.element) {
    return {}
  }
  
  const rect = activeTooltip.value.element.getBoundingClientRect()
  
  return {
    position: 'fixed',
    left: rect.right + 10 + 'px',
    top: rect.top + 'px',
    zIndex: 1000
  }
}

const getMessageReferences = (messageId) => {
  // 1. 优先从 messageReferences 存储中获取（由 onSources SSE 事件填充）
  if (messageReferences.value[messageId] && messageReferences.value[messageId].length > 0) {
    return messageReferences.value[messageId]
  }
  
  const msg = messages.value.find(m => m.id === messageId)
  
  // 2. 从消息内容中解析 [ref:xxx] 标记
  if (msg?.content) {
    const parsedRefs = parseReferencesFromContent(msg.content)
    if (parsedRefs.length > 0) {
      return parsedRefs
    }
  }
  
  // 3. 后备方案：从消息的 sources 字段转换（由 onFinish/result.sources 填充）
  if (msg?.sources && msg.sources.length > 0) {
    return msg.sources.map((source, idx) => ({
      id: source.id || source.chunkId || source.chunk_id || `ref_${messageId}_${idx}`,
      chunkId: source.chunkId || source.chunk_id || '',
      filename: source.doc_name || source.source || source.filename || source.title || source.name || '未知文档',
      location: source.page ? `第${source.page}页` : (source.section || source.location || source.table || ''),
      context: source.excerpt || source.content?.stem || source.content_stem || source.preview || source.context || (typeof source.content === 'string' ? source.content : '') || '',
      preview: source.preview || '',
      score: source.score || source.relevance_score || null
    }))
  }
  
  return []
}

const getGroupedReferences = (messageId) => {
  const refs = getMessageReferences(messageId)
  const groups = {}
  refs.forEach(ref => {
    const key = ref.filename || '未知文档'
    if (!groups[key]) {
      groups[key] = { filename: key, items: [] }
    }
    groups[key].items.push(ref)
  })
  return Object.values(groups)
}

// 切换引用分组的折叠状态
const toggleGroupCollapse = (filename) => {
  collapsedGroups.value[filename] = !collapsedGroups.value[filename]
}

const scrollToBottom = async () => {
  await nextTick()
  if (chatMessagesRef.value) {
    chatMessagesRef.value.scrollTop = chatMessagesRef.value.scrollHeight
  }
}

let lastScrollTime = 0
const throttleScrollToBottom = () => {
  const now = Date.now()
  if (now - lastScrollTime >= 80) {
    scrollToBottom()
    lastScrollTime = now
  }
}

const updateMessageContent = (messageId, newContent) => {
  const msgIndex = messages.value.findIndex(m => m.id === messageId)
  if (msgIndex !== -1) {
    messages.value[msgIndex] = {
      ...messages.value[msgIndex],
      content: newContent
    }
  }
}

const autoResize = () => {
  if (textareaRef.value) {
    textareaRef.value.style.height = 'auto'
    textareaRef.value.style.height = Math.min(textareaRef.value.scrollHeight, 200) + 'px'
  }
}

const handleKeydown = (e) => {
  if (e.key === 'Enter' && !e.shiftKey) {
    e.preventDefault()
    handleSend()
  }
}

const handleSend = async () => {
  const trimmedInput = inputValue.value.trim()
  
  if (!trimmedInput || isLoading.value) return

  if (isTemporarySession.value) {
    activeSessionId.value = `pending_${Date.now()}`
    isCreatingSession.value = true
  }
  
  const userMessage = {
    id: Date.now(),
    type: 'user',
    content: trimmedInput,
    timestamp: new Date().toISOString()
  }
  messages.value = [...messages.value, userMessage]
  
  inputValue.value = ''
  isLoading.value = true
  
  currentSources.value = []
  currentImages.value = []
  currentTables.value = []
  responseTime.value = 0
  startTime.value = Date.now()
  
  if (textareaRef.value) {
    textareaRef.value.style.height = 'auto'
  }
  
  let timeoutTimer = setTimeout(() => {
    if (isLoading.value) {
      handleCancelRequest()
      message.warning('请求超时，已自动停止')
    }
  }, 300000)
  
  const aiMessageId = Date.now() + 1
  const aiMessagePlaceholder = {
    id: aiMessageId,
    type: 'ai',
    content: '',
    timestamp: new Date().toISOString(),
    isStreaming: true,
    status: 'thinking',
    sources: [],
    images: [],
    tables: [],
    responseTime: 0
  }
  messages.value = [...messages.value, aiMessagePlaceholder]
  
  await scrollToBottom()
  
  abortController.value = new AbortController()

  const currentSessionId = activeSessionId.value && activeSessionId.value !== 'default' ? sessions.value.find(s => s.id === activeSessionId.value)?.sessionId : null
  let accumulatedContent = ''

  await aiAPI.sendMessage(
    {
      message: trimmedInput,
      id: `req_${Date.now()}`,
      session_id: currentSessionId || undefined,
      signal: abortController.value.signal
    },
    {
      onStart: (data) => {
        updateMessageContent(aiMessageId, '')
        const msgIndex = messages.value.findIndex(m => m.id === aiMessageId)
        if (msgIndex !== -1) {
          messages.value[msgIndex] = { ...messages.value[msgIndex], status: 'thinking' }
        }
        scrollToBottom()
      },
      
      onThinking: (data) => {
        const msgIndex = messages.value.findIndex(m => m.id === aiMessageId)
        if (msgIndex !== -1) {
          messages.value[msgIndex] = { 
            ...messages.value[msgIndex], 
            status: 'thinking',
            thinkingContent: data.content || '正在思考...'
          }
        }
      },

      onSearching: (data) => {
        const msgIndex = messages.value.findIndex(m => m.id === aiMessageId)
        if (msgIndex !== -1) {
          messages.value[msgIndex] = { 
            ...messages.value[msgIndex], 
            status: 'searching',
            searchingCollection: data.collection || ''
          }
        }
      },
      
      onSources: (data) => {
        const existingIds = currentSources.value.map(s => s.id || s.filename)
        const newSources = (data.sources || []).filter(s => !existingIds.includes(s.id || s.filename))
        currentSources.value = [...currentSources.value, ...newSources]
        
        if (!messageReferences.value[aiMessageId]) {
          messageReferences.value[aiMessageId] = []
        }
        
        newSources.forEach(source => {
          const refItem = {
            id: source.id || source.chunkId || source.chunk_id || `ref_${aiMessageId}_${messageReferences.value[aiMessageId].length + 1}`,
            chunkId: source.chunkId || source.chunk_id || '',
            // 文档字段映射：docName → source / doc_name
            filename: source.doc_name || source.source || source.filename || source.title || source.name || '未知文档',
            // 文档字段映射：page → page, section → section
            location: source.page ? `第${source.page}页` : (source.section || source.location || source.table || ''),
            // 文档字段映射：excerpt → content.stem, preview 由后端自动填充
            context: source.excerpt || source.content?.stem || source.content_stem || source.preview || source.context || (typeof source.content === 'string' ? source.content : '') || '',
            preview: source.preview || '',
            score: source.score || source.relevance_score || null,
            rawData: source
          }
          
          const exists = messageReferences.value[aiMessageId].some(r => r.id === refItem.id)
          if (!exists) {
            messageReferences.value[aiMessageId].push(refItem)
          }
        })
        
        const msgIndex = messages.value.findIndex(m => m.id === aiMessageId)
        if (msgIndex !== -1) {
          messages.value[msgIndex] = {
            ...messages.value[msgIndex],
            sources: [...currentSources.value]
          }
        }
      },
      
      onChunk: (data) => {
        if (!data.content || data.content.length === 0) return
        
        accumulatedContent += data.content
        typewriterBuffer.value += data.content
        
        if (!typewriterActive) {
          startTypewriter(aiMessageId)
        }
      },
      
      onFinish: (result) => {
        isLoading.value = false
        clearTimeout(timeoutTimer)
        
        responseTime.value = result.duration || (Date.now() - startTime.value)
        
        console.log('🖼️ [DEBUG] onFinish 收到的 result:', result)
        console.log('🖼️ [DEBUG] result.images:', result.images)
        
        // 优先使用 result.answer（后端完整回答），回退到 accumulatedContent（前端拼接的chunks）
        const displayContent = result.answer || accumulatedContent || '（空回复）'
        
        // 收到 finish 事件后立即停止打字机，用 result.answer 作为最终内容
        // 避免 accumulatedContent 与 result.answer 不一致导致内容跳变
        stopTypewriter()
        
        // 优先使用 citations（finish 事件中的权威引用数据，已写入数据库）
        // 收到 citations 后替换 onSources 填充的临时数据，确保引用来源与刷新后一致
        if (result.citations && result.citations.length > 0) {
          // 保存 onSources 已填充的引用数据（含 context），用于后备
          const existingRefs = (messageReferences.value[aiMessageId] || []).reduce((map, ref) => {
            map[ref.chunkId] = ref
            return map
          }, {})
          messageReferences.value[aiMessageId] = result.citations.map((citation, idx) => {
            const chunkId = citation.chunk_id || citation.chunkId || ''
            const existing = existingRefs[chunkId]
            // 优先使用 citation 中的 excerpt/content.stem/preview
            // 若 citation 缺少 context 数据，则复用 onSources 阶段已填充的 context
            let context = citation.excerpt || citation.content?.stem || citation.preview || ''
            if (!context && existing?.context) {
              context = existing.context
            }
            return {
              id: chunkId || `ref_${aiMessageId}_${idx}`,
              chunkId,
              filename: citation.source || citation.doc_name || '未知文档',
              location: citation.page ? `第${citation.page}页` : (citation.section || ''),
              context,
              preview: citation.preview || existing?.preview || '',
              score: citation.score || null,
              rawData: citation
            }
          })
        }
        // 后备方案：使用 sources（SSE 事件中的候选数据，未写入数据库）
        else if (result.sources && result.sources.length > 0) {
          if (!messageReferences.value[aiMessageId] || messageReferences.value[aiMessageId].length === 0) {
            messageReferences.value[aiMessageId] = result.sources.map((source, idx) => ({
              id: source.id || source.chunkId || source.chunk_id || `ref_${aiMessageId}_${idx}`,
              chunkId: source.chunkId || source.chunk_id || '',
              filename: source.doc_name || source.source || source.filename || source.title || source.name || '未知文档',
              location: source.page ? `第${source.page}页` : (source.section || source.location || source.table || ''),
              context: source.excerpt || source.content?.stem || source.content_stem || source.preview || source.context || (typeof source.content === 'string' ? source.content : '') || '',
              preview: source.preview || '',
              score: source.score || source.relevance_score || null
            }))
          }
        }
        const parsedImages = safeParseImages(result.images)
        // 将 citations 中 chunkType='image' 的 preview 补充到图片数据中，用于图片定位
        if (parsedImages.length > 0 && result.citations && result.citations.length > 0) {
          const imageCitations = result.citations.filter(c => c.chunk_type === 'image' && c.preview)
          if (imageCitations.length > 0) {
            parsedImages.forEach(img => {
              const imgId = img.image_id || img.imageId || ''
              const imgSource = img.source || ''
              const imgPage = img.page ? String(img.page) : ''
              for (const cit of imageCitations) {
                const citSource = cit.source || ''
                const citSourceBase = citSource.replace(/\.\w+$/, '')
                const citPage = cit.page ? String(cit.page) : ''
                if ((imgId && cit.chunk_id === imgId) ||
                    (imgSource && (imgSource === citSource || imgSource.replace(/\.\w+$/, '') === citSourceBase)) ||
                    (imgPage && citPage && imgPage === citPage && imgSource && imgSource.replace(/\.\w+$/, '') === citSourceBase)) {
                  img.preview = cit.preview
                  break
                }
              }
            })
          }
        }
        if (parsedImages.length > 0) {
          currentImages.value = parsedImages
          console.log('🖼️ [DEBUG] 设置 currentImages:', currentImages.value)
          parsedImages.forEach(img => loadImageWithAuth(img))
        }
        if (result.tables && result.tables.length > 0) {
          currentTables.value = result.tables
        }
        
        // 立即用 result.answer 设置最终内容，不再等待打字机
        finalizeMessage(aiMessageId, result, displayContent, trimmedInput)
        
        // 如果流完成时没有收到图片，从会话 API 异步加载
        if ((!result.images || safeParseImages(result.images).length === 0) && result.sessionId) {
          loadMessageImagesFromSession(aiMessageId, result.sessionId)
        }
      },
      
      onError: (error) => {
        stopTypewriter()
        isLoading.value = false
        accumulatedContent = ''
        clearTimeout(timeoutTimer)

        const errorMsg = error || '未知错误'

        if (errorMsg.includes('请求已取消') || errorMsg.includes('已停止生成')) {
          const lastAiMsg = [...messages.value].reverse().find(m => m.type === 'ai' && m.id === aiMessageId)
          if (lastAiMsg) {
            const existingContent = lastAiMsg.content || ''
            if (existingContent && existingContent.length > 0) {
              const msgIndex = messages.value.findIndex(m => m.id === aiMessageId)
              if (msgIndex !== -1) {
                messages.value[msgIndex] = {
                  ...messages.value[msgIndex],
                  isStreaming: false,
                  isFinished: true,
                  status: 'stopped',
                  images: currentImages.value.length > 0 ? [...currentImages.value] : messages.value[msgIndex].images
                }
              }
            } else {
              updateMessageContent(aiMessageId, '（已停止生成）')
            }
          }
          return
        }

        if (errorMsg.includes('生成中断') || errorMsg.includes('已收到') || errorMsg.includes('INCOMPLETE_CHUNKED_ENCODING')) {
          const lastAiMsg = [...messages.value].reverse().find(m => m.type === 'ai' && m.id === aiMessageId)
          if (lastAiMsg && lastAiMsg.content && lastAiMsg.content.length > 0) {
            const msgIndex = messages.value.findIndex(m => m.id === aiMessageId)
            if (msgIndex !== -1) {
              messages.value[msgIndex] = {
                ...messages.value[msgIndex],
                isStreaming: false,
                isFinished: true,
                status: 'interrupted',
                isInterrupted: true,
                canRetry: true,
                images: currentImages.value.length > 0 ? [...currentImages.value] : messages.value[msgIndex].images
              }
            }
            message.warning('AI回答生成中断，已显示部分内容，可点击重新生成', 5)
          } else {
            const msgIndex = messages.value.findIndex(m => m.id === aiMessageId)
            if (msgIndex !== -1) {
              messages.value[msgIndex] = {
                ...messages.value[msgIndex],
                isError: true,
                isStreaming: false,
                status: 'error',
                errorMessage: errorMsg,
                canRetry: true
              }
            }
            message.error('AI回答生成中断，请重试', 5)
          }
          return
        }

        let userFriendlyMsg = '抱歉，回答生成失败'
        let displayError = error
        let shouldRetry = false

        if (errorMsg.includes('network') || errorMsg.includes('网络') || errorMsg.includes('连接')) {
          userFriendlyMsg = '网络连接中断'
          displayError = `${errorMsg}\n\n可能原因：\n• AI/RAG服务响应中断\n• 网络波动\n• 代理超时`
          shouldRetry = true
        } else if (errorMsg.includes('timeout') || errorMsg.includes('超时')) {
          userFriendlyMsg = 'AI服务响应超时'
          displayError = `${errorMsg}\n\n建议：简化问题内容后重试`
          shouldRetry = true
        } else if (errorMsg.includes('401') || errorMsg.includes('登录')) {
          userFriendlyMsg = '登录已过期'
          displayError = errorMsg
        } else if (errorMsg.includes('500') || errorMsg.includes('服务器')) {
          userFriendlyMsg = 'AI服务内部错误'
          displayError = `${errorMsg}\n\n建议：稍后重试或联系技术支持`
          shouldRetry = true
        }

        const msgIndex = messages.value.findIndex(m => m.id === aiMessageId)
        if (msgIndex !== -1) {
          messages.value[msgIndex] = {
            ...messages.value[msgIndex],
            content: userFriendlyMsg + '。' + (shouldRetry ? '可点击"重新发送"重试。' : ''),
            isError: true,
            isStreaming: false,
            status: 'error',
            errorMessage: displayError,
            canRetry: shouldRetry
          }
        }

        message.error(userFriendlyMsg, 5)
      }
    }
  )
}

const updateSessionTitle = (content) => {
  const sessionIndex = sessions.value.findIndex(s => s.id === activeSessionId.value)
  if (sessionIndex !== -1) {
    const shortTitle = content.length > 20 ? content.substring(0, 20) + '...' : content
    const updatedSessions = [...sessions.value]
    updatedSessions[sessionIndex] = {
      ...updatedSessions[sessionIndex],
      title: shortTitle
    }
    sessions.value = updatedSessions
  }
}

const handleCancelRequest = () => {
  if (abortController.value) {
    // 按文档规范：通知后端清理资源
    const currentSession = activeSessionId.value ? sessions.value.find(s => s.id === activeSessionId.value) : null
    if (currentSession?.sessionId) {
      aiAPI.stopStream(currentSession.sessionId).catch(() => {})
    }
    
    abortController.value.abort()
    stopTypewriter()
    isLoading.value = false
    
    const lastAiMsg = [...messages.value].reverse().find(m => m.type === 'ai' && m.isStreaming)
    if (lastAiMsg) {
      const msgIndex = messages.value.findIndex(m => m.id === lastAiMsg.id)
      if (msgIndex !== -1) {
        messages.value[msgIndex] = {
          ...messages.value[msgIndex],
          status: lastAiMsg.content ? 'stopped' : 'cancelled',
          isStreaming: false,
          isFinished: true,
          content: lastAiMsg.content || '（已停止生成）'
        }
      }
    }
    
    message.info('已停止生成')
  }
}

const copyMessage = async (content) => {
  try {
    let textToCopy = content || ''
    if (textToCopy && textToCopy !== '（空回复）') {
      const tempDiv = document.createElement('div')
      tempDiv.innerHTML = textToCopy
      textToCopy = tempDiv.textContent || tempDiv.innerText || textToCopy

      if (navigator.clipboard && window.isSecureContext) {
        await navigator.clipboard.writeText(textToCopy)
      } else {
        const textarea = document.createElement('textarea')
        textarea.value = textToCopy
        textarea.style.position = 'fixed'
        textarea.style.left = '-9999px'
        textarea.style.top = '-9999px'
        document.body.appendChild(textarea)
        textarea.select()
        document.execCommand('copy')
        document.body.removeChild(textarea)
      }
      message.success('已复制到剪贴板')
    } else {
      message.warning('没有可复制的内容')
    }
  } catch (e) {
    console.error('复制失败:', e.message)
    message.error('复制失败')
  }
}

const regenerateMessage = (index) => {
  if (isLoading.value) {
    message.warning('AI正在回答中，请稍后')
    return
  }
  
  // 查找当前AI消息之前的用户问题
  const aiMessage = messages.value[index]
  if (!aiMessage || aiMessage.type !== 'ai') {
    message.warning('无法找到对应的回答')
    return
  }
  
  // 查找该AI消息对应的用户提问
  const userMessageIndex = index - 1
  if (userMessageIndex < 0 || messages.value[userMessageIndex].type !== 'user') {
    message.warning('无法找到对应的用户提问')
    return
  }
  
  const userMessage = messages.value[userMessageIndex]
  console.log('🔄 [DEBUG] 重新生成回答 | 用户问题:', userMessage.content)
  
  // 移除当前的AI消息（保留用户消息）
  messages.value = messages.value.filter((_, i) => i !== index)
  
  messageReferences.value = {}
  
  // 直接使用用户问题调用API重新生成
  inputValue.value = userMessage.content
  handleSend()
}

const likeMessage = (index) => {
  message.success('感谢您的反馈！')
}

const sendQuickQuestion = (question) => {
  inputValue.value = question
  handleSend()
}

const handleSessionClick = async (session) => {
  if (isLoading.value) {
    handleCancelRequest()
  }
  
  activeSessionId.value = session.id
  isTemporarySession.value = false
  
  if (session.sessionId) {
    await loadSessionDetailFromAPI(session.sessionId)
  } else {
    messages.value = session.messagesData || []
    chatHistory.value = (session.messagesData || []).map(msg => ({
      role: msg.type === 'user' ? 'user' : 'assistant',
      content: msg.content
    }))
  }
}

const handleNewSession = () => {
  console.log('🆕 [DEBUG] handleNewSession 被调用')
  console.log('   - 当前 isLoading:', isLoading.value)
  console.log('   - 当前 sessions 数量:', sessions.value.length)
  console.log('   - 调用堆栈:', new Error().stack?.split('\n')[2]?.trim())
  
  if (isLoading.value) {
    handleCancelRequest()
  }
  
  activeSessionId.value = null
  isTemporarySession.value = true
  
  const welcomeMessage = {
    id: Date.now() + 1,
    type: 'ai',
    content: '您好！我是制度文件管理学习AI智能体，有什么可以帮助您的吗？',
    timestamp: new Date().toISOString(),
    isFinished: true
  }
  
  messages.value = [welcomeMessage]
  chatHistory.value = [{
    role: 'assistant',
    content: welcomeMessage.content
  }]
  
  inputValue.value = ''
  isLoading.value = false
  revokeImageBlobUrls()
  
  console.log('✅ [DEBUG] handleNewSession 完成')
  console.log('   - activeSessionId:', activeSessionId.value)
  console.log('   - isTemporarySession:', isTemporarySession.value)
  console.log('   - sessions 数量:', sessions.value.length)
}

const handleDeleteSession = async (session) => {
  console.log('🗑️ [DEBUG] handleDeleteSession 被调用')
  console.log('   - 要删除的会话:', JSON.stringify({
    id: session.id,
    sessionId: session.sessionId,
    title: session.title
  }))
  console.log('   - 当前 sessions 数量:', sessions.value.length)
  console.log('   - 当前 sessions 列表:', sessions.value.map(s => ({id: s.id, title: s.title, sessionId: s.sessionId})))
  console.log('   - activeSessionId:', activeSessionId.value)
  
  // 移除"至少保留一个会话"的限制，允许删除所有会话
  
  // 显示确认对话框
  Modal.confirm({
    title: '确认删除',
    content: `确定要删除对话"${session.title}"吗？此操作不可恢复。`,
    okText: '确认删除',
    cancelText: '取消',
    okType: 'danger',
    onOk: async () => {
      try {
        console.log('🗑️ [DEBUG] 用户确认删除')
        console.log('   - session.sessionId 存在吗?', !!session.sessionId)
        console.log('   - session.sessionId 值:', session.sessionId)
        
        // 调用删除API（如果有sessionId则调用后端API）
        if (session.sessionId) {
          console.log('🌐 [DEBUG] 正在调用 deleteSession API...', session.sessionId)
          const deleteResult = await aiAPI.deleteSession(session.sessionId)
          console.log('✅ [DEBUG] 后端会话删除成功，返回结果:', deleteResult)
        } else {
          console.log('⚠️ [DEBUG] 该会话没有 sessionId，跳过API调用（可能是本地临时会话）')
        }
        
        // 从本地列表中移除
        const beforeLength = sessions.value.length
        const updatedSessions = sessions.value.filter(s => s.id !== session.id)
        sessions.value = updatedSessions
        
        console.log('💾 [DEBUG] 从本地列表移除完成')
        console.log('   - 移除前数量:', beforeLength)
        console.log('   - 移除后数量:', updatedSessions.length)
        console.log('   - 当前 sessions:', updatedSessions.map(s => ({id: s.id, title: s.title})))
        
        // 如果删除的是当前激活的会话
        if (activeSessionId.value === session.id) {
          if (updatedSessions.length > 0) {
            // 还有其他会话，切换到第一个
            console.log('🔄 [DEBUG] 切换到其他会话:', updatedSessions[0].id)
            activeSessionId.value = updatedSessions[0].id
            isTemporarySession.value = false
            
            if (updatedSessions[0].sessionId) {
              await loadSessionDetailFromAPI(updatedSessions[0].sessionId)
            } else {
              messages.value = updatedSessions[0].messagesData || []
              chatHistory.value = (updatedSessions[0].messagesData || []).map(msg => ({
                role: msg.type === 'user' ? 'user' : 'assistant',
                content: msg.content
              }))
            }
          } else {
            // 没有其他会话了，显示空白页面（类似新建对话），但不创建记录
            console.log('📝 [DEBUG] 所有会话已删除，显示空白页面（不创建记录）')
            
            activeSessionId.value = null
            isTemporarySession.value = true
            
            const welcomeMessage = {
              id: Date.now() + 1,
              type: 'ai',
              content: '您好！我是制度文件管理学习AI智能体，有什么可以帮助您的吗？',
              timestamp: new Date().toISOString(),
              isFinished: true
            }
            
            messages.value = [welcomeMessage]
            chatHistory.value = [{
              role: 'assistant',
              content: welcomeMessage.content
            }]
            
            inputValue.value = ''
            isLoading.value = false
            
            console.log('✅ [DEBUG] 空白页面设置完成')
            console.log('   - activeSessionId:', activeSessionId.value)
            console.log('   - isTemporarySession:', isTemporarySession.value)
            console.log('   - sessions 数量:', sessions.value.length)
          }
        }
        
        message.success('✅ 会话已删除')
        console.log('✅ [DEBUG] 删除流程完成')
        
      } catch (error) {
        console.error('❌ [DEBUG] 删除会话失败:', error)
        message.error('删除会话失败: ' + (error.message || '网络错误'))
        
        // 即使API失败，也从本地移除
        const updatedSessions = sessions.value.filter(s => s.id !== session.id)
        sessions.value = updatedSessions
        
        if (activeSessionId.value === session.id && updatedSessions.length === 0) {
          // API失败且没有其他会话，也显示空白页面
          activeSessionId.value = null
          isTemporarySession.value = true
          
          const welcomeMessage = {
            id: Date.now() + 1,
            type: 'ai',
            content: '您好！我是制度文件管理学习AI智能体，有什么可以帮助您的吗？',
            timestamp: new Date().toISOString(),
            isFinished: true
          }
          
          messages.value = [welcomeMessage]
          chatHistory.value = [{
            role: 'assistant',
            content: welcomeMessage.content
          }]
          
          inputValue.value = ''
          isLoading.value = false
        }
      }
    }
  })
}

const clearChat = () => {
  if (messages.value.length === 0) {
    return
  }
  
  messages.value = [
    {
      id: Date.now(),
      type: 'ai',
      content: '您好！我是制度文件管理学习AI智能体，有什么可以帮助您的吗？',
      timestamp: new Date().toISOString()
    }
  ]
  chatHistory.value = []
  saveSessionsToStorage()
  message.success('已清空对话')
}

const viewSessionDetail = async (session) => {
  console.log('👁️ 查看会话详情:', session)
  
  // 设置当前会话
  currentSession.value = session
  showSessionDetailModal.value = true
  isLoadingDetail.value = true
  sessionDetailMessages.value = []
  
  // 如果有sessionId，从API加载真实数据
  if (session.sessionId) {
    try {
      console.log('📝 正在加载会话详情:', session.sessionId)
      const result = await aiAPI.getSessionDetail(session.sessionId, { page: 1, pageSize: 100 })
      
      if (result.data && Array.isArray(result.data)) {
        console.log('✅ 获取到', result.data.length, '条消息')
        
        // 使用与loadSessionDetailFromAPI相同的映射逻辑
        const mappedMessages = []
        
        result.data.forEach((msg) => {
          if (msg.role === 'user') {
            mappedMessages.push({
              id: msg.id,
              type: 'user',
              content: msg.content || '',
              timestamp: msg.createTime || new Date().toISOString(),
              isFinished: msg.isFinished === 1
            })
          } else if (msg.role === 'assistant') {
            if (msg.answer && msg.answer.trim()) {
              if (msg.content && msg.content.trim()) {
                mappedMessages.push({
                  id: `user_${msg.id}`,
                  type: 'user',
                  content: msg.content,
                  timestamp: msg.createTime || new Date().toISOString(),
                  isFinished: 1
                })
              }
              
              mappedMessages.push({
                id: msg.id,
                type: 'ai',
                content: msg.answer,
                timestamp: msg.createTime || new Date().toISOString(),
                isFinished: msg.isFinished === 1
              })
            } else {
              const content = msg.content || ''
              const looksLikeQuestion = content.length < 30 && (
                content.includes('？') || 
                content.includes('?') || 
                content.startsWith('什么') ||
                content.startsWith('怎么') ||
                content.startsWith('如何') ||
                content.startsWith('哪里') ||
                content.includes('在哪') ||
                content.includes('是什么')
              )
              
              if (looksLikeQuestion) {
                mappedMessages.push({
                  id: msg.id,
                  type: 'user',
                  content: content,
                  timestamp: msg.createTime || new Date().toISOString(),
                  isFinished: msg.isFinished === 1,
                  isAbnormalData: true
                })
              } else {
                mappedMessages.push({
                  id: msg.id,
                  type: 'ai',
                  content: content,
                  timestamp: msg.createTime || new Date().toISOString(),
                  isFinished: msg.isFinished === 1
                })
              }
            }
          } else {
            mappedMessages.push({
              id: msg.id,
              type: 'ai',
              content: msg.content || msg.answer || '',
              timestamp: msg.createTime || new Date().toISOString(),
              isFinished: msg.isFinished === 1
            })
          }
        })
        
        sessionDetailMessages.value = mappedMessages
        console.log('✅ 会话详情已加载:', mappedMessages.length, '条消息')
      } else {
        console.warn('⚠️ 该会话没有消息记录')
        sessionDetailMessages.value = []
      }
    } catch (error) {
      console.error('❌ 加载会话详情失败:', error)
      message.error('加载对话记录失败: ' + (error.message || '网络错误'))
      sessionDetailMessages.value = []
    }
  } else {
    // 没有sessionId的临时会话，显示本地数据
    sessionDetailMessages.value = session.messagesData || []
  }
  
  isLoadingDetail.value = false
}

// 从详情模态框跳转到对话页面
const goToChatFromDetail = async () => {
  if (currentSession.value) {
    showSessionDetailModal.value = false
    activeTab.value = 'chat'
    await handleSessionClick(currentSession.value)
  }
}

const loadSessionMessages = (sessionId) => {
  const session = sessions.value.find(s => s.id === sessionId)
  if (session && session.messagesData) {
    messages.value = session.messagesData
    chatHistory.value = buildChatHistory(session.messagesData)
  }
}

const buildChatHistory = (msgs) => {
  return msgs
    .filter(msg => !msg.isStreaming && !msg.isError)
    .map(msg => ({
      role: msg.type === 'user' ? 'user' : 'assistant',
      content: msg.content
    }))
}

const saveSessionsToStorage = () => {
  try {
    const sessionIndex = sessions.value.findIndex(s => s.id === activeSessionId.value)
    if (sessionIndex !== -1) {
      const updatedSessions = [...sessions.value]
      updatedSessions[sessionIndex] = {
        ...updatedSessions[sessionIndex],
        messagesData: messages.value,
        referencesData: { ...messageReferences.value }
      }
      sessions.value = updatedSessions
    }
    
    const data = {
      sessions: sessions.value,
      activeSessionId: activeSessionId.value
    }
    
    localStorage.setItem('chatSessions', JSON.stringify(data))
  } catch (e) {
    console.error('Failed to save sessions:', e)
  }
}

const loadSessionsFromAPI = async () => {
  console.log('📋 [DEBUG] ==================== loadSessionsFromAPI 开始执行 ====================')
  console.log('   - 调用堆栈:', new Error().stack?.split('\n')[2]?.trim())
  console.log('   - 当前 sessions 数量(调用前):', sessions.value.length)
  console.log('   - 当前 sessions 列表(调用前):', sessions.value.map(s => ({id: s.id, title: s.title, sessionId: s.sessionId})))
  
  isLoadingSessions.value = true
  try {
    console.log('📋 [DEBUG] 正在调用 getSessions API...')
    const result = await aiAPI.getSessions({ page: 1, pageSize: 50 })
    
    console.log('📋 [DEBUG] API 返回结果:', JSON.stringify(result, null, 2))
    
    if (result.data && Array.isArray(result.data)) {
      console.log('✅ [DEBUG] 获取到', result.data.length, '个会话')
      console.log('   - 原始数据详情:')
      result.data.forEach((session, index) => {
        console.log(`     [${index}] id=${session.id}, sessionId=${session.sessionId}, summary="${session.summary}"`)
      })
      
      const mappedSessions = result.data.map(session => ({
        id: session.id,
        sessionId: session.sessionId,
        title: (session.summary || session.title || '未命名会话').replace(/^(用户问[:：]\s*)/, ''),
        createdAt: session.createTime || new Date().toISOString(),
        messagesData: []
      }))
      
      console.log('✅ [DEBUG] 映射后的会话:')
      mappedSessions.forEach((session, index) => {
        console.log(`     [${index}] id=${session.id}, title="${session.title}", sessionId=${session.sessionId}`)
      })
      
      sessions.value = mappedSessions
      console.log('💾 [DEBUG] sessions.value 已更新，当前数量:', sessions.value.length)
      console.log('💾 [DEBUG] 更新后的 sessions 列表:', sessions.value.map(s => ({id: s.id, title: s.title})))
      
      // 异步获取每个会话的消息数量（不阻塞主流程）
      if (mappedSessions.length > 0) {
        // 使用Promise.allSettled并行获取所有会话的消息数
        const countPromises = mappedSessions
          .filter(s => s.sessionId)  // 只处理有sessionId的会话
          .map(async (session) => {
            try {
              const detailResult = await aiAPI.getSessionDetail(session.sessionId, { page: 1, pageSize: 1 })
              if (detailResult.data && detailResult.total !== undefined) {
                sessionMessageCount.value[session.id] = detailResult.total
              } else if (detailResult.data && Array.isArray(detailResult.data)) {
                sessionMessageCount.value[session.id] = detailResult.data.length
              }
            } catch (error) {
              console.warn(`⚠️ 获取会话 ${session.sessionId} 消息数失败:`, error.message)
              sessionMessageCount.value[session.id] = 0
            }
          })
        
        // 等待所有计数请求完成（但不阻塞UI）
        Promise.allSettled(countPromises).then(() => {
          console.log('✅ 所有会话消息数已更新:', sessionMessageCount.value)
        })
      }
      
      if (mappedSessions.length > 0) {
        activeSessionId.value = mappedSessions[0].id
        isTemporarySession.value = false
        await loadSessionDetailFromAPI(mappedSessions[0].sessionId)
      } else {
        console.log('⚠️ [DEBUG] 会话列表为空，调用 handleNewSession')
        handleNewSession()
      }
    } else {
      console.log('⚠️ [DEBUG] 没有找到会话数据或格式错误，result.data:', result.data)
      console.log('⚠️ [DEBUG] 调用 handleNewSession')
      handleNewSession()
    }
  } catch (error) {
    console.error('❌ [DEBUG] 获取会话列表失败:', error)
    message.error('获取会话列表失败: ' + (error.message || '网络错误'))
    console.log('⚠️ [DEBUG] 由于API失败，调用 handleNewSession')
    handleNewSession()
  } finally {
    isLoadingSessions.value = false
    console.log('📋 [DEBUG] ==================== loadSessionsFromAPI 执行完成 ====================')
  }
}

const loadSessionDetailFromAPI = async (sessionId) => {
  try {
    console.log('📝 正在获取会话详情:', sessionId)
    const result = await aiAPI.getSessionDetail(sessionId, { page: 1, pageSize: 100 })
    
    if (result.data && Array.isArray(result.data)) {
      console.log('✅ 获取到', result.data.length, '条消息')
      console.log('📋 原始消息数据:', JSON.stringify(result.data, null, 2))
      
      const mappedMessages = []
      
      result.data.forEach((msg, index) => {
        console.log(`🔍 处理第${index + 1}条消息:`, {
          id: msg.id,
          原始role: msg.role,
          用户问题: msg.content?.substring(0, 50),
          AI回复: msg.answer?.substring(0, 50),
          messageType: msg.messageType
        })
        
        // 根据API文档规范处理数据
        if (msg.role === 'user') {
          // 用户消息：直接添加
          mappedMessages.push({
            id: msg.id,
            type: 'user',
            content: msg.content || '',
            timestamp: msg.createTime || new Date().toISOString(),
            isFinished: msg.isFinished === 1
          })
        } else if (msg.role === 'assistant') {
          // AI消息：根据API文档，content存储用户问题，answer存储AI回复
          
          // 如果存在 answer 字段（标准格式），拆分为两条消息
          if (msg.answer && msg.answer.trim()) {
            // 先添加用户问题
            if (msg.content && msg.content.trim()) {
              mappedMessages.push({
                id: `user_${msg.id}`,
                type: 'user',
                content: msg.content,
                timestamp: msg.createTime || new Date().toISOString(),
                isFinished: 1
              })
            }
            
            // 再添加AI回复
            mappedMessages.push({
              id: msg.id,
              type: 'ai',
              content: msg.answer,
              timestamp: msg.createTime || new Date().toISOString(),
              isFinished: msg.isFinished === 1,
              images: safeParseImages(msg.images),
              tables: msg.tables || [],
              sources: msg.sources || msg.citations || []
            })
            } 
          // 如果没有 answer 字段（兼容旧格式或异常数据）
          else if (msg.content && msg.content.trim()) {
            // 智能检测内容是否像用户问题
            const looksLikeQuestion = msg.content.length < 30 && (
              msg.content.includes('？') || 
              msg.content.includes('?') || 
              msg.content.startsWith('什么') ||
              msg.content.startsWith('怎么') ||
              msg.content.startsWith('如何') ||
              msg.content.startsWith('哪里') ||
              msg.content.includes('在哪') ||
              msg.content.includes('是什么')
            )
            
            if (looksLikeQuestion) {
              // 异常数据：AI消息中包含用户问题，转换为用户消息显示
              console.warn(`⚠️ 检测到异常数据(id=${msg.id}): AI角色但内容像用户问题`)
              mappedMessages.push({
                id: msg.id,
                type: 'user',
                content: msg.content,
                timestamp: msg.createTime || new Date().toISOString(),
                isFinished: msg.isFinished === 1,
                isAbnormalData: true
              })
            } else {
              // 正常的AI回复（无answer字段的情况）
              mappedMessages.push({
                id: msg.id,
                type: 'ai',
                content: msg.content,
                timestamp: msg.createTime || new Date().toISOString(),
                isFinished: msg.isFinished === 1,
                images: safeParseImages(msg.images),
                tables: msg.tables || [],
                sources: msg.sources || msg.citations || []
              })
              }
          }
        } else {
          // 其他未知角色，默认作为AI消息
          mappedMessages.push({
            id: msg.id,
            type: 'ai',
            content: msg.content || msg.answer || '',
            timestamp: msg.createTime || new Date().toISOString(),
            isFinished: msg.isFinished === 1,
            images: safeParseImages(msg.images),
            tables: msg.tables || [],
            sources: msg.sources || msg.citations || []
          })
          }
      })
      
      console.log('✅ 映射后的消息列表:')
      mappedMessages.forEach((msg, idx) => {
        console.log(`  ${idx + 1}. [${msg.type.toUpperCase()}] ${msg.content.substring(0, 50)}${msg.content.length > 50 ? '...' : ''}${msg.isAbnormalData ? ' ⚠️异常' : ''}`)
      })
      
      if (mappedMessages.length === 0) {
        console.warn('⚠️ 会话为空')
        messages.value = []
      } else {
        messages.value = mappedMessages
      }
      
      chatHistory.value = messages.value.map(msg => ({
        role: msg.type === 'user' ? 'assistant' : 'assistant',
        content: msg.content
      }))
      
      // 重建引用数据：通过 API 接口获取每条 AI 消息的引用列表
      const rebuiltRefs = {}
      const imagePreviewMap = {}  // msgId -> { imageId -> preview }
      const aiMessages = mappedMessages.filter(msg => msg.type === 'ai')
      
      // 并行获取所有 AI 消息的引用数据
      const refPromises = aiMessages.map(async (msg) => {
        try {
          const refResult = await aiAPI.getMessageReferences(msg.id)
          if (refResult.data && Array.isArray(refResult.data) && refResult.data.length > 0) {
            rebuiltRefs[msg.id] = refResult.data.map((ref, idx) => ({
              id: ref.id || ref.chunkId || `ref_${msg.id}_${idx}`,
              chunkId: ref.chunkId || '',
              filename: ref.docName || '未知文档',
              location: ref.page ? `第${ref.page}页` : (ref.section || ''),
              context: ref.excerpt || ref.content?.stem || ref.preview || '',
              preview: ref.preview || '',
              score: ref.score || null,
              rawData: ref
            }))
            // 提取 chunkType='image' 的引用的 preview，用于图片定位
            const imageRefs = refResult.data.filter(r => r.chunkType === 'image')
            if (imageRefs.length > 0) {
              imagePreviewMap[msg.id] = {}
              imageRefs.forEach(r => {
                // 用 chunkId 或 docName+page 作为 key 匹配图片
                if (r.preview) {
                  imagePreviewMap[msg.id][r.chunkId] = r.preview
                }
              })
            }
          }
        } catch (e) {
          console.warn(`⚠️ 获取消息 ${msg.id} 引用失败:`, e.message)
        }
      })
      await Promise.allSettled(refPromises)
      
      // 将引用接口返回的图片 preview 补充到消息的 images 数据中
      mappedMessages.forEach(msg => {
        if (msg.type === 'ai' && msg.images && msg.images.length > 0 && imagePreviewMap[msg.id]) {
          const previewMap = imagePreviewMap[msg.id]
          msg.images = msg.images.map(img => {
            // 尝试多种方式匹配：chunkId、image_id+page、source+page
            const imgId = img.image_id || img.imageId || ''
            const imgSource = img.source || ''
            const imgPage = img.page || ''
            let matchedPreview = ''
            for (const [chunkId, preview] of Object.entries(previewMap)) {
              const chunkSource = chunkId.includes('_') ? chunkId.substring(0, chunkId.lastIndexOf('_')) : chunkId
              if (chunkId === imgId || chunkSource === imgSource || 
                  (imgSource && chunkSource.includes(imgSource.replace(/\.\w+$/, ''))) ||
                  (imgPage && chunkId.includes(imgPage))) {
                matchedPreview = preview
                break
              }
            }
            return matchedPreview ? { ...img, preview: matchedPreview } : img
          })
        }
      })
      
      // 后备：对没有获取到引用数据的消息，从内容中解析 [ref:xxx] 标记
      mappedMessages.forEach(msg => {
        if (msg.type === 'ai' && !rebuiltRefs[msg.id] && msg.content && msg.content.includes('[ref:')) {
          const parsed = parseReferencesFromContent(msg.content)
          if (parsed.length > 0) {
            rebuiltRefs[msg.id] = parsed
          }
        }
      })
      try {
        const cached = JSON.parse(localStorage.getItem('chatSessions'))
        const cachedSession = cached?.sessions?.find(s => s.id === activeSessionId.value)
        if (cachedSession?.referencesData) {
          Object.keys(cachedSession.referencesData).forEach(key => {
            if (cachedSession.referencesData[key]?.length > 0) {
              rebuiltRefs[key] = cachedSession.referencesData[key]
            }
          })
        }
      } catch (e) {}
      messageReferences.value = rebuiltRefs
      
      // 更新当前会话的消息数量缓存
      if (activeSessionId.value) {
        sessionMessageCount.value[activeSessionId.value] = mappedMessages.length
        console.log(`💾 已更新会话 ${activeSessionId.value} 的消息数:`, mappedMessages.length)
      }
      
      await scrollToBottom()
    }
  } catch (error) {
    console.error('❌ 获取会话详情失败:', error)
    message.error('获取对话记录失败: ' + (error.message || '网络错误'))
  }
}

const handleEditFaq = (faq) => {
  editingFaq.value = faq
  editQuestion.value = faq.question
  editAnswer.value = faq.answer
  showEditFaqModal.value = true
  message.info('进入编辑模式')
}

const handleSaveFaq = () => {
  const updatedFaqList = faqList.value.map(faq =>
    faq.id === editingFaq.value.id
      ? { ...faq, question: editQuestion.value, answer: editAnswer.value }
      : faq
  )
  faqList.value = updatedFaqList
  showEditFaqModal.value = false
  message.success('FAQ编辑成功！')
}

const handleDeleteFaq = (faq) => {
  const updatedFaqList = faqList.value.filter(f => f.id !== faq.id)
  faqList.value = updatedFaqList
  message.warning('FAQ已删除')
}

const handleAddFaq = () => {
  const newFaqId = faqList.value.length + 1
  const newFaq = {
    id: newFaqId,
    question: '新问题',
    answer: '新答案'
  }
  faqList.value = [...faqList.value, newFaq]
  message.success('FAQ新增成功！')
}

watch(
  () => messages.value,
  () => {
    saveSessionsToStorage()
  },
  { deep: true }
)

onMounted(async () => {
  await loadSessionsFromAPI()

  // 恢复之前的导航状态（从文件阅读器返回时）
  await restoreNavigationState()
})

onUnmounted(() => {
  saveSessionsToStorage()
  stopTypewriter()
  revokeImageBlobUrls()
})

const beforeLeave = () => {
  console.log('🚪 [DEBUG] beforeLeave 被调用')
  console.log('   - isLoading:', isLoading.value)
  console.log('   - isCreatingSession:', isCreatingSession.value)
  
  if (isLoading.value || isCreatingSession.value) {
    console.log('⚠️ [DEBUG] 检测到未完成的操作，提示用户')
    return window.confirm('AI正在回答问题，离开可能导致内容丢失。确定要离开吗？')
  }
  
  console.log('✅ [DEBUG] 可以安全离开')
  return true
}

const restoreSession = (sessionId) => {
  const session = sessions.value.find(s => s.id === sessionId || s.sessionId === sessionId)
  if (session) {
    handleSessionClick(session)
    return true
  }
  return false
}

defineExpose({
  beforeLeave,
  restoreSession
})
</script>

<style scoped>
.chat-page {
  height: 100%;
  min-height: 100%;
  background: var(--bg-page);
  overflow: hidden;
  display: flex;
  flex-direction: column;
}

.tab-icon {
  font-size: var(--font-lg);
}

.chat-layout {
  display: flex;
  flex: 1;
  overflow: hidden;
  max-width: 1600px;
  width: 100%;
  margin: 0 auto;
  min-width: 0;
}

.sidebar {
  width: 220px;
  background: var(--bg-card);
  border-right: 1px solid var(--border-light);
  display: flex;
  flex-direction: column;
  flex-shrink: 0;
}

.sidebar-header {
  padding: var(--space-xl);
  border-bottom: 1px solid var(--border-light);
}

.app-logo {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  margin-bottom: var(--space-md);
}

.logo-icon {
  font-size: var(--font-2xl);
}

.logo-text {
  font-size: var(--font-xl);
  font-weight: var(--weight-bold);
  color: var(--text-primary);
}

.new-chat-btn {
  width: 100%;
  padding: var(--space-md) var(--space-lg);
  background: var(--color-primary);
  color: var(--text-inverse);
  border: none;
  border-radius: var(--radius-lg);
  font-size: var(--font-sm);
  font-weight: var(--weight-semibold);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-sm);
  transition: all var(--transition-normal);
  box-shadow: var(--shadow-sm);
}

.new-chat-btn:hover {
  transform: translateY(-2px);
  box-shadow: var(--shadow-md);
  background: var(--color-primary-hover);
}

.btn-icon {
  font-size: var(--font-lg);
  font-weight: bold;
}

.sidebar-content {
  flex: 1;
  overflow-y: auto;
  overflow-x: hidden;
  padding: var(--space-md);
}

.session-list-title {
  font-size: var(--font-xs);
  font-weight: var(--weight-semibold);
  color: var(--text-tertiary);
  text-transform: uppercase;
  letter-spacing: 0.5px;
  margin-bottom: var(--space-sm);
  padding: 0 var(--space-md);
}

.session-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
}

.session-loading,
.session-empty {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-3xl) var(--space-lg);
  color: var(--text-disabled);
  font-size: var(--font-sm);
  gap: var(--space-sm);
}

.loading-spinner {
  animation: spin 1s linear infinite;
}

@keyframes spin {
  from { transform: rotate(0deg); }
  to { transform: rotate(360deg); }
}

.session-item {
  display: flex;
  align-items: center;
  gap: var(--space-md);
  padding: var(--space-md);
  border-radius: var(--radius-lg);
  cursor: pointer;
  transition: all var(--transition-fast);
  position: relative;
}

.session-item:hover {
  background: var(--bg-hover);
}

.session-item:hover .session-actions {
  opacity: 1;
}

.session-item.active {
  background: var(--bg-active);
  border: 1px solid var(--border-medium);
}

.session-icon {
  font-size: var(--font-lg);
  flex-shrink: 0;
}

.session-content {
  flex: 1;
  min-width: 0;
}

.session-title {
  font-size: var(--font-sm);
  font-weight: var(--weight-medium);
  color: var(--text-primary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  margin-bottom: 2px;
}

.session-item.active .session-title {
  color: var(--color-primary);
  font-weight: var(--weight-semibold);
}

.session-date {
  font-size: var(--font-xs);
  color: var(--text-tertiary);
}

.session-actions {
  display: flex;
  gap: 4px;
  opacity: 0;
  transition: opacity 0.2s ease;
}

.session-item:hover .session-actions,
.session-item.active .session-actions {
  opacity: 1;
}

.action-btn {
  width: 28px;
  height: 28px;
  border: none;
  background: transparent;
  border-radius: var(--radius-md);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: all var(--transition-fast);
  font-size: var(--font-sm);
}

.action-btn:hover {
  background: var(--bg-error);
  transform: scale(1.1);
}

.delete-btn:hover {
  background: var(--bg-error);
  border: 1px solid var(--color-error);
}

.sidebar-footer {
  padding: var(--space-md);
  border-top: 1px solid var(--border-light);
  flex-shrink: 0;
}

.sidebar-footer .chat-input-area {
  padding: 0;
  background: transparent;
  border-top: none;
}

.sidebar-footer .input-wrapper {
  max-width: 100%;
  background: var(--bg-container);
  border: 2px solid var(--border-light);
  border-radius: var(--radius-lg);
  padding: var(--space-sm) var(--space-md);
}

.sidebar-footer .chat-textarea {
  font-size: var(--font-sm);
  line-height: 1.5;
}

.sidebar-footer .input-actions {
  margin-top: var(--space-xs);
  padding-top: var(--space-xs);
}

.sidebar-footer .cancel-btn,
.sidebar-footer .send-btn {
  padding: var(--space-xs) var(--space-md);
  font-size: var(--font-xs);
  border-radius: var(--radius-md);
}

.sidebar-footer .input-hint {
  margin: var(--space-xs) auto 0;
  font-size: var(--font-2xs);
}

.main-area {
  flex: 1;
  display: flex;
  flex-direction: column;
  background: var(--bg-container);
  min-width: 0;
  overflow: hidden;
}

.main-area.full-width {
  width: 100%;
  max-width: 1400px;
  margin: 0 auto;
}

.tab-content {
  flex: 1;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.chat-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: var(--space-lg) var(--space-2xl);
  background: var(--bg-card);
  border-bottom: 1px solid var(--border-light);
  flex-shrink: 0;
}

.current-session-title {
  display: flex;
  align-items: center;
  gap: var(--space-md);
  font-size: var(--font-xl);
  font-weight: var(--weight-semibold);
  color: var(--text-primary);
}

.title-icon {
  font-size: var(--font-2xl);
}

.chat-header-actions {
  display: flex;
  gap: var(--space-md);
}

.header-btn {
  width: 36px;
  height: 36px;
  border: 1px solid var(--border-light);
  background: var(--bg-card);
  border-radius: var(--radius-lg);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: var(--font-lg);
  transition: all var(--transition-fast);
}

.header-btn:hover {
  background: var(--bg-hover);
  border-color: var(--color-primary);
}

.chat-messages {
  flex: 1;
  padding: var(--space-lg);
  overflow-y: auto;
  overflow-x: hidden;
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
}

.empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  flex: 1;
  text-align: center;
  padding: var(--space-3xl) var(--space-lg);
}

.empty-icon {
  font-size: var(--font-5xl);
  margin-bottom: var(--space-lg);
}

.empty-title {
  font-size: var(--font-2xl);
  font-weight: var(--weight-semibold);
  color: var(--text-primary);
  margin-bottom: var(--space-md);
}

.empty-desc {
  font-size: var(--font-sm);
  color: var(--text-tertiary);
  margin-bottom: var(--space-lg);
}

.quick-questions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-md);
  justify-content: center;
  max-width: 600px;
}

.quick-question-btn {
  padding: var(--space-sm) var(--space-lg);
  background: var(--bg-card);
  border: 1px solid var(--border-light);
  border-radius: var(--radius-xl);
  font-size: var(--font-sm);
  color: var(--text-secondary);
  cursor: pointer;
  transition: all var(--transition-fast);
}

.quick-question-btn:hover {
  border-color: var(--color-primary);
  color: var(--color-primary);
  background: var(--bg-hover);
  transform: translateY(-2px);
  box-shadow: var(--shadow-md);
}

.chat-message {
  display: flex;
  gap: var(--space-lg);
  max-width: 900px;
  width: 100%;
  margin: 0 auto;
}

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

.message-avatar {
  width: 32px;
  height: 32px;
  border-radius: var(--radius-md);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: var(--font-xl);
  flex-shrink: 0;
  background: var(--bg-container);
  border: 1px solid var(--border-light);
}

.chat-message.user .message-avatar {
  background: var(--color-primary);
  color: var(--text-inverse);
}

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

.message-header {
  display: flex;
  align-items: center;
  gap: var(--space-md);
  margin-bottom: var(--space-md);
}

.message-role {
  font-size: var(--font-md);
  font-weight: var(--weight-semibold);
  color: var(--text-secondary);
}

.chat-message.user .message-role {
  color: var(--color-primary);
}

.message-time {
  font-size: var(--font-xs);
  color: var(--text-disabled);
}

.message-content {
  background: var(--bg-card);
  border-radius: var(--radius-xl);
  padding: var(--space-lg) var(--space-xl);
  box-shadow: var(--shadow-sm);
  line-height: 1.7;
  color: var(--text-primary);
  font-size: var(--font-md);
  position: relative;
  word-wrap: break-word;
  overflow-wrap: break-word;
  word-break: break-all;
  overflow: hidden;
  max-width: 100%;
  border: 1px solid var(--border-light);
}

.chat-message.user .message-content {
  background: var(--color-primary);
  color: var(--text-inverse);
  border-bottom-right-radius: var(--radius-sm);
}

.chat-message.ai .message-content {
  border-bottom-left-radius: var(--radius-sm);
}

/* AI状态指示器 Mini - 小巧精致版 */
.ai-status {
  padding: 0;
}

/* 思考状态 AI风格 */
.ai-status.thinking-ai {
  padding: var(--space-md) var(--space-lg);
  background: var(--bg-container);
  border-radius: var(--radius-lg);
  border: 1px solid var(--border-light);
  display: flex;
  align-items: center;
  gap: var(--space-md);
}

.thinking-ai-visualizer {
  display: flex;
  align-items: center;
  gap: 10px;
}

.ai-orb {
  position: relative;
  width: 32px;
  height: 32px;
  flex-shrink: 0;
}

.ai-orb-core {
  position: absolute;
  width: 14px;
  height: 14px;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  background: var(--color-primary);
  border-radius: 50%;
  animation: ai-core-pulse 2s ease-in-out infinite;
  z-index: 2;
}

.ai-orb-glow {
  position: absolute;
  width: 32px;
  height: 32px;
  top: 0;
  left: 0;
  background: radial-gradient(circle, rgba(33, 37, 41, 0.2) 0%, transparent 70%);
  border-radius: 50%;
  animation: ai-glow 2.5s ease-in-out infinite;
}

.ai-orb-ring {
  position: absolute;
  width: 28px;
  height: 28px;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  border: 1.5px solid rgba(33, 37, 41, 0.2);
  border-radius: 50%;
  animation: ai-ring-spin 3s linear infinite;
}

@keyframes ai-core-pulse {
  0%, 100% { transform: translate(-50%, -50%) scale(0.9); opacity: 0.8; }
  50% { transform: translate(-50%, -50%) scale(1.1); opacity: 1; }
}

@keyframes ai-glow {
  0%, 100% { transform: scale(1); opacity: 0.4; }
  50% { transform: scale(1.3); opacity: 0.15; }
}

@keyframes ai-ring-spin {
  0% { transform: translate(-50%, -50%) rotate(0deg); border-color: rgba(33, 37, 41, 0.2); }
  50% { border-color: rgba(33, 37, 41, 0.3); }
  100% { transform: translate(-50%, -50%) rotate(360deg); border-color: rgba(33, 37, 41, 0.2); }
}

.ai-wave-container {
  display: flex;
  align-items: center;
  gap: 3px;
  height: 18px;
}

.ai-wave-container span {
  width: 3px;
  height: 14px;
  background: var(--color-primary);
  border-radius: var(--radius-sm);
  animation: ai-wave 1.2s ease-in-out infinite;
}

.ai-wave-container span:nth-child(1) { animation-delay: 0s; }
.ai-wave-container span:nth-child(2) { animation-delay: 0.1s; }
.ai-wave-container span:nth-child(3) { animation-delay: 0.2s; }
.ai-wave-container span:nth-child(4) { animation-delay: 0.3s; }
.ai-wave-container span:nth-child(5) { animation-delay: 0.4s; }

@keyframes ai-wave {
  0%, 60%, 100% { transform: scaleY(0.4); opacity: 0.3; }
  30% { transform: scaleY(1); opacity: 0.9; }
}

.thinking-ai-text {
  font-size: var(--font-sm);
  color: var(--text-secondary);
  font-weight: var(--weight-medium);
  letter-spacing: 0.2px;
}

/* 搜索状态 Mini */
.ai-status.searching-mini {
  padding: var(--space-md) var(--space-lg);
  background: var(--bg-container);
  border-radius: var(--radius-lg);
  border: 1px solid var(--border-light);
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}

.searching-mini-row {
  display: flex;
  align-items: center;
  gap: 10px;
}

.searching-mini-spinner {
  display: flex;
  gap: var(--space-xs);
}

.searching-mini-spinner div {
  width: 5px;
  height: 16px;
  background: var(--color-primary);
  border-radius: var(--radius-sm);
  animation: mini-bars-wave 1s ease-in-out infinite;
}

.searching-mini-spinner div:nth-child(1) { animation-delay: 0s; }
.searching-mini-spinner div:nth-child(2) { animation-delay: 0.1s; }
.searching-mini-spinner div:nth-child(3) { animation-delay: 0.2s; }
.searching-mini-spinner div:nth-child(4) { animation-delay: 0.3s; }

@keyframes mini-bars-wave {
  0%, 60%, 100% { transform: scaleY(0.5); opacity: 0.4; }
  30% { transform: scaleY(1); opacity: 1; }
}

.searching-mini-info {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.searching-mini-text {
  font-size: var(--font-sm);
  color: var(--text-primary);
  font-weight: var(--weight-medium);
}

.searching-mini-collection {
  font-size: var(--font-xs);
  color: var(--color-primary);
  font-weight: var(--weight-medium);
}

.searching-mini-bars {
  display: flex;
  gap: var(--space-xs);
  height: 4px;
}

.searching-mini-bars div {
  flex: 1;
  background: var(--border-default);
  border-radius: var(--radius-sm);
  transition: background 0.4s ease;
}

.searching-mini-bars div.active {
  background: var(--color-primary);
  animation: mini-progress-pulse 1s ease-in-out infinite;
}

@keyframes mini-progress-pulse {
  0%, 100% { opacity: 0.7; }
  50% { opacity: 1; }
}

/* 响应时间 - 移到对话框外右下角 */
.response-time-outside {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  margin-top: var(--space-md);
  margin-bottom: 4px;
  font-size: var(--font-xs);
  color: var(--text-disabled);
  justify-content: flex-end;
  width: 100%;
  padding-right: 6px;
  opacity: 0.85;
  transition: opacity var(--transition-fast);
}

.response-time-outside:hover {
  opacity: 1;
}

.response-time-icon {
  font-size: var(--font-xs);
}

/* 引用条目可点击提示 */
.ref-group .reference-item {
  cursor: pointer;
}

/* 响应耗时徽章 */
.response-time-badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-top: var(--space-md);
  padding: var(--space-xs) var(--space-md);
  background: var(--bg-container);
  border: 1px solid var(--border-light);
  border-radius: var(--radius-xl);
  font-size: var(--font-xs);
  color: var(--color-primary);
  font-weight: var(--weight-semibold);
  animation: fade-in-up 0.3s ease-out;
}

/* 富媒体内容展示 */
.media-gallery {
  margin-top: var(--space-lg);
  padding: var(--space-lg);
  background: var(--bg-container);
  border: 1px solid var(--border-light);
  border-radius: var(--radius-lg);
  animation: fade-in-up 0.4s ease-out;
}

.gallery-title {
  font-size: var(--font-md);
  font-weight: var(--weight-semibold);
  color: var(--text-primary);
  margin: 0 0 var(--space-md) 0;
  display: flex;
  align-items: center;
  gap: var(--space-md);
}

.images-gallery {
  overflow-x: auto;
}

.images-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: 12px;
}

.image-item {
  position: relative;
  border-radius: var(--radius-lg);
  overflow: hidden;
  cursor: pointer;
  transition: all var(--transition-normal);
  box-shadow: var(--shadow-sm);
}

.image-item:hover {
  transform: translateY(-4px);
  box-shadow: var(--shadow-lg);
}

.image-item img {
  width: 100%;
  height: auto;
  display: block;
  object-fit: cover;
  max-height: 200px;
}

.image-caption {
  padding: var(--space-sm) var(--space-md);
  background: rgba(255, 255, 255, 0.95);
  font-size: var(--font-xs);
  color: var(--text-secondary);
  text-align: center;
}

/* 图片加载失败占位符 */
.image-item::before {
  content: '🖼️ 图片加载失败';
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  font-size: var(--font-md);
  color: var(--text-disabled);
  text-align: center;
  z-index: 1;
  pointer-events: none;
  opacity: 0;
  transition: opacity var(--transition-fast);
}

.image-item img[src=""] + .image-caption::before,
.image-item:has(img[style*="display: none"])::before {
  opacity: 1;
}

.tables-gallery {
  max-width: 100%;
  overflow-x: auto;
}

.table-container {
  margin-bottom: 16px;
}

.table-container:last-child {
  margin-bottom: 0;
}

.table-title {
  font-size: var(--font-sm);
  font-weight: var(--weight-semibold);
  color: var(--text-primary);
  margin: 0 0 var(--space-md) 0;
  padding-bottom: var(--space-sm);
  border-bottom: 2px solid var(--color-primary);
}

.table-wrapper {
  overflow-x: auto;
  border-radius: var(--radius-lg);
  border: 1px solid var(--border-light);
}

.data-table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--font-md);
}

.data-table thead {
  background: var(--color-primary);
  color: var(--text-inverse);
}

.data-table th,
.data-table td {
  padding: var(--space-sm) var(--space-md);
  text-align: left;
  border: 1px solid var(--border-light);
}

.data-table th {
  font-weight: var(--weight-semibold);
  font-size: var(--font-xs);
  text-transform: uppercase;
  letter-spacing: 0.5px;
}

.data-table tbody tr {
  transition: all var(--transition-fast);
}

.data-table tbody tr:hover {
  background: var(--bg-hover);
}

.data-table tbody tr:nth-child(even) {
  background: var(--bg-container);
}

/* 动画效果 */
@keyframes fade-in-up {
  from {
    opacity: 0;
    transform: translateY(10px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.error-hint {
  margin-top: var(--space-md);
  padding: var(--space-sm) var(--space-lg);
  background: var(--bg-error);
  border: 1px solid var(--color-error);
  border-radius: var(--radius-lg);
  display: flex;
  align-items: center;
  gap: var(--space-md);
}

.error-icon {
  font-size: var(--font-lg);
}

.error-text {
  font-size: var(--font-md);
  color: var(--color-error);
}

.message-actions {
  display: flex;
  gap: var(--space-xs);
  margin-top: var(--space-sm);
  opacity: 0;
  transition: opacity var(--transition-fast);
}

.chat-message.ai:hover .message-actions {
  opacity: 1;
}

.message-action-btn {
  padding: var(--space-xs) var(--space-md);
  background: var(--bg-card);
  border: 1px solid var(--border-light);
  border-radius: var(--radius-md);
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: var(--space-xs);
  font-size: var(--font-sm);
  color: var(--text-secondary);
  transition: all var(--transition-fast);
}

.message-action-btn:hover {
  background: var(--bg-hover);
  border-color: var(--color-primary);
  color: var(--color-primary);
}

.streaming-cursor {
  display: inline-block;
  width: 3px;
  height: 1.1em;
  background: var(--color-primary);
  border-radius: var(--radius-sm);
  margin-left: 2px;
  vertical-align: text-bottom;
  animation: cursorBlink 1s ease-in-out infinite;
}

@keyframes cursorBlink {
  0%, 100% { opacity: 1; }
  50% { opacity: 0; }
}

.streaming-output {
  position: relative;
  padding-bottom: 4px;
  line-height: 1.7;
}

.streaming-output a,
.content-paragraph a,
.message-content-detail a {
  color: var(--color-primary, #1677ff);
  text-decoration: underline;
  cursor: pointer;
}
.streaming-output a:hover,
.content-paragraph a:hover,
.message-content-detail a:hover {
  opacity: 0.8;
}

.ai-status.thinking {
  display: flex;
  align-items: center;
  gap: var(--space-md);
  padding: var(--space-md) var(--space-lg);
  background: var(--bg-container);
  border-radius: var(--radius-lg);
  border: 1px solid var(--border-light);
}

.thinking-animation {
  flex-shrink: 0;
}

.thinking-dots {
  display: flex;
  gap: var(--space-sm);
  padding: 4px 0;
}

.thinking-dots .dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--color-primary);
  animation: thinkingPulse 1.4s ease-in-out infinite;
}

.thinking-dots .dot:nth-child(2) {
  animation-delay: 0.2s;
}

.thinking-dots .dot:nth-child(3) {
  animation-delay: 0.4s;
}

@keyframes thinkingPulse {
  0%, 80%, 100% {
    transform: scale(0.6);
    opacity: 0.4;
  }
  40% {
    transform: scale(1);
    opacity: 1;
  }
}

.chat-message.ai.is-streaming .message-content {
  border-left: 3px solid var(--color-primary);
  padding-left: var(--space-md);
  background: var(--bg-container);
}

.code-block {
  background: var(--bg-container);
  padding: var(--space-md) var(--space-lg);
  border-radius: var(--radius-lg);
  margin: var(--space-md) 0;
  overflow-x: auto;
  font-family: 'Monaco', 'Menlo', monospace;
  font-size: var(--font-md);
  line-height: 1.6;
  color: var(--text-primary);
}

.chat-input-area {
  padding: 16px 24px 24px;
  background: var(--bg-card);
  flex-shrink: 0;
}

.input-wrapper {
  max-width: 768px;
  margin: 0 auto;
  background: var(--bg-container);
  border: 1px solid rgba(0, 0, 0, 0.12);
  border-radius: 16px;
  padding: 12px 16px;
  display: flex;
  align-items: flex-end;
  gap: 8px;
  transition: all 0.2s ease;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
}

.input-wrapper:hover {
  border-color: rgba(0, 0, 0, 0.18);
}

.input-wrapper:focus-within {
  border-color: rgba(0, 0, 0, 0.25);
  box-shadow: 0 1px 6px rgba(0, 0, 0, 0.08), 0 0 0 2px rgba(33, 37, 41, 0.04);
}

.chat-textarea {
  width: 100%;
  border: none;
  background: transparent;
  font-size: 15px;
  line-height: 1.5;
  resize: none;
  outline: none;
  color: var(--text-primary);
  font-family: inherit;
  min-height: 24px;
  max-height: 200px;
  padding: 4px 0;
}

.chat-textarea::placeholder {
  color: #9ca3af;
}

.chat-textarea:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.input-actions {
  display: flex;
  align-items: center;
  flex-shrink: 0;
  margin-bottom: 2px;
}

.stop-btn {
  width: 32px;
  height: 32px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: #f3f4f6;
  color: #374151;
  border: none;
  border-radius: 50%;
  font-size: 14px;
  cursor: pointer;
  transition: all 0.15s ease;
  flex-shrink: 0;
}

.stop-btn:hover {
  background: #e5e7eb;
  transform: scale(1.05);
}

.send-btn {
  width: 32px;
  height: 32px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: #e5e7eb;
  color: #9ca3af;
  border: none;
  border-radius: 50%;
  font-size: 14px;
  cursor: pointer;
  transition: all 0.2s ease;
  flex-shrink: 0;
}

.send-btn.has-text {
  background: var(--color-primary);
  color: #fff;
}

.send-btn.has-text:hover:not(:disabled) {
  background: var(--color-primary);
  filter: brightness(1.08);
  transform: scale(1.05);
}

.send-btn:disabled {
  cursor: default;
}

/* 暂停按钮样式 */
.pause-btn {
  padding: var(--space-xs) var(--space-lg);
  background: var(--color-warning);
  color: var(--text-inverse);
  border: none;
  border-radius: var(--radius-lg);
  font-size: var(--font-md);
  font-weight: var(--weight-semibold);
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  transition: all var(--transition-normal);
  box-shadow: var(--shadow-sm);
}

.pause-btn:hover {
  transform: translateY(-2px);
  box-shadow: var(--shadow-md);
}

.pause-btn:active {
  transform: translateY(0);
}

/* 继续按钮样式 */
.resume-btn {
  padding: var(--space-xs) var(--space-lg);
  background: var(--color-success);
  color: var(--text-inverse);
  border: none;
  border-radius: var(--radius-lg);
  font-size: var(--font-md);
  font-weight: var(--weight-semibold);
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  transition: all var(--transition-normal);
  box-shadow: var(--shadow-sm);
  animation: pulse-glow 2s ease-in-out infinite;
}

.resume-btn:hover {
  transform: translateY(-2px) scale(1.05);
  box-shadow: var(--shadow-lg);
  animation: none;
}

@keyframes pulse-glow {
  0%, 100% { 
    box-shadow: var(--shadow-md); 
  }
  50% { 
    box-shadow: var(--shadow-lg); 
  }
}

.input-hint {
  max-width: 900px;
  margin: 4px auto 0;
  text-align: center;
  font-size: var(--font-xs);
  color: var(--text-disabled);
}

/* 问答历史和质量分析页面样式 */
.history-page,
.quality-page {
  flex: 1;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.page-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: var(--space-xl) var(--space-2xl);
  background: var(--bg-card);
  border-bottom: 1px solid var(--border-light);
  flex-shrink: 0;
  flex-wrap: wrap;
  gap: var(--space-lg);
}

.page-header h1 {
  font-size: var(--font-xl);
  font-weight: var(--weight-bold);
  color: var(--text-primary);
  margin: 0;
  display: flex;
  align-items: center;
  gap: var(--space-md);
}

.header-stats {
  display: flex;
  gap: var(--space-xl);
}

.stat-item {
  text-align: center;
}

.stat-value {
  font-size: var(--font-2xl);
  font-weight: var(--weight-bold);
  color: var(--color-primary);
  display: block;
}

.stat-label {
  font-size: var(--font-xs);
  color: var(--text-tertiary);
  margin-top: 2px;
}

.header-badge {
  padding: var(--space-xs) var(--space-lg);
  background: var(--bg-container);
  border-radius: var(--radius-xl);
  font-size: var(--font-md);
  font-weight: var(--weight-medium);
  color: var(--color-primary);
}

.page-content {
  flex: 1;
  padding: var(--space-lg);
  overflow-y: auto;
  overflow-x: hidden;
}

.content-card {
  background: var(--bg-card);
  border-radius: var(--radius-lg);
  padding: var(--space-lg);
  margin-bottom: var(--space-lg);
  box-shadow: var(--shadow-sm);
  overflow: hidden;
}

.card-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: var(--space-md);
  padding-bottom: var(--space-sm);
  border-bottom: 1px solid var(--border-light);
  cursor: pointer;
  transition: all var(--transition-fast);
}

.card-header:hover {
  background: var(--bg-container);
  margin: -8px -12px 8px -12px;
  padding: var(--space-sm) var(--space-md) var(--space-sm) var(--space-md);
  border-radius: var(--radius-md);
}

.card-header h2 {
  margin: 0;
  font-size: var(--font-lg);
  color: var(--text-primary);
  display: flex;
  align-items: center;
  gap: var(--space-md);
}

.header-right {
  display: flex;
  align-items: center;
  gap: var(--space-md);
}

.header-actions {
  display: flex;
  gap: var(--space-sm);
}

.collapse-icon {
  font-size: var(--font-sm);
  color: var(--text-tertiary);
  transition: transform var(--transition-normal);
  user-select: none;
}

.collapse-icon.collapsed {
  transform: rotate(-90deg);
}

.card-content {
  transition: all var(--transition-normal);
}

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-sm) var(--space-lg);
  border: none;
  border-radius: var(--radius-lg);
  font-size: var(--font-sm);
  font-weight: var(--weight-medium);
  cursor: pointer;
  transition: all var(--transition-fast);
  gap: var(--space-sm);
}

.btn-primary {
  background: var(--color-primary);
  color: var(--text-inverse);
}

.btn-primary:hover:not(:disabled) {
  transform: translateY(-2px);
  box-shadow: var(--shadow-md);
}

.btn-danger {
  background: var(--color-error);
  color: var(--text-inverse);
}

.btn-sm {
  padding: var(--space-xs) var(--space-md);
  font-size: var(--font-md);
}

.btn-xs {
  padding: var(--space-xs) var(--space-md);
  font-size: var(--font-xs);
}

.session-card-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
}

.session-card {
  background: var(--bg-container);
  border: 1px solid var(--border-light);
  border-radius: var(--radius-lg);
  padding: var(--space-lg);
  transition: all var(--transition-fast);
}

.session-card:hover {
  box-shadow: var(--shadow-md);
  border-color: var(--border-default);
}

.session-card.active {
  background: var(--bg-hover);
  border-color: var(--color-primary);
  box-shadow: var(--shadow-sm);
}

/* 加载状态容器 */
.loading-container {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: var(--space-3xl) var(--space-lg);
  background: var(--bg-card);
  border-radius: var(--radius-lg);
  min-height: 200px;
}

/* 空状态容器 */
.empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: var(--space-3xl) var(--space-lg);
  background: var(--bg-card);
  border-radius: var(--radius-lg);
  min-height: 200px;
}

.session-card-main {
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.session-card-info h3 {
  margin: 0 0 var(--space-sm) 0;
  font-size: var(--font-md);
  color: var(--text-primary);
}

.session-card-meta {
  display: flex;
  gap: var(--space-lg);
  font-size: var(--font-md);
  color: var(--text-tertiary);
}

.session-card-actions {
  display: flex;
  gap: var(--space-md);
}

.report-highlights {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-lg);
  margin-top: var(--space-lg);
}

.report-highlight {
  text-align: center;
  padding: var(--space-xl) var(--space-lg);
  background: var(--bg-container);
  border-radius: var(--radius-lg);
}

.highlight-value {
  font-size: var(--font-2xl);
  font-weight: var(--weight-bold);
  margin-bottom: var(--space-md);
}

.highlight-label {
  font-size: var(--font-md);
  color: var(--text-tertiary);
  font-weight: var(--weight-medium);
}

.tag-group {
  display: flex;
  gap: var(--space-md);
  flex-wrap: wrap;
  margin-top: var(--space-md);
}

.tag-item {
  padding: var(--space-xs) var(--space-xl);
  background: var(--bg-container);
  color: var(--color-primary);
  border: 1px solid var(--border-light);
  border-radius: var(--radius-xl);
  font-size: var(--font-sm);
  font-weight: var(--weight-medium);
  transition: all var(--transition-normal);
}

.tag-item:hover {
  transform: translateY(-2px);
  box-shadow: var(--shadow-md);
}

.recommend-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  margin-top: var(--space-md);
}

.recommend-item {
  padding: var(--space-md) var(--space-lg);
  background: var(--bg-card);
  border: 1px solid var(--border-light);
  border-radius: var(--radius-lg);
  cursor: pointer;
  transition: all var(--transition-fast);
  font-size: var(--font-sm);
  color: var(--text-primary);
}

.recommend-item:hover {
  border-color: var(--color-primary);
  color: var(--color-primary);
  background: var(--bg-hover);
}

.rank-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
  margin-top: var(--space-lg);
}

.rank-item {
  display: flex;
  gap: var(--space-lg);
  align-items: center;
  padding: var(--space-lg);
  background: var(--bg-container);
  border-radius: var(--radius-lg);
  border: 1px solid var(--border-light);
}

.rank-number {
  width: 40px;
  height: 40px;
  background: var(--color-primary);
  color: var(--text-inverse);
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: var(--weight-bold);
  font-size: var(--font-lg);
  flex-shrink: 0;
}

.rank-content {
  flex: 1;
}

.rank-question {
  font-size: var(--font-sm);
  color: var(--text-primary);
  margin-bottom: var(--space-xs);
  font-weight: var(--weight-medium);
}

.rank-count {
  font-size: var(--font-xs);
  color: var(--text-tertiary);
}

.low-score-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
  margin-top: var(--space-lg);
}

.low-score-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: var(--space-lg);
  background: var(--bg-warning);
  border-radius: var(--radius-lg);
  border: 1px solid var(--color-warning);
}

.low-score-question {
  font-size: var(--font-sm);
  color: var(--text-primary);
  font-weight: var(--weight-medium);
}

.low-score-score {
  font-size: var(--font-sm);
  color: var(--color-warning);
  font-weight: var(--weight-semibold);
}

.score-star {
  margin-right: 8px;
}

.faq-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
}

.faq-item {
  padding: var(--space-lg);
  background: var(--bg-container);
  border: 1px solid var(--border-light);
  border-radius: var(--radius-lg);
  transition: all var(--transition-fast);
}

.faq-item:hover {
  box-shadow: var(--shadow-md);
  border-color: var(--border-default);
}

.faq-question {
  font-size: var(--font-md);
  font-weight: var(--weight-semibold);
  color: var(--color-primary);
  margin-bottom: var(--space-sm);
}

.faq-answer {
  font-size: var(--font-sm);
  color: var(--text-secondary);
  margin-bottom: var(--space-md);
  line-height: 1.7;
}

.faq-actions {
  display: flex;
  gap: var(--space-md);
  justify-content: flex-end;
}

.status-badge {
  display: inline-block;
  padding: var(--space-xs) var(--space-lg);
  border-radius: var(--radius-xl);
  font-size: var(--font-md);
  font-weight: var(--weight-medium);
}

.status-info {
  background: var(--bg-info);
  color: var(--color-info);
}

.form-group {
  margin-bottom: var(--space-lg);
}

.form-label {
  display: block;
  font-size: var(--font-md);
  font-weight: var(--weight-semibold);
  color: var(--text-primary);
  margin-bottom: var(--space-xs);
}

.form-input {
  width: 100%;
  height: var(--input-height);
  padding: 0 var(--space-md);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-md);
  font-size: var(--font-md);
  transition: all var(--transition-normal);
  background: var(--bg-container);
  box-sizing: border-box;
}

.form-input:focus {
  outline: none;
  border-color: var(--color-primary);
  box-shadow: 0 0 0 3px rgba(33, 37, 41, 0.05);
  background: var(--bg-card);
}

.modal-chat {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  padding: var(--space-xs) 0;
}

.modal-ai-msg,
.modal-user-msg {
  display: flex;
}

.modal-ai-msg {
  justify-content: flex-start;
}

.modal-user-msg {
  justify-content: flex-end;
}

.modal-bubble {
  max-width: 75%;
  padding: var(--space-xs) var(--space-md);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  line-height: 1.5;
  font-size: var(--font-md);
}

.modal-ai-msg .modal-bubble {
  background: var(--bg-card);
  color: var(--text-primary);
  border: 1px solid var(--border-light);
}

.modal-user-msg .modal-bubble {
  background: var(--color-primary);
  color: var(--text-inverse);
}

@media (max-width: 900px) {
  .sidebar {
    position: fixed;
    left: 0;
    top: 0;
    bottom: 0;
    z-index: 100;
    transform: translateX(-100%);
    transition: transform 0.3s ease;
  }
  
  .sidebar.open {
    transform: translateX(0);
  }
  
  .chat-message {
    max-width: 100%;
  }
  
  .quick-questions {
    flex-direction: column;
    width: 100%;
  }
  
  .quick-question-btn {
    width: 100%;
  }
  
  .report-highlights {
    grid-template-columns: 1fr;
  }
  
  .tabs-bar {
    padding: 10px 16px;
  }
  
  .tab-btn {
    padding: var(--space-sm) var(--space-lg);
    font-size: var(--font-md);
  }
  
  .main-area.full-width {
    max-width: 100%;
  }

  .page-content {
    padding: var(--space-md);
  }

  .content-card {
    padding: var(--space-md);
  }
  
  .header-stats {
    flex-wrap: wrap;
    justify-content: center;
  }
}

@media (max-width: 600px) {
  .tab-btn {
    padding: var(--space-sm) var(--space-md);
    font-size: var(--font-xs);
  }
  
  .tab-icon {
    font-size: var(--font-lg);
  }
  
  .tab-text {
    display: none;
  }
  
  .chat-messages {
    padding: var(--space-md);
  }
  
  .chat-input-area {
    padding: var(--space-md);
  }
}

/* ========== 会话详情模态框样式 ========== */
.session-detail-modal :deep(.ant-modal-content) {
  border-radius: var(--radius-lg);
  overflow: hidden;
  box-shadow: var(--shadow-xl);
}

.session-detail-modal :deep(.ant-modal-close) {
  z-index: 1001;
  top: var(--space-sm);
  right: var(--space-sm);
  width: 28px;
  height: 28px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(255, 255, 255, 0.2);
  border-radius: 50%;
  transition: all var(--transition-fast) ease;
  color: white;
}

.session-detail-modal :deep(.ant-modal-close:hover) {
  background: rgba(255, 255, 255, 0.35);
  transform: rotate(90deg);
}

.session-detail-container {
  display: flex;
  flex-direction: column;
  max-height: 75vh;
}

/* 模态框头部 - 紧凑美观版 */
.detail-header {
  background: var(--color-primary);
  color: var(--text-inverse);
  padding: var(--space-lg) var(--space-2xl);
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
}

.detail-title {
  display: flex;
  align-items: center;
  gap: var(--space-md);
  margin-bottom: 4px;
}

.title-icon {
  font-size: var(--font-2xl);
}

.detail-title h2 {
  margin: 0;
  font-size: var(--font-lg);
  font-weight: var(--weight-semibold);
  color: var(--text-inverse);
  line-height: 1.3;
  max-width: calc(100% - 40px);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.detail-meta {
  display: flex;
  gap: var(--space-lg);
  opacity: 0.85;
  font-size: var(--font-xs);
  margin-top: 2px;
}

.meta-item {
  display: flex;
  align-items: center;
  gap: 3px;
}

/* 加载状态 */
.detail-loading {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: var(--space-3xl) var(--space-lg);
  gap: var(--space-lg);
  min-height: 300px;
}

.detail-loading p {
  color: var(--text-secondary);
  margin: 0;
  font-size: var(--font-md);
}

/* 消息列表容器（带滚动条）- 扩展版 */
.detail-messages {
  flex: 1;
  overflow-y: auto;
  padding: var(--space-lg) var(--space-2xl);
  background: var(--bg-container);
  max-height: 58vh; /* 增加最大高度，为对话记录预留更多空间 */
  
  /* 自定义滚动条样式 */
  scrollbar-width: thin;
  scrollbar-color: var(--border-default) transparent;
}

.detail-messages::-webkit-scrollbar {
  width: 6px;
}

.detail-messages::-webkit-scrollbar-track {
  background: transparent;
}

.detail-messages::-webkit-scrollbar-thumb {
  background-color: var(--border-default);
  border-radius: var(--radius-sm);
}

.detail-messages::-webkit-scrollbar-thumb:hover {
  background-color: var(--border-dark);
}

/* 单条消息样式 */
.detail-message {
  display: flex;
  gap: 14px;
  margin-bottom: var(--space-xl);
  animation: fadeInUp 0.3s ease-out;
}

@keyframes fadeInUp {
  from {
    opacity: 0;
    transform: translateY(10px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.message-avatar-small {
  width: 38px;
  height: 38px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: var(--font-lg);
  flex-shrink: 0;
  box-shadow: var(--shadow-sm);
}

.detail-message.user .message-avatar-small {
  background: var(--color-primary);
  color: var(--text-inverse);
}

.detail-message.ai .message-avatar-small {
  background: var(--bg-active);
}

.message-body {
  flex: 1;
  min-width: 0;
}

.message-role-label {
  font-weight: var(--weight-semibold);
  font-size: var(--font-sm);
  color: var(--text-primary);
  margin-bottom: var(--space-sm);
  display: flex;
  align-items: center;
  gap: var(--space-md);
}

.message-time-small {
  font-weight: var(--weight-normal);
  font-size: var(--font-xs);
  color: var(--text-disabled);
}

.message-content-detail {
  background: var(--bg-card);
  padding: var(--space-md) var(--space-lg);
  border-radius: var(--radius-lg);
  line-height: 1.7;
  color: var(--text-secondary);
  font-size: var(--font-md);
  word-wrap: break-word;
  overflow-wrap: break-word;
  box-shadow: var(--shadow-sm);
  border: 1px solid var(--border-light);
}

.detail-message.user .message-content-detail {
  background: var(--color-primary);
  color: var(--text-inverse);
  border: none;
}

.detail-message.user .message-role-label {
  color: var(--color-primary);
}

/* 空状态 */
.detail-empty {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 200px;
  padding: var(--space-3xl);
}

/* 模态框底部操作栏 - 紧凑版 */
.detail-footer {
  display: flex;
  justify-content: flex-end;
  gap: var(--space-md);
  padding: var(--space-sm) var(--space-2xl) var(--space-lg);
  background: var(--bg-card);
  border-top: 1px solid var(--border-light);
  margin-top: auto; /* 推到底部 */
}

.detail-footer .btn {
  padding: var(--space-xs) var(--space-lg);
  font-size: var(--font-md);
  font-weight: var(--weight-medium);
  border-radius: var(--radius-md);
  transition: all var(--transition-fast);
}

.detail-footer .btn-secondary {
  background: var(--bg-container);
  color: var(--text-secondary);
  border: 1px solid var(--border-default);
}

.detail-footer .btn-secondary:hover {
  background: var(--bg-hover);
  border-color: var(--border-dark);
}

.detail-footer .btn-primary {
  background: var(--color-primary);
  color: var(--text-inverse);
  border: none;
  box-shadow: var(--shadow-sm);
}

.detail-footer .btn-primary:hover {
  transform: translateY(-1px);
  box-shadow: var(--shadow-md);
}

/* ==================== 引用组件样式 ==================== */

/* 引用链接标签（内联）- 按文档规范使用 .ref-link */
:deep(.ref-link) {
  display: inline;
  cursor: pointer;
  color: var(--color-primary, #1677ff);
  font-size: var(--font-sm);
  text-decoration: underline;
  text-decoration-style: dotted;
  vertical-align: baseline;
  margin: 0 2px;
  white-space: nowrap;
}

:deep(.ref-link:hover) {
  color: var(--color-primary-hover, #4096ff);
  text-decoration: underline;
  text-decoration-style: solid;
}

/* 引用来源卡片 */
.references-card {
  margin-top: var(--space-md);
  background: var(--bg-container);
  border: 1px solid var(--border-light);
  border-radius: var(--radius-lg);
  overflow: hidden;
  transition: all var(--transition-normal);
}

.references-card:hover {
  border-color: var(--border-medium);
  box-shadow: var(--shadow-sm);
}

.references-header {
  display: flex;
  align-items: center;
  gap: var(--space-md);
  padding: var(--space-sm) var(--space-md);
  cursor: pointer;
  user-select: none;
  transition: background var(--transition-fast);
}

.references-header:hover {
  background: var(--bg-hover);
}

.ref-icon {
  font-size: var(--font-lg);
}

.ref-title {
  flex: 1;
  font-weight: var(--weight-semibold);
  font-size: var(--font-md);
  color: var(--text-primary);
}

.ref-count {
  font-size: var(--font-xs);
  color: var(--text-tertiary);
  background: var(--bg-active);
  padding: var(--space-2xs) var(--space-md);
  border-radius: var(--radius-xl);
}

.toggle-icon {
  font-size: var(--font-xs);
  color: var(--text-disabled);
  transition: transform var(--transition-normal);
}

.references-card.expanded .toggle-icon {
  transform: rotate(180deg);
}

/* 引用列表 */
.references-list {
  padding: 0 var(--space-md) var(--space-md);
  border-top: 1px solid var(--border-light);
}

/* 引用分组容器 */
.ref-group {
  margin-top: var(--space-sm);
  background: var(--bg-card);
  border: 1px solid var(--border-light);
  border-radius: var(--radius-md);
  overflow: hidden;
}

.ref-group:first-child {
  margin-top: 10px;
}

.ref-group-header {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  padding: var(--space-sm) var(--space-md);
  background: var(--bg-container);
  border-bottom: 1px solid var(--border-light);
  cursor: pointer;
  user-select: none;
  transition: background-color var(--transition-fast);
}

.ref-group-header:hover {
  background: var(--bg-hover);
}

.group-collapse-icon {
  font-size: 11px;
  color: var(--text-secondary);
  transition: transform 0.2s ease;
  flex-shrink: 0;
}

.ref-group-filename {
  font-weight: var(--weight-semibold);
  font-size: var(--font-sm);
  color: var(--text-primary);
}

.ref-group-count {
  font-size: var(--font-xs);
  color: var(--text-tertiary);
  margin-left: auto;
}

/* 折叠状态：隐藏底部边框 */
.group-collapsed .ref-group-header {
  border-bottom: none;
}

/* 引用项容器 */
.ref-group-body {
  overflow: hidden;
}

.ref-group .reference-item {
  cursor: pointer;
  padding: var(--space-sm) var(--space-md);
  border-bottom: 1px solid var(--border-light);
  background: var(--bg-card);
  transition: all var(--transition-fast);
  margin-top: 0;
}

.ref-group .reference-item:last-child {
  border-bottom: none;
}

.ref-group .reference-item:hover {
  background: var(--bg-hover);
}

.ref-group .reference-item.highlighted {
  background: var(--bg-hover);
  box-shadow: inset 0 0 0 2px rgba(33, 37, 41, 0.1);
}

.content-paragraph {
  line-height: 1.7;
  margin-bottom: 8px;
}

.content-paragraph:last-child {
  margin-bottom: 0;
}

.chat-image-item {
  position: relative;
  border-radius: 8px;
  overflow: hidden;
  background: #fafafa;
  border: 1px solid rgba(0,0,0,0.06);
  transition: border-color 0.2s, box-shadow 0.2s;
  cursor: pointer;
  max-width: 360px;
  display: inline-block;
  margin: 10px 0;
}

.chat-image-item:hover {
  border-color: rgba(0,0,0,0.15);
  box-shadow: 0 2px 8px rgba(0,0,0,0.08);
}

.chat-image-item img {
  display: block;
  max-width: 100%;
  max-height: 280px;
  object-fit: contain;
  border-radius: 8px;
}

.chat-image-info {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 6px 10px;
  background: rgba(0,0,0,0.02);
  border-top: 1px solid rgba(0,0,0,0.04);
}

.chat-image-desc {
  font-size: 12px;
  color: var(--text-secondary);
  line-height: 1.4;
  flex: 1;
}

.chat-image-source {
  font-size: 11px;
  color: var(--text-tertiary);
  white-space: nowrap;
  display: flex;
  align-items: center;
  gap: 3px;
}

.ref-item-header {
  display: flex;
  align-items: center;
  gap: var(--space-md);
  margin-bottom: var(--space-xs);
}

.ref-file-icon {
  font-size: var(--font-lg);
}

.ref-location {
  font-size: var(--font-xs);
  color: var(--text-tertiary);
}

.ref-score {
  font-size: var(--font-xs);
  color: var(--color-primary);
  font-weight: var(--weight-semibold);
  margin-left: auto;
}

.ref-context {
  font-size: var(--font-xs);
  color: var(--text-secondary);
  line-height: 1.5;
  padding: var(--space-xs) var(--space-md);
  background: var(--bg-container);
  border-left: 3px solid var(--color-primary);
  border-radius: var(--radius-sm);
  font-style: italic;
  overflow: hidden;
  text-overflow: ellipsis;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
}

/* 悬浮提示卡片 */
.reference-tooltip {
  position: fixed;
  min-width: 220px;
  max-width: 320px;
  background: var(--bg-card);
  border: 1px solid var(--border-light);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  padding: var(--space-md) var(--space-lg);
  z-index: 9999;
  animation: tooltipFadeIn 0.2s ease-out;
  pointer-events: none;
}

@keyframes tooltipFadeIn {
  from {
    opacity: 0;
    transform: translateY(-4px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.tooltip-header {
  display: flex;
  align-items: center;
  gap: var(--space-md);
  margin-bottom: var(--space-xs);
}

.tooltip-file-icon {
  font-size: var(--font-lg);
}

.tooltip-filename {
  font-weight: var(--weight-semibold);
  font-size: var(--font-sm);
  color: var(--text-primary);
}

.tooltip-location {
  font-size: var(--font-xs);
  color: var(--text-tertiary);
  margin-bottom: var(--space-xs);
  padding-left: 26px;
}

.tooltip-hint {
  font-size: var(--font-xs);
  color: var(--text-disabled);
  text-align: center;
  margin-top: var(--space-xs);
  padding-top: var(--space-xs);
  border-top: 1px solid var(--border-light);
}

/* 展开动画 */
.expand-enter-active,
.expand-leave-active {
  transition: all var(--transition-normal) ease;
  overflow: hidden;
}

.expand-enter-from,
.expand-leave-to {
  opacity: 0;
  max-height: 0;
  padding-top: 0;
  padding-bottom: 0;
}

.expand-enter-to,
.expand-leave-from {
  opacity: 1;
  max-height: 500px;
}

/* 分组折叠动画 */
.group-expand-enter-active,
.group-expand-leave-active {
  transition: all 0.25s ease;
  overflow: hidden;
}

.group-expand-enter-from,
.group-expand-leave-to {
  opacity: 0;
  max-height: 0;
}

.group-expand-enter-to,
.group-expand-leave-from {
  opacity: 1;
  max-height: 800px;
}

/* 响应式设计 */
@media (max-width: 768px) {
  :deep(.ref-link) {
    font-size: var(--font-xs);
  }
  
  .references-card {
    margin-top: var(--space-sm);
  }
  
  .references-header {
    padding: var(--space-sm) var(--space-md);
  }
  
  .ref-title {
    font-size: var(--font-xs);
  }
  
  .ref-group .reference-item {
    padding: var(--space-sm) var(--space-md);
  }
  
  .ref-group-filename {
    font-size: var(--font-xs);
  }
  
  /* 移动端隐藏悬浮提示，使用折叠列表 */
  .reference-tooltip {
    display: none !important;
  }
  
  /* 移动端默认展开引用列表 */
  .references-list {
    max-height: 200px;
    overflow-y: auto;
  }

  .content-paragraph {
    margin-bottom: 6px;
  }

  .chat-image-item {
    max-width: 100%;
  }

  .chat-image-item img {
    max-height: 200px;
  }

  .chat-image-info {
    padding: 5px 8px;
    flex-direction: column;
    align-items: flex-start;
  }

  .chat-image-desc {
    font-size: 11px;
  }
}

/* ========== 图片展示相关样式 ========== */
.images-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: var(--space-md);
  margin-top: var(--space-md);
}

.image-item {
  display: flex;
  flex-direction: column;
  background: var(--bg-card);
  border: 1px solid var(--border-light);
  border-radius: var(--radius-lg);
  overflow: hidden;
  transition: all var(--transition-normal);
}

.image-item:hover {
  border-color: var(--border-medium);
  box-shadow: var(--shadow-md);
  transform: translateY(-2px);
}

.image-wrapper {
  position: relative;
  width: 100%;
  padding-top: 75%;
  background: var(--bg-container);
  cursor: pointer;
  overflow: hidden;
}

.image-wrapper .image-error-placeholder {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--text-tertiary, #adb5bd);
  font-size: 14px;
  background: var(--bg-secondary, #f5f5f5);
}

.image-wrapper img {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  transition: transform var(--transition-normal);
}

.image-item:hover .image-wrapper img {
  transform: scale(1.05);
}

.image-overlay {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: rgba(0, 0, 0, 0.6);
  display: flex;
  align-items: center;
  justify-content: center;
  color: white;
  opacity: 0;
  transition: opacity var(--transition-fast);
  gap: var(--space-xs);
  font-size: var(--font-sm);
}

.image-wrapper:hover .image-overlay {
  opacity: 1;
}

.image-info {
  padding: var(--space-sm);
  flex: 1;
}

.image-caption {
  font-size: var(--font-sm);
  color: var(--text-primary);
  margin-bottom: var(--space-xs);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.image-meta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xs);
  font-size: var(--font-xs);
  color: var(--text-secondary);
}

.image-meta span {
  display: inline-flex;
  align-items: center;
  gap: 3px;
}

.image-page,
.image-type,
.image-score {
  background: var(--bg-active);
  padding: 2px 6px;
  border-radius: var(--radius-sm);
}

.image-score {
  background: var(--color-primary-light);
  color: var(--color-primary);
  font-weight: var(--weight-medium);
}

.image-actions {
  padding: var(--space-xs) var(--space-sm) var(--space-sm);
  border-top: 1px solid var(--border-light);
}

/* 图片预览模态框 */
.image-modal-overlay {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(0, 0, 0, 0.85);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 9999;
  padding: var(--space-lg);
  animation: fadeIn var(--transition-fast) ease;
}

@keyframes fadeIn {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

.image-modal-content {
  background: var(--bg-card);
  border-radius: var(--radius-xl);
  max-width: 90vw;
  max-height: 90vh;
  display: flex;
  flex-direction: column;
  box-shadow: var(--shadow-xl);
  animation: slideUp var(--transition-normal) ease;
}

@keyframes slideUp {
  from {
    opacity: 0;
    transform: translateY(20px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.image-modal-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: var(--space-md) var(--space-lg);
  border-bottom: 1px solid var(--border-light);
}

.image-modal-header h3 {
  margin: 0;
  font-size: var(--font-lg);
  font-weight: var(--weight-semibold);
  color: var(--text-primary);
}

.modal-close-btn {
  background: none;
  border: none;
  cursor: pointer;
  padding: var(--space-xs);
  border-radius: var(--radius-sm);
  color: var(--text-secondary);
  transition: all var(--transition-fast);
  display: flex;
  align-items: center;
  justify-content: center;
}

.modal-close-btn:hover {
  background: var(--bg-hover);
  color: var(--text-primary);
}

.image-modal-body {
  flex: 1;
  overflow: auto;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-lg);
  background: var(--bg-container);
}

.preview-image {
  max-width: 100%;
  max-height: 70vh;
  object-fit: contain;
  border-radius: var(--radius-md);
}

.image-modal-footer {
  padding: var(--space-md) var(--space-lg);
  border-top: 1px solid var(--border-light);
}

.image-details {
  margin-bottom: var(--space-md);
}

.image-detail-item {
  font-size: var(--font-sm);
  color: var(--text-secondary);
  margin-bottom: var(--space-xs);
  line-height: 1.6;
}

.image-detail-item.full-description {
  color: var(--text-primary);
}

.image-meta-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-md);
  font-size: var(--font-xs);
  color: var(--text-tertiary);
  margin-top: var(--space-sm);
  padding-top: var(--space-sm);
  border-top: 1px solid var(--border-light);
}

.image-meta-row span {
  display: inline-flex;
  align-items: center;
  gap: 4px;
}

.image-meta-row .detail-item {
  background: var(--bg-active);
  padding: 3px 8px;
  border-radius: var(--radius-sm);
}

.image-modal-actions {
  display: flex;
  justify-content: flex-end;
  gap: var(--space-md);
}

/* 响应式图片展示 */
@media (max-width: 768px) {
  .images-grid {
    grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
    gap: var(--space-sm);
  }

  .content-paragraph {
    margin-bottom: 6px;
  }

  .chat-image-item {
    max-width: 100%;
  }

  .chat-image-item img {
    max-height: 200px;
  }

  .chat-image-info {
    padding: 8px 10px;
    flex-direction: column;
    align-items: flex-start;
  }

  .image-modal-content {
    max-width: 95vw;
    max-height: 95vh;
  }

  .image-modal-header,
  .image-modal-footer {
    padding: var(--space-sm) var(--space-md);
  }
}
</style>
