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

import { onBeforeUnmount, onMounted, ref } from 'vue';

import { IconifyIcon } from '@easyflow/icons';
import {
  ChatTimeHistoryMapper,
  ChatTimeTimelineBuilder,
  cn,
  createChatVariantSwitchController,
} from '@easyflow/utils';

import { ElAside, ElContainer, ElMain, ElMessage } from 'element-plus';
import { tryit } from 'radash';

import { api } from '#/api/request';
import defaultAssistantAvatar from '#/assets/defaultAssistantAvatar.svg';
import {
  Card,
  CardAvatar,
  CardContent,
  CardDescription,
  CardTitle,
} from '#/components/card';
import { ChatBubbleList, ChatContainer, ChatSender } from '#/components/chat';

onMounted(() => {
  getAssistantList();
});
onBeforeUnmount(() => {
  if (prefetchTimer) {
    clearTimeout(prefetchTimer);
  }
});
const recentUsedAssistant = ref<any[]>([]);
const currentBot = ref<any>({});
const handleSelectAssistant = (bot: any) => {
  currentBot.value = bot;
  messageList.value = [];
};
function getAssistantList() {
  api.get('/userCenter/botRecentlyUsed/getRecentlyBot').then((res) => {
    recentUsedAssistant.value = res.data;
    if (recentUsedAssistant.value.length > 0) {
      currentBot.value = recentUsedAssistant.value[0];
    }
  });
}
const messageList = ref<ChatTimeTimelineItem[]>([]);
const senderRef = ref<InstanceType<typeof ChatSender>>();
const variantSwitchStateVersion = ref(0);
let prefetchTimer: ReturnType<typeof setTimeout> | undefined;
const variantSwitchController = createChatVariantSwitchController<any, ChatTimeTimelineItem>({
  mapRecords: (records) => ChatTimeHistoryMapper.fromHistoryRecords(records),
  onError: () => ElMessage.error('答案版本切换失败'),
  onStateChange: () => {
    variantSwitchStateVersion.value += 1;
  },
  replaceRound(items, roundId, nextItems) {
    ChatTimeTimelineBuilder.replaceRoundMessages(items, roundId, nextItems);
  },
});
function setMessageList(messages: ChatTimeTimelineItem[]) {
  messageList.value = messages;
  schedulePrefetchVisibleVariants();
}
function mutateMessages(mutator: (messages: ChatTimeTimelineItem[]) => void) {
  const next = [...messageList.value];
  mutator(next);
  messageList.value = next;
  schedulePrefetchVisibleVariants();
}
const isFold = ref(false);
const toggleFold = () => {
  isFold.value = !isFold.value;
};

async function handleRegenerate(item: ChatTimeTimelineItem) {
  if (!item.roundId) {
    return;
  }
  const prompt = resolveRoundPrompt(String(item.roundId));
  if (!prompt) {
    return;
  }
  senderRef.value?.sendMessage({
    prompt,
    regenerateRoundId: String(item.roundId),
  });
}

async function handleSelectVariant(
  item: ChatTimeTimelineItem,
  direction: 'next' | 'previous',
) {
  if (!item.roundId) {
    return;
  }
  const current = Number(item.variantIndex || item.selectedVariantIndex || 1);
  const variantIndex = direction === 'previous' ? current - 1 : current + 1;
  const conversationId = resolveConversationId();
  if (!conversationId || variantIndex <= 0) {
    return;
  }
  await variantSwitchController.switchVariant({
    fetchVariants: () => fetchRoundVariants(conversationId, item.roundId!),
    items: messageList.value,
    persistVariant: async () => {
      const [, res] = await tryit(api.post)(
        `/userCenter/chatHistory/sessions/${conversationId}/rounds/${item.roundId}/selectVariant`,
        {
          variantIndex,
        },
      );
      if (res?.errorCode !== 0 || !res?.data) {
        throw new Error(String((res as any)?.message || '答案版本切换失败'));
      }
      return res.data;
    },
    roundId: item.roundId,
    sessionId: conversationId,
    targetVariantIndex: variantIndex,
  });
}

function resolveConversationId() {
  return String(senderRef.value?.getConversationId?.() || '');
}

function resolveRoundPrompt(roundId: string) {
  const target = [...messageList.value].reverse().find(
    (item) => item.role === 'user' && item.roundId === roundId,
  );
  return String(target?.content || '').trim();
}

async function fetchRoundVariants(sessionId: string, roundId: string | number) {
  const [, res] = await tryit(api.get)(
    `/userCenter/chatHistory/sessions/${sessionId}/rounds/${roundId}/variants`,
  );
  if (res?.errorCode !== 0) {
    throw new Error(String((res as any)?.message || '答案版本加载失败'));
  }
  return res?.data || [];
}

function schedulePrefetchVisibleVariants() {
  if (prefetchTimer) {
    clearTimeout(prefetchTimer);
  }
  prefetchTimer = setTimeout(() => {
    prefetchVisibleVariants();
  }, 120);
}

function prefetchVisibleVariants() {
  const conversationId = resolveConversationId();
  if (!conversationId) {
    return;
  }
  const prefetchedRoundIds = new Set<string>();
  for (const item of messageList.value) {
    if (
      item.role !== 'assistant' ||
      !item.roundId ||
      Number(item.variantCount || 0) <= 1 ||
      prefetchedRoundIds.has(String(item.roundId))
    ) {
      continue;
    }
    prefetchedRoundIds.add(String(item.roundId));
    variantSwitchController.prefetchVariants({
      fetchVariants: () => fetchRoundVariants(conversationId, item.roundId!),
      roundId: item.roundId,
      sessionId: conversationId,
    });
  }
}

function currentSwitchingRoundIds() {
  void variantSwitchStateVersion.value;
  const conversationId = resolveConversationId();
  if (!conversationId) {
    return [];
  }
  return Array.from(
    new Set(
      messageList.value
        .filter(
          (item) =>
            item.roundId &&
            variantSwitchController.isSwitching(conversationId, item.roundId),
        )
        .map((item) => String(item.roundId)),
    ),
  );
}
</script>

<template>
  <div class="bg-background-deep h-full w-full p-6">
    <ElContainer
      class="bg-background border-border h-full overflow-hidden rounded-lg border"
    >
      <ElMain class="!p-0">
        <ChatContainer
          class="border-none"
          :bot="currentBot"
          :is-fold="isFold"
          :on-message-list="setMessageList"
          :toggle-fold="toggleFold"
        >
          <template #default="{ conversationId }">
            <div class="flex h-full flex-col justify-between">
              <ChatBubbleList
                :bot="currentBot"
                :messages="messageList"
                allow-regenerate
                allow-variant-switch
                :switching-round-ids="currentSwitchingRoundIds()"
                @regenerate="handleRegenerate"
                @select-next-variant="handleSelectVariant($event, 'next')"
                @select-previous-variant="handleSelectVariant($event, 'previous')"
              />
              <div class="mx-auto w-full max-w-[1000px]">
                <ChatSender
                  ref="senderRef"
                  :bot="currentBot"
                  :conversation-id="conversationId"
                  :mutate-messages="mutateMessages"
                />
              </div>
            </div>
          </template>
        </ChatContainer>
      </ElMain>
      <transition name="collapse-horizontal">
        <ElAside
          v-if="!isFold"
          width="283px"
          class="bg-background border-border flex flex-col gap-5 border-l p-5 pt-4"
        >
          <div class="flex items-center justify-between">
            <span class="pl-2.5 text-base font-medium">助理</span>
            <IconifyIcon
              icon="svg:assistant-fold"
              class="cursor-pointer"
              @click="toggleFold"
            />
          </div>
          <div class="flex h-full flex-col gap-5 overflow-auto">
            <Card
              v-for="assistant in recentUsedAssistant"
              :key="assistant.id"
              :class="
                cn(
                  currentBot.id === assistant.id
                    ? 'bg-[hsl(var(--primary)/15%)] dark:bg-[hsl(var(--accent))]'
                    : 'hover:bg-[hsl(var(--accent))]',
                )
              "
              @click="handleSelectAssistant(assistant)"
            >
              <CardAvatar
                :src="assistant.icon"
                :default-avatar="defaultAssistantAvatar"
              />
              <CardContent>
                <CardTitle
                  :title="assistant.title"
                  :class="cn(assistant.checked && 'text-primary')"
                >
                  {{ assistant.title }}
                </CardTitle>
                <CardDescription :title="assistant.description">
                  {{ assistant.description }}
                </CardDescription>
              </CardContent>
            </Card>
          </div>
        </ElAside>
      </transition>
    </ElContainer>
  </div>
</template>

<style lang="css" scoped>
.el-aside::-webkit-scrollbar {
  display: none;
}

.collapse-horizontal-enter-active,
.collapse-horizontal-leave-active {
  overflow: hidden;
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}

.collapse-horizontal-enter-from,
.collapse-horizontal-leave-to {
  max-width: 0;
  padding: 0;
  opacity: 0;
  transform-origin: left;
}

.collapse-horizontal-enter-to,
.collapse-horizontal-leave-from {
  max-width: 283px;
  opacity: 1;
  transform-origin: left;
}
</style>
