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

import { computed, onMounted, ref, watch } from 'vue';
import { useRoute, useRouter } from 'vue-router';

import {
  ChatTimeHistoryMapper,
  ChatTimeTimelineBuilder,
  createChatVariantSwitchController,
} from '@easyflow/utils';
import { Delete, Edit, Search } from '@element-plus/icons-vue';
import {
  ElButton,
  ElContainer,
  ElDrawer,
  ElEmpty,
  ElHeader,
  ElInput,
  ElMain,
  ElMessage,
  ElMessageBox,
  ElPagination,
  ElSelect,
  ElSpace,
  ElTag,
} from 'element-plus';
import { tryit } from 'radash';

import { api } from '#/api/request';
import { ChatBubbleList } from '#/components/chat';

const route = useRoute();
const router = useRouter();

const assistantList = ref<any[]>([]);
const sessions = ref<any[]>([]);
const loading = ref(false);
const queryParams = ref({
  assistantId: undefined as number | undefined,
  keyword: '',
  pageNumber: 1,
  pageSize: 20,
});

const pageState = ref({
  total: 0,
});

const drawerVisible = ref(false);
const drawerLoading = ref(false);
const currentSession = ref<any>();
const messageList = ref<ChatTimeTimelineItem[]>([]);
const variantSwitchStateVersion = ref(0);
const loadedMessageRecordCount = ref(0);
const messagePage = ref({
  total: 0,
  pageNumber: 1,
  pageSize: 20,
});
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);
  },
});

const filteredSessions = computed(() => {
  const keyword = queryParams.value.keyword.trim().toLowerCase();
  if (!keyword) {
    return sessions.value;
  }
  return sessions.value.filter((item) => {
    const title = String(item.title || '').toLowerCase();
    const preview = String(item.lastMessagePreview || '').toLowerCase();
    const assistantName = String(item.assistantName || '').toLowerCase();
    return title.includes(keyword) || preview.includes(keyword) || assistantName.includes(keyword);
  });
});

onMounted(async () => {
  await Promise.all([fetchAssistants(), fetchSessions()]);
  const sessionId = route.query.sessionId ? String(route.query.sessionId) : '';
  if (sessionId) {
    await openSession(sessionId);
  }
});

watch(
  () => route.query.sessionId,
  async (sessionId) => {
    if (!sessionId) {
      drawerVisible.value = false;
      currentSession.value = undefined;
      messageList.value = [];
      return;
    }
    if (!currentSession.value || String(currentSession.value.id) !== String(sessionId)) {
      await openSession(String(sessionId));
    }
  },
);

async function fetchAssistants() {
  const [, res] = await tryit(api.get)('/userCenter/bot/list', {
    params: { status: 1 },
  });
  if (res?.errorCode === 0) {
    assistantList.value = (res.data || []).map((item: any) => ({
      label: item.title,
      value: item.id,
    }));
  }
}

async function fetchSessions() {
  loading.value = true;
  const [, res] = await tryit(api.get)('/userCenter/chatHistory/sessions', {
    params: {
      assistantId: queryParams.value.assistantId,
      pageNumber: queryParams.value.pageNumber,
      pageSize: queryParams.value.pageSize,
    },
  });
  loading.value = false;
  if (res?.errorCode === 0) {
    sessions.value = res.data?.records || [];
    pageState.value.total = res.data?.total || 0;
  }
}

async function openSession(sessionId: string | number) {
  drawerLoading.value = true;
  const [, summaryRes] = await tryit(api.get)(`/userCenter/chatHistory/sessions/${sessionId}`);
  if (summaryRes?.errorCode !== 0) {
    drawerLoading.value = false;
    return;
  }
  currentSession.value = summaryRes.data;
  messageList.value = [];
  loadedMessageRecordCount.value = 0;
  messagePage.value = {
    total: 0,
    pageNumber: 1,
    pageSize: 20,
  };
  drawerVisible.value = true;
  await loadMessages(true);
  drawerLoading.value = false;
  if (String(route.query.sessionId || '') !== String(sessionId)) {
    router.replace({
      path: '/chatHistory',
      query: { ...route.query, sessionId: String(sessionId) },
    });
  }
}

async function loadMessages(reset = false) {
  if (!currentSession.value?.id) {
    return;
  }
  const nextPageNumber = reset ? 1 : messagePage.value.pageNumber + 1;
  const [, res] = await tryit(api.get)(
    `/userCenter/chatHistory/sessions/${currentSession.value.id}/messages`,
    {
      params: {
        pageNumber: nextPageNumber,
        pageSize: messagePage.value.pageSize,
      },
    },
  );
  if (res?.errorCode !== 0) {
    return;
  }
  const normalized = normalizeMessages(res.data?.records || []);
  if (reset) {
    messageList.value = normalized;
  } else {
    messageList.value = [...normalized, ...messageList.value];
  }
  prefetchVisibleVariants();
  loadedMessageRecordCount.value = reset
    ? (res.data?.records || []).length
    : loadedMessageRecordCount.value + (res.data?.records || []).length;
  messagePage.value.total = res.data?.total || 0;
  messagePage.value.pageNumber = nextPageNumber;
}

function normalizeMessages(records: any[]) {
  return ChatTimeHistoryMapper.fromHistoryRecords([...records].reverse());
}

async function handleSelectVariant(
  item: ChatTimeTimelineItem,
  direction: 'next' | 'previous',
) {
  if (!currentSession.value?.id || !item.roundId) {
    return;
  }
  const current = Number(item.variantIndex || item.selectedVariantIndex || 1);
  const variantIndex = direction === 'previous' ? current - 1 : current + 1;
  const sessionId = String(currentSession.value.id);
  await variantSwitchController.switchVariant({
    fetchVariants: () => fetchRoundVariants(sessionId, item.roundId!),
    items: messageList.value,
    persistVariant: async () => {
      const [, res] = await tryit(api.post)(
        `/userCenter/chatHistory/sessions/${sessionId}/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,
    targetVariantIndex: variantIndex,
  });
}

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 prefetchVisibleVariants() {
  if (!currentSession.value?.id) {
    return;
  }
  const sessionId = String(currentSession.value.id);
  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(sessionId, item.roundId!),
      roundId: item.roundId,
      sessionId,
    });
  }
}

function currentSwitchingRoundIds() {
  void variantSwitchStateVersion.value;
  if (!currentSession.value?.id) {
    return [];
  }
  const sessionId = String(currentSession.value.id);
  return Array.from(
    new Set(
      messageList.value
        .filter(
          (item) =>
            item.roundId &&
            variantSwitchController.isSwitching(sessionId, item.roundId),
        )
        .map((item) => String(item.roundId)),
    ),
  );
}

function closeDrawer() {
  drawerVisible.value = false;
  currentSession.value = undefined;
  messageList.value = [];
  loadedMessageRecordCount.value = 0;
  router.replace({ path: '/chatHistory', query: {} });
}

function changePage(pageNumber: number) {
  queryParams.value.pageNumber = pageNumber;
  fetchSessions();
}

async function renameSession(session: any) {
  const [, promptRes] = await tryit(ElMessageBox.prompt)(
    '请输入新的会话名称',
    '重命名会话',
    {
      inputValue: session.title || '',
      inputPlaceholder: '请输入会话名称',
      confirmButtonText: '确认',
      cancelButtonText: '取消',
    },
  );
  const value = promptRes?.value?.trim();
  if (!value) {
    return;
  }
  const [, res] = await tryit(api.post)(`/userCenter/chatHistory/sessions/${session.id}/rename`, {
    title: value,
  });
  if (res?.errorCode === 0) {
    ElMessage.success('重命名成功');
    if (currentSession.value?.id === session.id) {
      currentSession.value.title = value;
    }
    await fetchSessions();
  }
}

function deleteSession(session: any) {
  ElMessageBox.confirm('删除后将不再出现在聊天历史中，是否继续？', '删除会话', {
    type: 'warning',
    confirmButtonText: '删除',
    cancelButtonText: '取消',
  }).then(async () => {
    const [, res] = await tryit(api.post)(`/userCenter/chatHistory/sessions/${session.id}/delete`, {});
    if (res?.errorCode === 0) {
      ElMessage.success('删除成功');
      if (currentSession.value?.id === session.id) {
        closeDrawer();
      }
      await fetchSessions();
    }
  }).catch(() => {});
}

function formatTime(value?: string) {
  if (!value) {
    return '';
  }
  const time = new Date(value);
  if (Number.isNaN(time.getTime())) {
    return value;
  }
  return `${time.getMonth() + 1}-${time.getDate()} ${String(time.getHours()).padStart(2, '0')}:${String(time.getMinutes()).padStart(2, '0')}`;
}
</script>

<template>
  <ElContainer class="bg-background-deep h-full">
    <ElHeader class="!h-auto !px-8 !pb-0 !pt-8">
      <ElSpace direction="vertical" :size="20" alignment="flex-start" class="w-full">
        <div>
          <h1 class="text-2xl font-medium">聊天历史</h1>
          <p class="text-foreground/60 mt-2 text-sm">
            查看最近会话，并在右侧抽屉中回溯完整聊天内容。
          </p>
        </div>
        <div class="flex w-full flex-wrap items-center gap-4">
          <ElSelect
            v-model="queryParams.assistantId"
            clearable
            placeholder="筛选聊天助理"
            :options="assistantList"
            class="!w-[220px]"
            @change="fetchSessions"
          />
          <ElInput
            v-model="queryParams.keyword"
            class="max-w-[320px]"
            placeholder="搜索标题或最近消息"
            :prefix-icon="Search"
          />
        </div>
      </ElSpace>
    </ElHeader>

    <ElMain class="!px-8 !pb-8">
      <div class="bg-background border-border min-h-full rounded-2xl border p-5">
        <div v-if="filteredSessions.length > 0" class="flex flex-col gap-3">
          <button
            v-for="item in filteredSessions"
            :key="item.id"
            class="border-border hover:border-primary/30 hover:bg-accent/40 flex w-full items-start justify-between gap-4 rounded-2xl border px-5 py-4 text-left transition-colors"
            @click="openSession(item.id)"
          >
            <div class="min-w-0 flex-1">
              <div class="flex items-center gap-3">
                <span class="truncate text-base font-medium">{{ item.title || '未命名会话' }}</span>
                <ElTag size="small" effect="plain">{{ item.assistantName || '聊天助理' }}</ElTag>
              </div>
              <div class="text-foreground/65 mt-2 line-clamp-2 text-sm">
                {{ item.lastMessagePreview || '暂无消息内容' }}
              </div>
              <div class="text-foreground/50 mt-3 flex flex-wrap items-center gap-4 text-xs">
                <span>最近发送人：{{ item.lastSenderName || '未知' }}</span>
                <span>消息数：{{ item.messageCount || 0 }}</span>
                <span>活跃时间：{{ formatTime(item.lastMessageAt || item.accessAt) }}</span>
              </div>
            </div>
            <div class="flex items-center gap-2">
              <ElButton link :icon="Edit" @click.stop="renameSession(item)">重命名</ElButton>
              <ElButton link type="danger" :icon="Delete" @click.stop="deleteSession(item)">删除</ElButton>
            </div>
          </button>
        </div>
        <ElEmpty v-else description="暂无聊天历史" />

        <div class="mt-6 flex justify-end" v-if="pageState.total > queryParams.pageSize">
          <ElPagination
            background
            layout="prev, pager, next"
            :current-page="queryParams.pageNumber"
            :page-size="queryParams.pageSize"
            :total="pageState.total"
            @current-change="changePage"
          />
        </div>
      </div>
    </ElMain>
  </ElContainer>

  <ElDrawer
    v-model="drawerVisible"
    :title="currentSession?.title || '聊天详情'"
    size="760px"
    destroy-on-close
    @close="closeDrawer"
  >
    <div v-loading="drawerLoading" class="flex h-full flex-col">
      <div class="border-border mb-4 flex items-center justify-between rounded-2xl border px-4 py-3">
        <div class="min-w-0">
          <div class="truncate text-base font-medium">{{ currentSession?.title || '聊天详情' }}</div>
          <div class="text-foreground/55 mt-1 text-sm">
            {{ currentSession?.assistantName || '聊天助理' }}
          </div>
        </div>
        <div class="text-foreground/50 text-xs">
          {{ formatTime(currentSession?.lastMessageAt || currentSession?.accessAt) }}
        </div>
      </div>

      <div class="flex-1 overflow-hidden">
        <div class="mb-4 flex justify-center">
          <ElButton
            v-if="loadedMessageRecordCount < messagePage.total"
            text
            type="primary"
            @click="loadMessages(false)"
          >
            加载更早消息
          </ElButton>
        </div>
        <ChatBubbleList
          :bot="{ icon: '', title: currentSession?.assistantName || '' }"
          :messages="messageList"
          allow-variant-switch
          :switching-round-ids="currentSwitchingRoundIds()"
          @select-next-variant="handleSelectVariant($event, 'next')"
          @select-previous-variant="handleSelectVariant($event, 'previous')"
        />
      </div>
    </div>
  </ElDrawer>
</template>
