<!-- 每个回话对应的聊天内容 -->
<script setup lang="ts">
import type { AnyObject } from 'typescript-api-pro';
import type { BubbleProps } from 'vue-element-plus-x/types/Bubble';
import type { BubbleListInstance } from 'vue-element-plus-x/types/BubbleList';
import type { FilesCardProps } from 'vue-element-plus-x/types/FilesCard';
import type { ThinkingStatus } from 'vue-element-plus-x/types/Thinking';
import { ArrowLeftBold, ArrowRightBold, Document, Loading } from '@element-plus/icons-vue';
import { ElIcon, ElMessage } from 'element-plus';
import { useHookFetch } from 'hook-fetch/vue';
import { computed, nextTick, ref, watch } from 'vue';
import { Sender } from 'vue-element-plus-x';
import { useRoute } from 'vue-router';
import { send } from '@/api';
import ModelSelect from '@/components/ModelSelect/index.vue';
import Collapse from '@/layouts/components0/Header/components/Collapse.vue';

import CreateChat from '@/layouts/components0/Header/components/CreateChat.vue';
import TitleEditing from '@/layouts/components0/Header/components/TitleEditing.vue';
import { useDesignStore, useGuideTourStore } from '@/stores';
import { useChatStore } from '@/stores/modules/chat';
import { useFilesStore } from '@/stores/modules/files';
import { useModelStore } from '@/stores/modules/model';
import { useSessionStore } from '@/stores/modules/session';
import { useUserStore } from '@/stores/modules/user';
import { getUserProfilePicture, systemProfilePicture } from '@/utils/user.ts';
import YMarkdown from '@/vue-element-plus-y/components/XMarkdown/index.vue';
import '@/styles/github-markdown.css';
import '@/styles/yixin-markdown.scss';

// 新增的导入
const designStore = useDesignStore();
const sessionStore = useSessionStore();
const currentSession = computed(() => sessionStore.currentSession);

type MessageItem = BubbleProps & {
  key: number;
  role: 'ai' | 'user' | 'assistant';
  avatar: string;
  thinkingStatus?: ThinkingStatus;
  thinlCollapse?: boolean;
  reasoning_content?: string;
  images?: Array<{ url: string; name?: string }>; // 用户消息中的图片列表
  files?: Array<{ name: string; size: number }>; // 用户消息中的文件列表
};

const route = useRoute();
const chatStore = useChatStore();
const modelStore = useModelStore();
const filesStore = useFilesStore();
const userStore = useUserStore();
const guideTourStore = useGuideTourStore();

// 用户头像
const avatar = computed(() => {
  const userInfo = userStore.userInfo;
  return userInfo?.avatar || 'https://avatars.githubusercontent.com/u/76239030?v=4';
});

const inputValue = ref('');
const senderRef = ref<InstanceType<typeof Sender> | null>(null);
const bubbleItems = ref<MessageItem[]>([]);
const bubbleListRef = ref<BubbleListInstance | null>(null);
const isSending = ref(false);

const { stream, loading: isLoading, cancel } = useHookFetch({
  request: send,
  onError: async (error) => {
    isLoading.value = false;

    if (error.status === 403) {
      const data = await (error.response.json());
      // 弹窗提示
      ElMessage.error(data.error.message);
      return Promise.reject(data);
    }
    if (error.status === 401) {
      ElMessage.error('登录已过期，请重新登录！');
      // 弹窗提示
      userStore.logout();
      userStore.openLoginDialog();
    }
  },

});
// 记录进入思考中
let isThinking = false;

watch(
  () => route.params?.id,
  async (_id_) => {
    if (_id_) {
      if (_id_ !== 'not_login') {
        // 判断的当前会话id是否有聊天记录，有缓存则直接赋值展示
        if (chatStore.chatMap[`${_id_}`] && chatStore.chatMap[`${_id_}`].length) {
          bubbleItems.value = chatStore.chatMap[`${_id_}`] as MessageItem[];
          // 滚动到底部
          setTimeout(() => {
            bubbleListRef.value!.scrollToBottom();
          }, 350);
          return;
        }

        // 无缓存则请求聊天记录
        await chatStore.requestChatList(`${_id_}`);
        // 请求聊天记录后，赋值回显，并滚动到底部
        bubbleItems.value = chatStore.chatMap[`${_id_}`] as MessageItem[];
        // 滚动到底部
        setTimeout(() => {
          bubbleListRef.value!.scrollToBottom();
        }, 350);
      }

      // 如果本地有发送内容 ，则直接发送
      const v = localStorage.getItem('chatContent');
      if (v) {
        // 发送消息
        setTimeout(() => {
          startSSE(v);
        }, 350);

        localStorage.removeItem('chatContent');
      }
    }
  },
  { immediate: true, deep: true },
);

/**
 * 处理流式响应的数据块
 * 解析 AI 返回的数据，更新消息内容和思考状态
 * @param {AnyObject} chunk - 流式响应的数据块
 */
function handleDataChunk(chunk: AnyObject) {
  try {
    // 安全获取 delta 和 content
    const delta = chunk.choices?.[0]?.delta;
    const reasoningChunk = delta?.reasoning_content;
    const parsedChunk = delta?.content;

    // usage 处理（可以移动到 startSSE 里也可以写这里）
    if (chunk.usage) {
      const { prompt_tokens, completion_tokens, total_tokens } = chunk.usage;
      const latest = bubbleItems.value[bubbleItems.value.length - 1];
      latest.tokenUsage = {
        prompt: prompt_tokens,
        completion: completion_tokens,
        total: total_tokens,
      };
    }

    if (reasoningChunk) {
      const latest = bubbleItems.value[bubbleItems.value.length - 1];
      latest.thinkingStatus = 'thinking';
      latest.loading = true;
      latest.thinlCollapse = true;
      latest.reasoning_content += reasoningChunk;
    }

    if (parsedChunk) {
      const thinkStart = parsedChunk.includes('<think>');
      const thinkEnd = parsedChunk.includes('</think>');

      if (thinkStart)
        isThinking = true;
      if (thinkEnd)
        isThinking = false;

      const latest = bubbleItems.value[bubbleItems.value.length - 1];

      if (isThinking) {
        latest.thinkingStatus = 'thinking';
        latest.loading = true;
        latest.thinlCollapse = true;
        latest.reasoning_content += parsedChunk.replace('<think>', '').replace('</think>', '');
      }
      else {
        latest.thinkingStatus = 'end';
        latest.loading = false;
        latest.content += parsedChunk;
      }
    }
  }
  catch (err) {
    console.error('解析数据时出错:', err);
  }
}

/**
 * 处理错误信息
 * @param {any} err - 错误对象
 */
function handleError(err: any) {
  console.error('Fetch error:', err);
}

/**
 * 发送消息并处理流式响应
 * 支持发送文本、图片和文件
 * @param {string} chatContent - 用户输入的文本内容
 */
async function startSSE(chatContent: string) {
  if (isSending.value)
    return;

  // 检查是否有未上传完成的文件
  const hasUnuploadedFiles = filesStore.filesList.some(f => !f.isUploaded);
  if (hasUnuploadedFiles) {
    ElMessage.warning('文件正在上传中，请稍候...');
    return;
  }

  isSending.value = true;

  try {
    // 清空输入框
    inputValue.value = '';

    // 获取当前上传的图片和文件（在清空之前保存）
    const imageFiles = filesStore.filesList.filter(f => f.isUploaded && f.fileType === 'image');
    const textFiles = filesStore.filesList.filter(f => f.isUploaded && f.fileType === 'text');

    const images = imageFiles.map(f => ({
      url: f.base64!, // 使用base64作为URL
      name: f.name,
    }));

    const files = textFiles.map(f => ({
      name: f.name!,
      size: f.fileSize!,
    }));

    addMessage(chatContent, true, images, files);
    addMessage('', false);

    // 立即清空文件列表（不要等到响应完成）
    filesStore.clearFilesList();

    // 这里有必要调用一下 BubbleList 组件的滚动到底部 手动触发 自动滚动
    bubbleListRef.value?.scrollToBottom();

    // 组装消息内容，支持图片和文件
    const messagesContent = bubbleItems.value.slice(0, -1).slice(-6).map((item: MessageItem) => {
      const baseMessage: any = {
        role: item.role,
      };

      // 如果是用户消息且有附件（图片或文件），组装成数组格式
      if (item.role === 'user' && item.key === bubbleItems.value.length - 2) {
        // 当前发送的消息
        const contentArray: any[] = [];

        // 添加文本内容
        if (item.content) {
          contentArray.push({
            type: 'text',
            text: item.content,
          });
        }

        // 添加文本文件内容（使用XML格式）
        if (textFiles.length > 0) {
          let fileContent = '\n\n';
          textFiles.forEach((fileItem, index) => {
            fileContent += `<ATTACHMENT_FILE>\n`;
            fileContent += `<FILE_INDEX>File ${index + 1}</FILE_INDEX>\n`;
            fileContent += `<FILE_NAME>${fileItem.name}</FILE_NAME>\n`;
            fileContent += `<FILE_CONTENT>\n${fileItem.fileContent}\n</FILE_CONTENT>\n`;
            fileContent += `</ATTACHMENT_FILE>\n`;
            if (index < textFiles.length - 1) {
              fileContent += '\n';
            }
          });

          contentArray.push({
            type: 'text',
            text: fileContent,
          });
        }

        // 添加图片内容（使用之前保存的 imageFiles）
        imageFiles.forEach((fileItem) => {
          if (fileItem.base64) {
            contentArray.push({
              type: 'image_url',
              image_url: {
                url: fileItem.base64, // 使用base64
                name: fileItem.name, // 保存图片名称
              },
            });
          }
        });

        // 如果有图片或文件，使用数组格式
        if (contentArray.length > 1 || imageFiles.length > 0 || textFiles.length > 0) {
          baseMessage.content = contentArray;
        }
        else {
          baseMessage.content = item.content;
        }
      }
      else {
        // 其他消息保持原样
        baseMessage.content = (item.role === 'ai' || item.role === 'assistant') && item.content.length > 10000
          ? `${item.content.substring(0, 10000)}...（内容过长，已省略）`
          : item.content;
      }

      return baseMessage;
    });

    // 使用 for-await 处理流式响应
    for await (const chunk of stream({
      messages: messagesContent,
      sessionId: route.params?.id !== 'not_login' ? String(route.params?.id) : 'not_login',
      stream: true,
      userId: userStore.userInfo?.userId,
      model: modelStore.currentModelInfo.modelId ?? '',
    })) {
      handleDataChunk(chunk.result as AnyObject);
    }
  }
  catch (err) {
    if (err.name === 'AbortError') {
      console.log('用户中止请求'); // 正常中止，无需提示
    }
    else {
      handleError(err); // 其他错误
      // ElMessage.error('消息发送失败，请重试');
    }
  }
  finally {
    isSending.value = false;

    // 停止打字器状态和加载状态
    if (bubbleItems.value.length) {
      const latest = bubbleItems.value[bubbleItems.value.length - 1];
      latest.typing = false;
      latest.loading = false;
      if (latest.thinkingStatus === 'thinking') {
        latest.thinkingStatus = 'end';
      }
    }

    // 保存聊天记录到 chatMap（本地缓存，刷新后可恢复）
    if (route.params?.id && route.params.id !== 'not_login') {
      chatStore.chatMap[`${route.params.id}`] = bubbleItems.value as any;
    }
  }
}

/**
 * 中断正在进行的请求
 * 停止流式响应并重置状态
 */
async function cancelSSE() {
  try {
    cancel(); // 直接调用，无需参数
    isSending.value = false;
    if (bubbleItems.value.length) {
      const latest = bubbleItems.value[bubbleItems.value.length - 1];
      latest.typing = false;
      latest.loading = false;
      if (latest.thinkingStatus === 'thinking') {
        latest.thinkingStatus = 'end';
      }
    }
  }
  catch (err) {
    handleError(err);
  }
}

/**
 * 添加消息到聊天列表
 * @param {string} message - 消息内容
 * @param {boolean} isUser - 是否为用户消息
 * @param {Array<{url: string, name?: string}>} images - 图片列表（可选）
 * @param {Array<{name: string, size: number}>} files - 文件列表（可选）
 */
function addMessage(message: string, isUser: boolean, images?: Array<{ url: string; name?: string }>, files?: Array<{ name: string; size: number }>) {
  const i = bubbleItems.value.length;
  const obj: MessageItem = {
    key: i,
    avatar: isUser
      ? getUserProfilePicture()
      : systemProfilePicture,
    avatarSize: '32px',
    role: isUser ? 'user' : 'assistant',
    placement: isUser ? 'end' : 'start',
    isMarkdown: !isUser,
    loading: !isUser,
    content: message || '',
    reasoning_content: '',
    thinkingStatus: 'start',
    thinlCollapse: false,
    noStyle: !isUser,
    images: images && images.length > 0 ? images : undefined,
    files: files && files.length > 0 ? files : undefined,
  };
  bubbleItems.value.push(obj);
}

/**
 * 处理思考链展开/收起状态变化
 * @param {object} payload - 状态变化的载荷
 * @param {boolean} payload.value - 展开/收起状态
 * @param {ThinkingStatus} payload.status - 思考状态
 */
function handleChange(payload: { value: boolean; status: ThinkingStatus }) {
}

/**
 * 删除文件卡片
 * @param {FilesCardProps} _item - 文件卡片项（未使用）
 * @param {number} index - 要删除的文件索引
 */
function handleDeleteCard(_item: FilesCardProps, index: number) {
  filesStore.deleteFileByIndex(index);
}

watch(
  () => filesStore.filesList.length,
  (val) => {
    if (val > 0) {
      nextTick(() => {
        senderRef.value?.openHeader();
      });
    }
    else {
      nextTick(() => {
        senderRef.value?.closeHeader();
      });
    }
  },
);

/**
 * 复制消息内容到剪贴板
 * @param {any} item - 消息项
 */
function copy(item: any) {
  navigator.clipboard.writeText(item.content || '')
    .then(() => ElMessage.success('已复制到剪贴板'))
    .catch(() => ElMessage.error('复制失败'));
}

/**
 * 图片预览
 * 在新窗口中打开图片
 * @param {string} url - 图片 URL
 */
function handleImagePreview(url: string) {
  window.open(url, '_blank');
}
</script>

<template>
  <div class="chat-with-id-container">
    <!-- 头部导航栏 -->
    <div class="chat-header">
      <div
        class="overflow-hidden flex h-full items-center flex-row flex-1 w-fit flex-shrink-0 min-w-0"
      >
        <div class="w-full flex items-center flex-row">
          <!-- 左边 -->
          <div

            class="left-box flex h-full items-center pl-20px gap-12px flex-shrink-0 flex-row"
          >
            <Collapse />
            <CreateChat />
            <div v-if="currentSession" class="w-0.5px h-30px bg-[rgba(217,217,217)]" />
          </div>

          <!-- 中间 -->
          <div class="middle-box flex-1 min-w-0 ml-12px">
            <TitleEditing />
          </div>
        </div>
      </div>
    </div>

    <!-- 聊天内容区域 -->
    <div class="chat-warp">
      <BubbleList ref="bubbleListRef" :list="bubbleItems" max-height="calc(100vh - 240px)">
        <template #header="{ item }">
          <Thinking
            v-if="item.reasoning_content" v-model="item.thinlCollapse" :content="item.reasoning_content"
            :status="item.thinkingStatus" class="thinking-chain-warp" @change="handleChange"
          />
        </template>
        <!-- 自定义气泡内容 -->
        <template #content="{ item }">
          <!-- chat 内容走 markdown -->
          <YMarkdown v-if="item.content && (item.role === 'assistant' || item.role === 'system')" class="markdown-body" :markdown="item.content" :themes="{ light: 'github-light', dark: 'github-dark' }" default-theme-mode="dark" />
          <!-- user 内容 纯文本 + 图片 + 文件 -->
          <div v-if="item.role === 'user'" class="user-content-wrapper">
            <!-- 图片列表 -->
            <div v-if="item.images && item.images.length > 0" class="user-images">
              <img
                v-for="(image, index) in item.images"
                :key="index"
                :src="image.url"
                :alt="image.name || '图片'"
                class="user-image"
                @click="() => handleImagePreview(image.url)"
              >
            </div>
            <!-- 文件列表 -->
            <div v-if="item.files && item.files.length > 0" class="user-files">
              <div
                v-for="(file, index) in item.files"
                :key="index"
                class="user-file-item"
              >
                <ElIcon class="file-icon">
                  <Document />
                </ElIcon>
                <span class="file-name">{{ file.name }}</span>
              </div>
            </div>
            <!-- 文本内容 -->
            <div v-if="item.content" class="user-content">
              {{ item.content }}
            </div>
          </div>
        </template>

        <!-- 自定义底部 -->
        <template #footer="{ item }">
          <div class="footer-wrapper">
            <div class="footer-container">
              <div class="footer-time">
                <span v-if="item.creationTime ">   {{ item.creationTime }}</span>
                <span v-if="((item.role === 'ai' || item.role === 'assistant') && item?.tokenUsage?.total) " style="margin-left: 10px;" class="footer-token">
                  {{ ((item.role === 'ai' || item.role === 'assistant') && item?.tokenUsage?.total) ? `token:${item?.tokenUsage?.total}` : '' }}</span>
                <el-button icon="DocumentCopy" size="small" circle @click="copy(item)" />
              </div>
            </div>
          </div>
        </template>
      </BubbleList>

      <Sender
        ref="senderRef" v-model="inputValue" class="chat-defaul-sender" data-tour="chat-sender" :auto-size="{
          maxRows: 6,
          minRows: 2,
        }" variant="updown" clearable allow-speech :loading="isLoading" @submit="startSSE" @cancel="cancelSSE"
      >
        <template #header>
          <div class="sender-header p-12px pt-6px pb-0px">
            <Attachments :items="filesStore.filesList" :hide-upload="true" @delete-card="handleDeleteCard">
              <template #prev-button="{ show, onScrollLeft }">
                <div
                  v-if="show"
                  class="prev-next-btn left-8px flex-center w-22px h-22px rounded-8px border-1px border-solid border-[rgba(0,0,0,0.08)] c-[rgba(0,0,0,.4)] hover:bg-#f3f4f6 bg-#fff font-size-10px"
                  @click="onScrollLeft"
                >
                  <ElIcon>
                    <ArrowLeftBold />
                  </ElIcon>
                </div>
              </template>

              <template #next-button="{ show, onScrollRight }">
                <div
                  v-if="show"
                  class="prev-next-btn right-8px flex-center w-22px h-22px rounded-8px border-1px border-solid border-[rgba(0,0,0,0.08)] c-[rgba(0,0,0,.4)] hover:bg-#f3f4f6 bg-#fff font-size-10px"
                  @click="onScrollRight"
                >
                  <ElIcon>
                    <ArrowRightBold />
                  </ElIcon>
                </div>
              </template>
            </Attachments>
          </div>
        </template>
        <template #prefix>
          <div class="flex-1 flex items-center gap-8px flex-none w-fit overflow-hidden">
            <FilesSelect />
            <!--            < ToolList/> -->
            <ModelSelect />
          </div>
        </template>
        <!-- 新增的 #suffix 模板 -->
        <template #suffix>
          <ElIcon v-if="isSending" class="is-loading">
            <Loading />
          </ElIcon>
        </template>
      </Sender>
    </div>
  </div>
</template>

<style scoped lang="scss">
.chat-with-id-container {
  padding: 0 20px;
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  width: 100%;
  height: 100%;

  .chat-header {
    width: 100%;
    //max-width: 1000px;
    height: 60px;
    display: flex;
    align-items: center;
    flex-shrink: 0;
  }

  .chat-warp {
    max-width: 1000px;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    width: 100%;
    height: calc(100vh - 120px); // 减去头部高度
    .thinking-chain-warp {
      margin-bottom: 12px;
    }
  }
  :deep() {
    .el-bubble-list {
      padding-top: 24px;
    }
    .el-bubble {
      padding: 0 12px;
      padding-bottom: 24px;
    }
    .el-typewriter {
      overflow: hidden;
      border-radius: 12px;
    }
    .user-content-wrapper {
      display: flex;
      flex-direction: column;
      gap: 8px;
    }
    .user-images {
      display: flex;
      flex-wrap: wrap;
      gap: 8px;
      margin-bottom: 4px;
    }
    .user-image {
      max-width: 200px;
      max-height: 200px;
      border-radius: 8px;
      object-fit: cover;
      cursor: pointer;
      transition: transform 0.2s;
      &:hover {
        transform: scale(1.05);
      }
    }
    .user-files {
      display: flex;
      flex-direction: column;
      gap: 6px;
      margin-bottom: 4px;
    }
    .user-file-item {
      display: flex;
      align-items: center;
      gap: 8px;
      padding: 8px 12px;
      background: rgba(0, 0, 0, 0.05);
      border-radius: 6px;
      font-size: 13px;
      .file-icon {
        font-size: 16px;
        color: #409eff;
      }
      .file-name {
        flex: 1;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
      }
      .file-size {
        color: #909399;
        font-size: 12px;
      }
    }
    .user-content {
      // 换行
      white-space: pre-wrap;
    }
    .markdown-body {
      background-color: transparent;
    }
    .markdown-elxLanguage-header-div {
      top: -25px !important;
    }

    // xmarkdown 样式
    .elx-xmarkdown-container {
      padding: 8px 4px;
    }
  }
  .chat-defaul-sender {
    width: 100%;
    margin-bottom: 22px;
  }
}

.footer-wrapper {
  display: flex;
  align-items: center;
  gap: 10px;
  .footer-time {
    font-size: 12px;
    margin-top: 3px;
    .footer-token {
      background: rgba(1, 183, 86, 0.53);
      padding: 0 4px;
      margin: 0 2px;
      border-radius: 4px;
      color: #ffffff;
    }
  }
}

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

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

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