<template>
  <a-modal
    :visible="props.visible"
    @cancel="emit('close')"
    :width="isFullscreen ? '100vw' : 1400"
    :footer="null"
    destroyOnClose
    :class="['ai-assistant-modal', { fullscreen: isFullscreen }]"
  >
    <template #title>
      <div :class="['draggable-title', { 'no-drag': isFullscreen }]" @mousedown="onDragStart">"通安智风"大模型</div>
    </template>
    <!-- 最大化/还原按钮 -->
    <div class="maximize-btn" @click="toggleFullscreen">
      <div :class="['maximize-icon', { minimized: isFullscreen }]"></div>
    </div>
    <!-- 收起/展开按钮 -->
    <div class="collapse-btn" @click="togglePanel">
      <div :class="['collapse-icon', { collapsed: panelCollapsed }]"></div>
    </div>
    <div class="ai-container">
      <!-- 左侧任务列表 -->
      <div :class="['task-panel-wrapper', { collapsed: panelCollapsed }]">
        <TaskListPanel
          :taskList="taskList"
          :currentTaskId="currentTaskId"
          @switch-task="switchTask"
          @create-task="createNewTask"
          @delete-task="handleDeleteTask"
        />
      </div>

      <!-- 中间聊天区域 -->
      <div class="chat-panel">
        <!-- 顶部任务选择器 -->
        <div class="chat-header">
          <template v-if="panelCollapsed">
            <div :class="['nav-btn', { disabled: !canGoPrev }]" @click="goToPrev">
              <div class="nav-icon prev-icon"></div>
            </div>
            <div :class="['nav-btn', { disabled: !canGoNext }]" @click="goToNext">
              <div class="nav-icon next-icon"></div>
            </div>
            <div class="nav-btn" @click="createNewTask">
              <div class="nav-icon add-icon"></div>
            </div>
          </template>
          <span v-if="currentTask?.sessionId" class="chat-header-title">{{ currentTask.name }}</span>
          <div v-if="currentWordUrl" class="upload-bg" @click="downloadWordReport">
            <div class="download-icon"></div>
          </div>
        </div>

        <!-- 附件列表面板 -->
        <FileListPanel :visible="showFileList" :files="getCurrentTaskFiles()" @close="showFileList = false" @file-click="openFilePreview" />

        <!-- 消息列表 -->
        <ChatMessages ref="chatMessagesRef" :messages="messages" @file-preview="openFilePreview" @download-word="downloadWordFile" />

        <!-- 输入区域 -->
        <ChatInputArea
          ref="chatInputRef"
          v-model="inputMessage"
          :pendingFile="pendingFile"
          :loading="loading"
          @send="handleSendMessage"
          @file-upload="triggerFileUpload"
          @file-change="handleFileUpload"
          @remove-pending-file="removePendingFile"
        />
      </div>

      <!-- 右侧文件预览面板 -->
      <div v-if="previewFile" class="right-panel">
        <FilePreviewPanel :file="previewFile" @close="removePreviewFile" />
      </div>
    </div>
  </a-modal>
</template>

<script setup lang="ts">
  import { ref, nextTick, watch, computed, onMounted, onBeforeUnmount } from 'vue';
  import dayjs from 'dayjs';
  import { unifiedStream, reviewPdfStream, getHistoryList, getDetail, deleteSession } from '../api';
  import { message, Modal } from 'ant-design-vue';
  import { isPdfFile } from './chatModal/utils';
  import TaskListPanel from './chatModal/TaskListPanel.vue';
  import FileListPanel from './chatModal/FileListPanel.vue';
  import ChatMessages from './chatModal/ChatMessages.vue';
  import ChatInputArea from './chatModal/ChatInputArea.vue';
  import FilePreviewPanel from './chatModal/FilePreviewPanel.vue';
  import type { AttachedFile, Message, Task } from './chatModal/types';

  interface Props {
    visible: boolean;
  }

  interface Emits {
    (e: 'close'): void;
  }

  const props = defineProps<Props>();
  const emit = defineEmits<Emits>();

  const taskList = ref<Task[]>([]);

  // 从接口获取会话列表
  const fetchSessionList = async () => {
    try {
      const res = await getHistoryList({});
      const sessions = res?.sessions || res?.data || [];
      if (Array.isArray(sessions)) {
        const apiTasks: Task[] = sessions.map((item: { session_id: string; title: string; updated_at: string }) => ({
          id: item.session_id,
          name: item.title || '未命名会话',
          sessionId: item.session_id,
          messages: [],
          attachedFiles: [],
          updatedAt: item.updated_at,
        }));

        const mergedTasks = apiTasks.map((apiTask) => {
          const existing = taskList.value.find((t) => t.id === apiTask.id);
          if (existing) {
            return {
              ...apiTask,
              messages: existing.messages,
              attachedFiles: existing.attachedFiles,
              wordUrl: existing.wordUrl,
            };
          }
          return apiTask;
        });

        const localOnlyTasks = taskList.value.filter((t) => !t.sessionId && !apiTasks.find((a) => a.id === t.id));

        taskList.value = [...localOnlyTasks, ...mergedTasks];
        return mergedTasks;
      }
    } catch (error) {
      console.error('获取会话列表失败:', error);
    }
    return [];
  };

  const currentTaskId = ref('');
  const chatInputRef = ref<InstanceType<typeof ChatInputArea>>();
  const chatMessagesRef = ref<InstanceType<typeof ChatMessages>>();

  const previewFile = ref<AttachedFile | null>(null);
  const showFileList = ref(false);
  const pendingFile = ref<AttachedFile | null>(null);

  const messages = ref<Message[]>([]);

  const panelCollapsed = ref(false);
  const togglePanel = () => {
    panelCollapsed.value = !panelCollapsed.value;
  };

  const isFullscreen = ref(false);
  const toggleFullscreen = () => {
    isFullscreen.value = !isFullscreen.value;
    nextTick(() => {
      const wraps = document.querySelectorAll<HTMLElement>('.zxm-modal-wrap, .ant-modal-wrap, [class*="modal-wrap"]');
      wraps.forEach((wrap) => {
        if (isFullscreen.value) {
          wrap.style.position = '';
          wrap.style.left = '';
          wrap.style.top = '0';
          wrap.style.right = '';
          wrap.style.bottom = '';
          wrap.style.width = '';
          wrap.style.height = '';
          wrap.style.margin = '0';
          wrap.style.padding = '0';
          wrap.style.transform = '';
        } else {
          wrap.style.position = '';
          wrap.style.left = '';
          wrap.style.top = '';
          wrap.style.right = '';
          wrap.style.bottom = '';
          wrap.style.width = '';
          wrap.style.height = '';
          wrap.style.margin = '';
          wrap.style.padding = '';
          wrap.style.transform = '';
        }
      });
      draggedEl = null;
      if (!isFullscreen.value) {
        centerModal();
      }
    });
  };

  const inputMessage = ref('');
  const loading = ref(false);

  const getCurrentSessionId = (): string => {
    const currentTask = taskList.value.find((t) => t.id === currentTaskId.value);
    return currentTask?.sessionId || '';
  };

  const historyLoadedTasks = ref<Set<string>>(new Set());

  const setCurrentSessionId = (sessionId: string) => {
    const currentTask = taskList.value.find((t) => t.id === currentTaskId.value);
    if (currentTask && currentTask.id !== sessionId) {
      taskList.value = taskList.value.filter((t) => t.id !== sessionId);
      currentTask.id = sessionId;
      currentTask.sessionId = sessionId;
      currentTaskId.value = sessionId;
      historyLoadedTasks.value.add(sessionId);
    }
  };

  const switchTask = async (taskId: string) => {
    const currentTask = taskList.value.find((t) => t.id === currentTaskId.value);
    if (currentTask) {
      currentTask.messages = [...messages.value];
    }

    currentTaskId.value = taskId;
    const newTask = taskList.value.find((t) => t.id === taskId);
    if (!newTask) return;

    if (newTask.sessionId) {
      taskList.value = taskList.value.filter((t) => t.sessionId);
    }

    inputMessage.value = '';
    pendingFile.value = null;
    previewFile.value = null;
    showFileList.value = false;

    if (newTask.sessionId && !historyLoadedTasks.value.has(taskId)) {
      loading.value = true;
      try {
        const res = await getDetail(newTask.sessionId);
        const messages = res?.messages || (Array.isArray(res) ? res : res?.data);
        if (Array.isArray(messages)) {
          newTask.messages = transformHistoryToMessages(messages);
        }
        historyLoadedTasks.value.add(taskId);
      } catch (error) {
        console.error('加载历史会话失败:', error);
      } finally {
        loading.value = false;
      }
    }

    messages.value = newTask.messages && newTask.messages.length > 0 ? [...newTask.messages] : getDefaultMessages();

    await nextTick();
    chatMessagesRef.value?.forceScrollToBottom();
  };

  const createNewTask = () => {
    const existingBlank = taskList.value.find((t) => !t.sessionId);
    if (existingBlank) {
      switchTask(existingBlank.id);
      return;
    }

    const newTask: Task = {
      id: `task-${Date.now()}`,
      name: '新任务',
      sessionId: '',
      messages: getDefaultMessages(),
      attachedFiles: [],
    };

    taskList.value.unshift(newTask);
    switchTask(newTask.id);
  };

  const handleDeleteTask = (task: Task, _index: number) => {
    Modal.confirm({
      title: '提示',
      content: '是否删除该对话？',
      okText: '确认',
      cancelText: '取消',
      wrapClassName: 'delete-task-confirm-modal',
      onOk: async () => {
        try {
          if (task.sessionId) {
            await deleteSession(task.sessionId);
          }
          const realIndex = taskList.value.findIndex((t) => t.id === task.id);
          if (realIndex !== -1) {
            taskList.value.splice(realIndex, 1);
          }
          if (currentTaskId.value === task.id) {
            if (taskList.value.length > 0) {
              switchTask(taskList.value[0].id);
            } else {
              messages.value = [];
              currentTaskId.value = '';
            }
          }
          message.success('删除成功');
        } catch {
          message.error('删除失败');
        }
      },
    });
  };

  const getDefaultMessages = (): Message[] => {
    return [];
  };

  const normalizeLineBreaks = (str: string): string => {
    if (!str) return '';
    return str.replace(/\\n/g, '\n').replace(/\r\n/g, '\n');
  };

  const transformHistoryToMessages = (data: Array<{ role?: string; type?: string; content: string; created_at?: string }>): Message[] => {
    const messages: Message[] = [];

    for (const item of data) {
      const normalizedContent = normalizeLineBreaks(item.content);
      const time = item.created_at ? dayjs(item.created_at).format('HH:mm') : '';
      const createdAt = item.created_at || '';

      // 新格式：role 字段 (user/assistant)
      if (item.role === 'user') {
        messages.push({ type: 'user', content: normalizedContent, time, createdAt });
      } else if (item.role === 'assistant') {
        messages.push({ type: 'ai', content: normalizedContent, time, createdAt });
      }
      // 兼容旧格式：type 字段
      else if (item.type === 'user_message') {
        messages.push({ type: 'user', content: normalizedContent, time, createdAt });
      } else if (item.type === 'model_thinking') {
        const lastAiMsg = messages[messages.length - 1];
        if (lastAiMsg && lastAiMsg.type === 'ai') {
          lastAiMsg.content += normalizedContent;
          lastAiMsg.content = lastAiMsg.content.replace(/\|\|\|SPLIT_CONTENT\|\|\|/g, '');
        } else {
          messages.push({ type: 'ai', content: normalizedContent, time, createdAt });
        }
      }
    }

    return messages;
  };

  const triggerFileUpload = () => {
    chatInputRef.value?.fileInputRef?.click();
  };

  const handleFileUpload = async (event: Event) => {
    const target = event.target as HTMLInputElement;
    const file = target.files?.[0];
    if (!file) return;

    if (!isPdfFile(file.name)) {
      message.error('仅支持 PDF 文件格式');
      target.value = '';
      return;
    }

    try {
      const newFile: AttachedFile = {
        id: `file-${Date.now()}`,
        name: file.name,
        size: file.size,
        type: file.type,
        uploadTime: dayjs().format('YYYY-MM-DD HH:mm'),
        originalFile: file,
      };

      const reader = new FileReader();
      reader.onload = (e) => {
        newFile.preview = e.target?.result as string;
      };
      reader.readAsDataURL(file);

      pendingFile.value = newFile;
      message.success(`文件 "${file.name}" 已添加，点击发送按钮提交`);
    } catch (error) {
      console.error('文件处理失败:', error);
      message.error('文件处理失败，请重试');
    }

    target.value = '';
  };

  const removePendingFile = () => {
    pendingFile.value = null;
  };

  const removePreviewFile = () => {
    previewFile.value = null;
  };

  const openFilePreview = (file: AttachedFile) => {
    previewFile.value = file;

    const msgIndex = messages.value.findIndex((m) => m.attachedFile?.id === file.id);
    if (msgIndex !== -1) {
      nextTick(() => {
        const messageElements = chatMessagesRef.value?.messagesRef?.querySelectorAll('.message-wrapper');
        if (messageElements && messageElements[msgIndex]) {
          (messageElements[msgIndex] as HTMLElement).scrollIntoView({
            behavior: 'smooth',
            block: 'center',
          });
        }
      });
    }
  };

  const getCurrentTaskFiles = (): AttachedFile[] => {
    const currentTask = taskList.value.find((t) => t.id === currentTaskId.value);
    return currentTask?.attachedFiles || [];
  };

  const currentTask = computed(() => taskList.value.find((t) => t.id === currentTaskId.value));

  const currentWordUrl = computed(() => {
    return currentTask.value?.wordUrl || '';
  });

  const currentTaskIndex = computed(() => taskList.value.findIndex((t) => t.id === currentTaskId.value));
  const canGoPrev = computed(() => currentTaskIndex.value > 0);
  const canGoNext = computed(() => currentTaskIndex.value >= 0 && currentTaskIndex.value < taskList.value.length - 1);

  const goToPrev = () => {
    if (!canGoPrev.value) return;
    const prevTask = taskList.value[currentTaskIndex.value - 1];
    if (prevTask) switchTask(prevTask.id);
  };

  const goToNext = () => {
    if (!canGoNext.value) return;
    const nextTask = taskList.value[currentTaskIndex.value + 1];
    if (nextTask) switchTask(nextTask.id);
  };

  const downloadWordReport = () => {
    const url = currentWordUrl.value;
    if (!url) return;
    const a = document.createElement('a');
    a.href = url;
    a.download = '';
    a.target = '_blank';
    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);
  };

  const downloadWordFile = (url: string) => {
    if (!url) return;
    const a = document.createElement('a');
    a.href = url;
    a.download = '';
    a.target = '_blank';
    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);
  };

  const handleSendMessage = async () => {
    const hasText = inputMessage.value.trim();
    const hasFile = pendingFile.value;

    if (!hasText) {
      message.warning('请输入消息内容');
      return;
    }
    if (loading.value) return;

    const userInput = inputMessage.value.trim();
    const fileToSend = pendingFile.value;

    inputMessage.value = '';
    pendingFile.value = null;

    try {
      loading.value = true;

      if (hasFile) {
        await sendWithAttachment(fileToSend!, userInput);
      } else {
        await sendTextOnly(userInput);
      }
    } catch (error) {
      console.error('发送消息失败:', error);
      message.error('发送失败，请重试');

      const lastMsg = messages.value[messages.value.length - 1];
      if (lastMsg && lastMsg.type === 'ai' && lastMsg.isLoading) {
        messages.value.pop();
      }

      const errorMsg: Message = {
        type: 'ai',
        content: '抱歉，请求失败，请稍后重试。',
        time: dayjs().format('HH:mm'),
      };
      messages.value.push(errorMsg);

      const currentTask = taskList.value.find((t) => t.id === currentTaskId.value);
      if (currentTask) {
        currentTask.messages = [...messages.value];
      }
    } finally {
      loading.value = false;
      await scrollToBottom();
    }
  };

  const extractAgentName = (toolName: string): string => {
    const nameMap: Record<string, string> = {
      form_review: '形式审查',
      verify_coal_face_ventilation: '采煤工作面需风量计算核验',
      verify_tunneling_face_ventilation: '掘进工作面需风量计算核验',
      verify_chamber_ventilation: '硐室需风量计算核验',
      verify_other_points_ventilation: '其他地点需风量计算核验',
      check_data_by_gas_report: '瓦斯数据一致性审查',
      check_data_by_face_design: '设计规程一致性审查',
      check_data_by_vent_report: '测风报表一致性审查',
      check_current_month_plan: '日期有效性审查',
      get_key_translate: '计算参数对照',
      calculate_ventilation: '需风量计算',
      get_ventilation_data: '需风量查询',
      recalculate_ventilation: '需风量重计算',
      get_plan_required_wind: '需风量提取',
      get_model_calculated_wind: '三维模型解算风量提取',
      get_sensor_wind: '巷道监测风量提取',
      merge_wind_by_location: '地点风量智能合并',
      check_wind_compliance: '风量检查',
    };

    return nameMap[toolName] || nameMap[toolName.toLowerCase()] || '智能分析助手';
  };

  // 累积所有步骤，保留完整历史
  const upsertStep = (steps: any[], step: any) => {
    steps.push(step);
  };

  const handleSSEMessage = (data: any, aiMsgIndex: number) => {
    const aiMsg = messages.value[aiMsgIndex];
    const normalizedContent = normalizeLineBreaks(data.content || '');

    // 初始化 thinkingSteps 数组
    if (!aiMsg.thinkingSteps) {
      aiMsg.thinkingSteps = [];
    }

    switch (data.type) {
      // token 流式内容 - 直接拼接到消息内容
      case 'token':
        if (normalizedContent) {
          aiMsg.content += normalizedContent;
        }
        break;

      // 系统进度消息 - 仅当 agent 不是 system 时才作为 thinking step
      case 'progress': {
        if (data.agent && data.agent !== 'system') {
          upsertStep(aiMsg.thinkingSteps!, {
            type: 'executing',
            agent: data.agent,
            message: data.message || '',
            timestamp: Date.now(),
          });
        }
        break;
      }

      // agent 开始处理
      case 'agent_start': {
        upsertStep(aiMsg.thinkingSteps!, {
          type: 'agent_start',
          agent: data.cn_agent || data.agent || '智能助手',
          message: data.message || '开始处理...',
          timestamp: Date.now(),
        });
        break;
      }

      // agent 任务列表更新（同一 agent 的 todos 更新已有条目，不重复添加）
      case 'agent_todos':
      case 'updated_todo_list': {
        const agentName = data.cn_agent || data.agent || '智能助手';
        const existingIdx = aiMsg.thinkingSteps!.findIndex((s) => s.type === 'updated_todo_list' && s.agent === agentName);
        if (existingIdx !== -1) {
          aiMsg.thinkingSteps![existingIdx].todos = data.todos || [];
          aiMsg.thinkingSteps![existingIdx].timestamp = Date.now();
        } else {
          aiMsg.thinkingSteps!.push({
            type: 'updated_todo_list',
            agent: agentName,
            todos: data.todos || [],
            message: data.message || '任务进度已更新',
            timestamp: Date.now(),
          });
        }
        break;
      }

      // agent 执行中（工具调用）
      case 'agent_executing':
      case 'executing': {
        upsertStep(aiMsg.thinkingSteps!, {
          type: 'executing',
          agent: data.cn_agent || data.agent || '智能助手',
          tool: (data.cn_tools || data.tools || []).join('、'),
          message: data.message || '',
          timestamp: Date.now(),
        });
        break;
      }

      // 工具结果
      case 'agent_tool_result':
      case 'tool_result': {
        upsertStep(aiMsg.thinkingSteps!, {
          type: 'tool_result',
          agent: data.cn_agent || data.agent || '智能助手',
          tool: data.cn_tool || data.tool || '',
          message: data.message || '执行完成',
          timestamp: Date.now(),
        });
        break;
      }

      // agent 完成
      case 'agent_done': {
        upsertStep(aiMsg.thinkingSteps!, {
          type: 'agent_done',
          agent: data.cn_agent || data.agent || '智能助手',
          message: `${(data.message || '审查完成').replace(/（\d+ms）/g, '')}${data.duration_ms ? `（${(data.duration_ms / 1000).toFixed(0)}s）` : ''}${data.progress ? ` [${data.progress}]` : ''}`,
          timestamp: Date.now(),
        });
        if (data.preview) {
          aiMsg.content += data.preview;
        }
        break;
      }

      // 旧格式兼容
      case 'model_thinking':
        if (normalizedContent) {
          aiMsg.content += normalizedContent;
          aiMsg.content = aiMsg.content.replace(/\|\|\|SPLIT_CONTENT\|\|\|/g, '');
        }
        break;

      case 'tool_select': {
        upsertStep(aiMsg.thinkingSteps!, {
          type: 'tool_call',
          agent: extractAgentName(normalizedContent),
          tool: normalizedContent,
          message: `选择工具：${normalizedContent}`,
          timestamp: Date.now(),
        });
        break;
      }

      case 'error':
        upsertStep(aiMsg.thinkingSteps!, {
          type: 'tool_result',
          agent: '系统',
          message: `❌ 执行失败：${normalizedContent}`,
          timestamp: Date.now(),
        });
        aiMsg.isLoading = false;
        break;

      case 'word_download':
        if (normalizedContent) {
          aiMsg.wordDownloadUrl = normalizedContent;
          const wordTask = taskList.value.find((t) => t.id === currentTaskId.value);
          if (wordTask) wordTask.wordUrl = normalizedContent;
        }
        break;

      case 'done':
        if (data.session_id) {
          aiMsg.sessionId = data.session_id;
        }
        if (data.duration_ms) {
          aiMsg.durationMs = data.duration_ms;
        }
        aiMsg.isLoading = false;
        break;

      case 'system':
        if (normalizedContent) {
          aiMsg.content += normalizedContent.replace(/\n/g, '  \n');
        }
        break;
    }

    const currentTask = taskList.value.find((t) => t.id === currentTaskId.value);
    if (currentTask) {
      currentTask.messages = [...messages.value];
    }
  };

  const parseSSEData = (chunk: string, aiMsgIndex: number) => {
    const lines = chunk.split('\n');

    for (const lineRaw of lines) {
      const line = lineRaw.trim();
      if (!line) continue;

      if (line.startsWith('data:')) {
        try {
          const jsonStr = line.substring(5).trim();
          const data = JSON.parse(jsonStr);
          handleSSEMessage(data, aiMsgIndex);
        } catch (error) {
          console.error('解析 SSE data 行失败:', error, line);
        }
        continue;
      }

      try {
        const data = JSON.parse(line);
        handleSSEMessage(data, aiMsgIndex);
        continue;
      } catch {
        // not json
      }

      handleSSEMessage({ type: 'model_thinking', content: line }, aiMsgIndex);
    }
  };

  const sendTextOnly = async (userInput: string) => {
    const now = dayjs();
    const userMsg: Message = {
      type: 'user',
      content: userInput,
      time: now.format('HH:mm'),
      createdAt: now.toISOString(),
    };
    messages.value.push(userMsg);
    await scrollToBottom();

    const aiMsgIndex = messages.value.length;
    const aiMsg: Message = {
      type: 'ai',
      content: '',
      time: now.format('HH:mm'),
      createdAt: now.toISOString(),
      isLoading: true,
      thinkingSteps: [],
      generateStartTime: Date.now(),
    };
    messages.value.push(aiMsg);
    await scrollToBottom();

    const result = await unifiedStream(
      {
        message: userInput,
        session_id: getCurrentSessionId() || undefined,
      },
      (chunk: string) => {
        parseSSEData(chunk, aiMsgIndex);
        scrollToBottom();
      }
    );

    if (result.session_id) {
      const taskBeforeUpdate = taskList.value.find((t) => t.id === currentTaskId.value);
      const isNewTask = taskBeforeUpdate && !taskBeforeUpdate.sessionId;

      setCurrentSessionId(result.session_id);

      if (isNewTask) {
        taskBeforeUpdate.name = userInput.substring(0, 20) + (userInput.length > 20 ? '...' : '');
      }
    }

    const currentTask = taskList.value.find((t) => t.id === currentTaskId.value);
    if (currentTask) {
      currentTask.messages = [...messages.value];
    }
  };

  const sendWithAttachment = async (file: AttachedFile, userInput: string) => {
    let currentTask = taskList.value.find((t) => t.id === currentTaskId.value);
    if (currentTask) {
      currentTask.attachedFiles.push(file);
    } else {
      throw new Error('未找到当前任务');
    }

    const now = dayjs();
    const userMsg: Message = {
      type: 'user',
      content: userInput || `上传了文件：${file.name}`,
      time: now.format('HH:mm'),
      createdAt: now.toISOString(),
      attachedFile: file,
    };
    messages.value.push(userMsg);
    await scrollToBottom();

    const aiMsgIndex = messages.value.length;
    const aiMsg: Message = {
      type: 'ai',
      content: '',
      time: now.format('HH:mm'),
      createdAt: now.toISOString(),
      isLoading: true,
      thinkingSteps: [],
      generateStartTime: Date.now(),
    };
    messages.value.push(aiMsg);
    await scrollToBottom();

    if (!file.originalFile) {
      throw new Error('缺少原始文件对象');
    }

    const result = await reviewPdfStream(
      {
        file: file.originalFile!,
        session_id: getCurrentSessionId() || undefined,
        message: userInput || undefined,
      },
      (chunk: string) => {
        parseSSEData(chunk, aiMsgIndex);
        scrollToBottom();
      }
    );

    if (result.session_id) {
      setCurrentSessionId(result.session_id);
    }

    currentTask = taskList.value.find((t) => t.id === currentTaskId.value);
    if (currentTask) {
      currentTask.messages = [...messages.value];
    }
  };

  const scrollToBottom = async () => {
    await nextTick();
    if (!chatMessagesRef.value?.isAtBottom) return;
    const el = chatMessagesRef.value?.messagesRef;
    if (el) {
      el.scrollTop = el.scrollHeight;
    }
  };

  watch(
    () => props.visible,
    async (newVisible) => {
      if (!newVisible) {
        const currentTask = taskList.value.find((t) => t.id === currentTaskId.value);
        if (currentTask) {
          currentTask.messages = [...messages.value];
        }
      } else {
        await fetchSessionList();

        // 去除重复的空白任务，只保留一个
        taskList.value = taskList.value.filter((task, index) => {
          if (task.sessionId) return true;
          return index === taskList.value.findIndex((t) => !t.sessionId);
        });

        // 默认选中第一条历史记录
        const firstHistoryTask = taskList.value.find((t) => t.sessionId);

        let targetTask: Task;
        if (firstHistoryTask) {
          targetTask = firstHistoryTask;
        } else {
          // 没有历史记录时才创建空白新任务
          targetTask = {
            id: `task-${Date.now()}`,
            name: '新任务',
            sessionId: '',
            messages: getDefaultMessages(),
            attachedFiles: [],
          };
          taskList.value.unshift(targetTask);
        }

        currentTaskId.value = targetTask.id;

        // 如果是历史任务，加载其消息
        if (targetTask.sessionId && !historyLoadedTasks.value.has(targetTask.id)) {
          loading.value = true;
          try {
            const res = await getDetail(targetTask.sessionId);
            const messages = res?.messages || (Array.isArray(res) ? res : res?.data);
            if (Array.isArray(messages)) {
              targetTask.messages = transformHistoryToMessages(messages);
            }
            historyLoadedTasks.value.add(targetTask.id);
          } catch (error) {
            console.error('加载历史会话失败:', error);
          } finally {
            loading.value = false;
          }
        }

        messages.value = targetTask.messages && targetTask.messages.length > 0 ? [...targetTask.messages] : getDefaultMessages();

        previewFile.value = null;
        pendingFile.value = null;
        showFileList.value = false;
        inputMessage.value = '';

        await nextTick(() => {
          chatMessagesRef.value?.forceScrollToBottom();
        });
      }
    }
  );

  // 拖拽相关（使用普通变量避免 mousemove 触发 Vue 响应式导致卡顿）
  let isDragging = false;
  let dragStartX = 0;
  let dragStartY = 0;
  let modalStartLeft = 0;
  let modalStartTop = 0;
  let draggedEl: HTMLElement | null = null;

  const findModalWrap = (target: HTMLElement): HTMLElement | null =>
    target.closest<HTMLElement>('.zxm-modal-wrap, .ant-modal-wrap, [class*="modal-wrap"]');

  const queryModalWrap = (): HTMLElement | null => document.querySelector<HTMLElement>('.zxm-modal-wrap, .ant-modal-wrap, [class*="modal-wrap"]');

  const centerModal = () => {
    nextTick(() => {
      const wrap = queryModalWrap();
      if (wrap) {
        wrap.style.position = '';
        wrap.style.left = '';
        wrap.style.top = '';
        wrap.style.right = '';
        wrap.style.bottom = '';
        wrap.style.width = '';
        wrap.style.height = '';
        wrap.style.margin = '';
        wrap.style.transform = '';
      }
      draggedEl = null;
    });
  };

  const onDragStart = (e: MouseEvent) => {
    if (isFullscreen.value) return;
    draggedEl = findModalWrap(e.target as HTMLElement) || queryModalWrap();
    if (!draggedEl) return;

    isDragging = true;
    dragStartX = e.clientX;
    dragStartY = e.clientY;

    const rect = draggedEl.getBoundingClientRect();
    modalStartLeft = rect.left;
    modalStartTop = rect.top;

    draggedEl.style.position = 'fixed';
    draggedEl.style.left = `${rect.left}px`;
    draggedEl.style.top = `${rect.top}px`;
    draggedEl.style.margin = '0';
    draggedEl.style.transform = 'none';

    document.addEventListener('mousemove', onDragMove);
    document.addEventListener('mouseup', onDragEnd);
  };

  const onDragMove = (e: MouseEvent) => {
    if (!isDragging || !draggedEl) return;
    draggedEl.style.left = `${modalStartLeft + (e.clientX - dragStartX)}px`;
    draggedEl.style.top = `${modalStartTop + (e.clientY - dragStartY)}px`;
  };

  const onDragEnd = () => {
    isDragging = false;
    document.removeEventListener('mousemove', onDragMove);
    document.removeEventListener('mouseup', onDragEnd);
  };

  watch(
    () => props.visible,
    (newVisible) => {
      if (newVisible) {
        centerModal();
      }
    }
  );

  onMounted(async () => {
    // fetchSessionList moved to watch(visible) to avoid unnecessary API calls
  });

  onBeforeUnmount(() => {
    document.removeEventListener('mousemove', onDragMove);
    document.removeEventListener('mouseup', onDragEnd);
  });
</script>

<style scoped lang="less">
  .ai-container {
    display: flex;
    height: 700px;
    gap: 10px;
    overflow: hidden;
    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
  }

  .collapse-btn {
    position: absolute;
    top: 16px;
    left: 24px;
    z-index: 10;
    width: 32px;
    height: 32px;
    background: #1a6696;
    border-radius: 4px;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    transition: background 0.3s;

    &:hover {
      background: #1f7ab8;
    }

    .collapse-icon {
      width: 16px;
      height: 16px;
      background-image: var(--img-chat-collapse-icon);
      background-repeat: no-repeat;
      background-size: 100% 100%;

      &.collapsed {
        background-image: var(--img-chat-expand-icon);
      }
    }
  }

  .task-panel-wrapper {
    width: 300px;
    flex-shrink: 0;
    overflow: hidden;
    transition:
      width 0.35s cubic-bezier(0.4, 0, 0.2, 1),
      margin 0.35s cubic-bezier(0.4, 0, 0.2, 1),
      opacity 0.25s ease;

    &.collapsed {
      width: 0;
      margin: 0;
      opacity: 0;
    }
  }

  .chat-panel {
    flex: 1;
    display: flex;
    position: relative;
    flex-direction: column;
    background-image: var(--img-chat-panel-bg);
    background-repeat: no-repeat;
    background-size: 100% 100%;
    border-radius: 4px;
    overflow: hidden;
    min-width: 0;

    :deep(.chat-header) {
      display: flex;
      padding: 12px 20px;
      flex-shrink: 0;
      align-items: center;
      background: rgba(2, 53, 100, 0.3);

      .header-icon {
        width: 35px;
        height: 28px;
        background-image: var(--img-chat-header-icon);
        background-repeat: no-repeat;
        background-size: 100% 100%;
        margin-right: 10px;
      }

      .chat-header-title {
        font-size: 16px;
        color: #e0e6ed;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
        margin-left: 10px;
      }

      .nav-btn {
        width: 28px;
        height: 28px;
        display: flex;
        align-items: center;
        justify-content: center;
        cursor: pointer;
        border-radius: 4px;
        transition: background 0.2s;

        &:hover:not(.disabled) {
          background: rgba(10, 132, 255, 0.15);
        }

        &.disabled {
          opacity: 0.3;
          cursor: not-allowed;
        }

        .nav-icon {
          width: 16px;
          height: 16px;
          background-repeat: no-repeat;
          background-size: 100% 100%;
        }

        .prev-icon {
          background-image: var(--img-chat-prev-icon);
        }

        .next-icon {
          background-image: var(--img-chat-next-icon);
        }

        .add-icon {
          background-image: var(--img-chat-options-add-icon);
        }
      }

      .upload-bg {
        cursor: pointer;
        width: 36px;
        height: 36px;
        background-image: var(--img-chat-close-bg);
        background-repeat: no-repeat;
        background-size: 100% 100%;
        display: flex;
        align-items: center;
        justify-content: center;
        margin-left: auto;
        position: relative;
      }
      .download-icon {
        width: 20px;
        height: 20px;
        background-image: var(--img-chat-download-icon);
        background-repeat: no-repeat;
        background-size: 100% 100%;
      }
    }
  }

  .right-panel {
    width: calc(50% - 140px);
    height: 100%;
    display: flex;
    flex-direction: column;
    background-image: var(--img-chat-panel-bg);
    background-repeat: no-repeat;
    background-size: 100% 100%;
    border-radius: 4px;
    flex-shrink: 0;
  }

  .draggable-title {
    cursor: grab;
    user-select: none;

    &:active {
      cursor: grabbing;
    }

    &.no-drag {
      cursor: default;

      &:active {
        cursor: default;
      }
    }
  }
</style>

<style lang="less">
  .ai-assistant-modal {
    .zxm-modal-content {
      background-image: var(--img-chat-modal-bg);
      background-repeat: no-repeat;
      background-size: 100% 100%;
      background-color: unset !important;
      box-shadow: none;
      padding: 0;
      border: none !important;
    }
    .zxm-modal-close {
      background-image: var(--img-chat-close-bg);
      background-repeat: no-repeat;
      background-size: 100% 100%;
      top: 0px;
      right: 0px;
      width: 38px;
      height: 38px;
      .zxm-modal-close-x {
        width: 38px;
        height: 38px;
      }
      svg {
        width: 26px;
        height: 26px;
        margin-bottom: 4px;
      }
    }

    .maximize-btn {
      position: absolute;
      top: 0px;
      right: 56px;
      width: 38px;
      height: 38px;
      background-image: var(--img-chat-close-bg);
      background-repeat: no-repeat;
      background-size: 100% 100%;
      display: flex;
      align-items: center;
      justify-content: center;
      cursor: pointer;
      z-index: 10;

      .maximize-icon {
        width: 22px;
        height: 22px;
        background-image: var(--img-chat-maximize-icon);
        background-repeat: no-repeat;
        background-size: 100% 100%;

        &.minimized {
          background-image: var(--img-chat-minimize-icon);
        }
      }
    }

    &.fullscreen {
      top: 0 !important;
      padding: 0 !important;
      max-width: 100vw !important;
      margin: 0 !important;
      background-color: #09172c;

      .zxm-modal-wrap,
      .ant-modal-wrap {
        padding: 0 !important;
        margin: 0 !important;
        top: 0 !important;
      }

      .zxm-modal-content {
        width: 100vw !important;
        height: 100vh !important;
        max-width: 100vw !important;
        margin: 0;
        border-radius: 0;
        overflow: hidden;
        background-image: var(--img-chat-fs-modal-bg);
      }
      .zxm-modal-header {
        height: 90px;
        background-image: var(--img-chat-fs-header-bg) !important;
        background-repeat: no-repeat;
        background-size: 100% 100%;
        margin-bottom: 16px;
      }
      .no-drag {
        cursor: default;
        font-size: 32px !important;
      }
      .zxm-modal-title {
        height: 90px;
      }
      .zxm-modal-body {
        height: calc(100vh - 120px);
        overflow: hidden;
        padding: 0 24px 12px;
      }
      .ai-container {
        height: 100%;
        overflow: hidden;
      }
      .chat-panel {
        background-image: var(--img-chat-fs-panel-bg) !important;
      }
      .chat-header {
        padding: 0 32px;
        height: 50px;
      }
      .collapse-btn {
        top: 60px;
        background-image: var(--img-chat-fs-collapse-btn-bg) !important;
        background-repeat: no-repeat;
        background-size: 100% 100%;
      }
      .task-list-panel {
        padding: 20px;
        background-image: var(--img-chat-fs-task-panel-bg) !important;
        background-repeat: no-repeat !important;
        background-size: 100% 100% !important;
      }
      .options-area {
        gap: 12px;
      }
      .options-area-item {
        background-image: var(--img-chat-fs-options-item-bg) !important;
        background-repeat: no-repeat !important;
        background-size: 100% 100% !important;

        &.active,
        &:active {
          background-image: var(--img-chat-fs-options-item-active-bg) !important;
        }
        .options-img {
          background-image: none !important;
        }
      }
      .chat-header-title {
        height: 40px;
        line-height: 40px;
        position: relative;
        background-image: var(--img-chat-fs-header-title-bg) !important;
        background-repeat: no-repeat !important;
        background-size: auto 100% !important;
        padding: 0 28px;

        &::before {
          content: '';
          position: absolute;
          left: 0;
          top: 50%;
          transform: translateY(-50%);
          width: 40px;
          height: 40px;
          background-image: var(--img-chat-fs-header-title-icon);
          background-repeat: no-repeat;
          background-size: 100% 100%;
        }
      }
      .chat-input-area {
        margin: 24px;
      }
    }

    .zxm-modal-header {
      background-color: unset !important;
      background: unset;
      border: none !important;
      padding: 8px;
      backdrop-filter: unset;
      display: flex;
      justify-content: center;
    }
    .zxm-modal-title {
      width: 100%;
      height: 60px;
      display: flex;
      align-items: center;
      justify-content: center;

      .draggable-title {
        color: #fff;
        font-family: var(--vet-font-family);
        letter-spacing: 3px;
        font-size: 24px;
        font-weight: 100;
        line-height: 1.4;
      }
    }
    .zxm-modal-body {
      padding: 12px 24px;
    }
  }

  .delete-task-confirm-modal {
    .zxm-modal-confirm-title,
    .zxm-modal-confirm-content {
      color: #fff;
    }
  }
</style>
