<script setup lang="ts">
import type { ChatTimeTimelineItem } from '@easyflow/types';

import { inject, ref } from 'vue';

import { ChatTimeTimelineBuilder, uuid } from '@easyflow/utils';

import { Paperclip, Promotion } from '@element-plus/icons-vue';
import { ElButton, ElIcon } from 'element-plus';

import { sseClient } from '#/api/request';
import SendingIcon from '#/components/icons/SendingIcon.vue';
import ChatFileUploader from '#/components/upload/ChatFileUploader.vue';
interface Props {
  conversationId: string | undefined;
  bot: any;
  mutateMessages: (mutator: (messages: ChatTimeTimelineItem[]) => void) => void;
}

const props = defineProps<Props>();
const senderValue = ref('');
const btnLoading = ref(false);
const getSessionList = inject<any>('getSessionList');

interface SendMessageOptions {
  prompt?: string;
  regenerateRoundId?: string;
}

interface SseRoundMeta {
  roundId?: string;
  roundNo?: number;
  selectedVariantIndex?: number;
  switchable?: boolean;
  variantCount?: number;
  variantIndex?: number;
}

defineExpose({
  getConversationId: () => props.conversationId,
  sendMessage,
});

const clearSenderFiles = () => {
  files.value = [];
  attachmentsRef.value?.clearFiles();
};
function sendMessage(options: SendMessageOptions = {}) {
  const prompt = String(options.prompt || senderValue.value || '').trim();
  const regenerateRoundId = options.regenerateRoundId
    ? String(options.regenerateRoundId)
    : '';
  const isRegenerate = !!regenerateRoundId;
  if (!props.conversationId || !prompt) {
    return;
  }
  const data = {
    conversationId: props.conversationId,
    prompt,
    botId: props.bot.id,
    attachments: attachmentsRef.value?.getFileList(),
    regenerateRoundId: regenerateRoundId || undefined,
  };
  clearSenderFiles();
  btnLoading.value = true;
  let userMessageId = '';
  if (!isRegenerate) {
    props.mutateMessages((messages) => {
      const latestAssistant = [...messages]
        .reverse()
        .find((item) => item.role === 'assistant');
      if (latestAssistant?.roundId) {
        ChatTimeTimelineBuilder.setRoundSwitchable(
          messages,
          latestAssistant.roundId,
          false,
        );
      }
    });
  }
  if (!isRegenerate) {
    userMessageId = uuid();
    props.mutateMessages((messages) => {
      ChatTimeTimelineBuilder.appendUserMessage(messages, {
        content: prompt,
        created: Date.now(),
        id: userMessageId,
      });
    });
    senderValue.value = '';
  }
  let receivedAssistantPayload = false;

  sseClient.post('/userCenter/bot/chat', data, {
    onMessage(res) {
      if (!res.data) {
        return;
      }
      const sseData = JSON.parse(res.data);
      const streamMeta = normalizeSseRoundMeta(sseData?.meta);
      props.mutateMessages((messages) => {
        ChatTimeTimelineBuilder.bindLatestPendingUserMessage(messages, streamMeta);
      });
      const delta = sseData.payload?.delta;

      if (res.event === 'done') {
        btnLoading.value = false;
        getSessionList();
      }

      // 处理系统错误
      if (
        sseData?.domain === 'SYSTEM' &&
        sseData.payload?.code === 'SYSTEM_ERROR'
      ) {
        if (isRegenerate && !receivedAssistantPayload) {
          btnLoading.value = false;
          return;
        }
        const errorMessage = sseData.payload.message;
        props.mutateMessages((messages) => {
          ChatTimeTimelineBuilder.applySystemError(messages, errorMessage);
        });
        return;
      }

      if (sseData?.domain === 'TOOL') {
        receivedAssistantPayload = true;
        props.mutateMessages((messages) => {
          if (sseData?.type === 'TOOL_CALL') {
            ChatTimeTimelineBuilder.upsertToolCall(messages, {
              created: Date.now(),
              ...streamMeta,
              name: sseData?.payload?.name,
              toolCallId: sseData?.payload?.tool_call_id,
              value: sseData?.payload?.arguments,
            });
            return;
          }
          ChatTimeTimelineBuilder.upsertToolResult(messages, {
            created: Date.now(),
            ...streamMeta,
            name: sseData?.payload?.name,
            result: sseData?.payload?.result,
            toolCallId: sseData?.payload?.tool_call_id,
          });
        });
        return;
      }

      if (sseData.type === 'THINKING') {
        receivedAssistantPayload = true;
        props.mutateMessages((messages) => {
          ChatTimeTimelineBuilder.appendThinkingDelta(
            messages,
            delta,
            Date.now(),
            streamMeta,
          );
        });
      } else if (sseData.type === 'MESSAGE') {
        receivedAssistantPayload = true;
        props.mutateMessages((messages) => {
          ChatTimeTimelineBuilder.appendMessageDelta(
            messages,
            delta,
            Date.now(),
            streamMeta,
          );
        });
      }
    },
    onError(err) {
      console.error(err);
      btnLoading.value = false;
      if (!isRegenerate && !receivedAssistantPayload && userMessageId) {
        props.mutateMessages((messages) => {
          const index = messages.findIndex((item) => item.id === userMessageId);
          if (index >= 0) {
            messages.splice(index, 1);
          }
        });
      }
      props.mutateMessages((messages) => {
        ChatTimeTimelineBuilder.finalize(messages);
      });
    },
    onFinished() {
      senderValue.value = '';
      btnLoading.value = false;
      props.mutateMessages((messages) => {
        ChatTimeTimelineBuilder.finalize(messages);
      });
    },
  });
}
function getDisabled() {
  return !senderValue.value || !props.conversationId;
}
const stopSse = () => {
  sseClient.abort();
  btnLoading.value = false;
  props.mutateMessages((messages) => {
    ChatTimeTimelineBuilder.finalize(messages);
  });
};
const attachmentsRef = ref();
const files = ref<any[]>([]);
function handlePasteFile(_: any, fileList: FileList) {
  files.value = [...fileList];
}
function triggerFileSelect() {
  attachmentsRef.value?.triggerFileSelect?.();
}
function handleDeleteAllSenderFiles() {
  files.value = [];
}

function normalizeSseRoundMeta(meta: any): SseRoundMeta {
  if (!meta || typeof meta !== 'object') {
    return {};
  }
  const variantIndex = meta.variantIndex ? Number(meta.variantIndex) : undefined;
  return {
    roundId: meta.roundId ? String(meta.roundId) : undefined,
    roundNo: meta.roundNo ? Number(meta.roundNo) : undefined,
    selectedVariantIndex: meta.selectedVariantIndex
      ? Number(meta.selectedVariantIndex)
      : variantIndex,
    switchable:
      typeof meta.switchable === 'boolean'
        ? meta.switchable
        : variantIndex != null
          ? true
          : undefined,
    variantCount: meta.variantCount ? Number(meta.variantCount) : variantIndex,
    variantIndex,
  };
}
</script>

<template>
  <ChatFileUploader
    ref="attachmentsRef"
    :external-files="files"
    @delete-all="handleDeleteAllSenderFiles"
    :max-size="10"
  />
  <ElSender
    v-model="senderValue"
    variant="updown"
    :auto-size="{ minRows: 2, maxRows: 5 }"
    clearable
    allow-speech
    placeholder="发送消息"
    @keyup.enter="() => sendMessage()"
    @paste-file="handlePasteFile"
  >
    <template #action-list>
      <div class="flex items-center gap-2">
        <ElButton circle @click="triggerFileSelect">
          <ElIcon><Paperclip /></ElIcon>
        </ElButton>
        <!-- <ElButton :icon="PaperclipIcon" link /> -->
        <ElButton v-if="btnLoading" circle @click="stopSse">
          <ElIcon size="30" color="#409eff"><SendingIcon /></ElIcon>
        </ElButton>
        <ElButton
          v-else
          type="primary"
          :icon="Promotion"
          :disabled="getDisabled()"
          @click="() => sendMessage()"
          round
        />
      </div>
    </template>
  </ElSender>
</template>
