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

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

import { useEasyFlowDrawer } from '@easyflow/common-ui';
import {
  createChatVariantSwitchController,
  ChatTimeHistoryMapper,
  ChatTimeTimelineBuilder,
} from '@easyflow/utils';

import { Search } from '@element-plus/icons-vue';
import {
  ElButton,
  ElDatePicker,
  ElEmpty,
  ElInput,
  ElMessage,
  ElPagination,
  ElSelect,
  ElTable,
  ElTableColumn,
} from 'element-plus';
import { tryit } from 'radash';

import { api } from '#/api/request';
import ChatHistoryDetailDrawer from '#/components/chat-history/ChatHistoryDetailDrawer.vue';
import ListPageShell from '#/components/page/ListPageShell.vue';

const agentOptions = ref<any[]>([]);
const agentLoading = ref(false);
const sessions = ref<any[]>([]);
const loading = ref(false);
const query = ref({
  assistantId: undefined as number | undefined,
  userAccount: '',
  timeRange: [] as string[],
  pageNumber: 1,
  pageSize: 20,
});
const quickRange = ref<'' | 'last7' | 'last30' | 'today'>('');
const pageState = ref({
  total: 0,
});
const pageSizeOptions = [20, 50, 100];
const quickRangeOptions = [
  { label: '今天', value: 'today' as const },
  { label: '最近 7 天', value: 'last7' as const },
  { label: '最近 30 天', value: 'last30' as const },
];

const drawerLoading = ref(false);
const currentSession = ref<any>();
const messageList = ref<ChatTimeTimelineItem[]>([]);
const loadedMessageRecordCount = ref(0);
const messagePage = ref({
  total: 0,
  pageNumber: 1,
  pageSize: 20,
});
const variantSwitchStateVersion = ref(0);
const variantSwitchController = createChatVariantSwitchController<any, ChatTimeTimelineItem>({
  mapRecords: (records) => ChatTimeHistoryMapper.fromHistoryRecords(records),
  onStateChange: () => {
    variantSwitchStateVersion.value += 1;
  },
  replaceRound: (items, roundId, nextItems) =>
    ChatTimeTimelineBuilder.replaceRoundMessages(items, roundId, nextItems),
});

const [Drawer, drawerApi] = useEasyFlowDrawer({
  appendToMain: false,
  class:
    '!w-[820px] max-w-[calc(100vw-24px)] !border-0 !bg-[hsl(var(--glass-tint))/0.28] shadow-[0_24px_48px_-36px_hsl(var(--foreground)/0.12)] supports-[backdrop-filter]:!bg-[hsl(var(--glass-tint))/0.2]',
  closable: false,
  contentClass: 'p-0',
  footer: false,
  header: false,
  modal: false,
  placement: 'right',
});

const hasMoreMessages = computed(
  () => loadedMessageRecordCount.value < messagePage.value.total,
);

const selectedSessionId = computed(() =>
  String(currentSession.value?.id || ''),
);

onMounted(async () => {
  await Promise.all([fetchAgents(), fetchSessions()]);
});

async function fetchAgents() {
  agentLoading.value = true;
  const [error, res] = await tryit(api.get)('/api/v1/agent/list');
  agentLoading.value = false;
  if (error || res?.errorCode !== 0) {
    agentOptions.value = [];
    ElMessage.error(res?.message || '智能体列表加载失败');
    return;
  }
  const agents = Array.isArray(res.data) ? res.data : [];
  agentOptions.value = agents
    .filter((item: any) => item?.id !== undefined && item?.id !== null)
    .map((item: any) => ({
      label: item.name || `智能体 #${item.id}`,
      value: item.id,
    }));
}

async function fetchSessions() {
  loading.value = true;
  const [, res] = await tryit(api.get)('/api/v1/chatHistory/sessions', {
    params: {
      assistantId: query.value.assistantId,
      userAccount: query.value.userAccount || undefined,
      startTime: query.value.timeRange?.[0],
      endTime: query.value.timeRange?.[1],
      pageNumber: query.value.pageNumber,
      pageSize: query.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: number | string) {
  drawerLoading.value = true;
  currentSession.value =
    sessions.value.find((item: any) => String(item.id) === String(sessionId)) ||
    undefined;
  messageList.value = [];
  loadedMessageRecordCount.value = 0;
  messagePage.value = {
    total: 0,
    pageNumber: 1,
    pageSize: 20,
  };
  drawerApi.open();

  const [, summaryRes] = await tryit(api.get)(
    `/api/v1/chatHistory/sessions/${sessionId}`,
  );
  if (summaryRes?.errorCode !== 0) {
    drawerLoading.value = false;
    drawerApi.close();
    return;
  }

  currentSession.value = summaryRes.data;

  await loadMessages(true);
  drawerLoading.value = false;
}

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

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;
  await variantSwitchController.switchVariant({
    fetchVariants: () =>
      fetchRoundVariants(String(currentSession.value.id), String(item.roundId)),
    items: messageList.value,
    persistVariant: async () => {
      const [, res] = await tryit(api.post)(
        `/api/v1/chatHistory/sessions/${currentSession.value.id}/rounds/${item.roundId}/selectVariant`,
        {
          variantIndex,
        },
      );
      if (res?.errorCode !== 0 || !res?.data) {
        throw new Error(res?.message || '答案版本切换失败');
      }
      return res.data;
    },
    roundId: item.roundId,
    sessionId: currentSession.value.id,
    targetVariantIndex: variantIndex,
  });
}

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

function prefetchVisibleVariants() {
  const sessionId = String(currentSession.value?.id || '');
  if (!sessionId) {
    return;
  }
  for (const item of messageList.value) {
    if (
      item.role === 'assistant' &&
      item.roundId &&
      Number(item.variantCount || 0) > 1
    ) {
      variantSwitchController.prefetchVariants({
        fetchVariants: () => fetchRoundVariants(sessionId, String(item.roundId)),
        roundId: item.roundId,
        sessionId,
      });
    }
  }
}

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

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

function changePageSize(pageSize: number) {
  query.value.pageNumber = 1;
  query.value.pageSize = pageSize;
  fetchSessions();
}

function formatTime(value?: string) {
  if (!value) {
    return '-';
  }
  const time = new Date(value);
  if (Number.isNaN(time.getTime())) {
    return value;
  }
  const year = time.getFullYear();
  const month = String(time.getMonth() + 1).padStart(2, '0');
  const day = String(time.getDate()).padStart(2, '0');
  const hour = String(time.getHours()).padStart(2, '0');
  const minute = String(time.getMinutes()).padStart(2, '0');
  return `${year}-${month}-${day} ${hour}:${minute}`;
}

function handleSearch() {
  query.value.pageNumber = 1;
  fetchSessions();
}

function applyQuickRange(range: 'last7' | 'last30' | 'today') {
  quickRange.value = range;
  query.value.timeRange = resolveQuickRange(range);
  handleSearch();
}

function handleTimeRangeChange(value?: string[]) {
  quickRange.value = '';
  query.value.timeRange = normalizeTimeRange(value);
  handleSearch();
}

function resolveQuickRange(range: 'last7' | 'last30' | 'today') {
  const now = new Date();
  const end = endOfDay(now);
  let start = startOfDay(now);

  if (range === 'last7') {
    start = startOfDay(addDays(now, -6));
  } else if (range === 'last30') {
    start = startOfDay(addDays(now, -29));
  }

  return [formatDateTime(start), formatDateTime(end)];
}

function normalizeTimeRange(value?: string[]) {
  if (!value?.length) {
    return [];
  }
  const [startValue, endValue] = value;
  if (!startValue || !endValue) {
    return [];
  }
  return [
    formatDateTime(startOfDay(new Date(startValue))),
    formatDateTime(endOfDay(new Date(endValue))),
  ];
}

function startOfDay(date: Date) {
  const value = new Date(date);
  value.setHours(0, 0, 0, 0);
  return value;
}

function endOfDay(date: Date) {
  const value = new Date(date);
  value.setHours(23, 59, 59, 0);
  return value;
}

function addDays(date: Date, days: number) {
  const value = new Date(date);
  value.setDate(value.getDate() + days);
  return value;
}

function formatDateTime(value: Date) {
  const year = value.getFullYear();
  const month = String(value.getMonth() + 1).padStart(2, '0');
  const day = String(value.getDate()).padStart(2, '0');
  const hour = String(value.getHours()).padStart(2, '0');
  const minute = String(value.getMinutes()).padStart(2, '0');
  const second = String(value.getSeconds()).padStart(2, '0');
  return `${year}-${month}-${day} ${hour}:${minute}:${second}`;
}

function closeDetail() {
  drawerApi.close();
}
</script>

<template>
  <div class="chat-history-page flex h-full flex-col gap-6 p-6">
    <ListPageShell class="flex-1" :content-padding="20">
      <template #filters>
        <div class="chat-history-page__filters">
          <ElSelect
            v-model="query.assistantId"
            clearable
            filterable
            placeholder="筛选智能体"
            :loading="agentLoading"
            :options="agentOptions"
            class="chat-history-page__filter-control is-select"
            @change="handleSearch"
          />
          <ElInput
            v-model="query.userAccount"
            class="chat-history-page__filter-control is-input"
            placeholder="搜索聊天用户"
            :prefix-icon="Search"
            @keyup.enter="handleSearch"
          />
          <div class="chat-history-page__quick-ranges">
            <ElButton
              v-for="item in quickRangeOptions"
              :key="item.value"
              :type="quickRange === item.value ? 'primary' : 'default'"
              class="chat-history-page__quick-range-button"
              @click="applyQuickRange(item.value)"
            >
              {{ item.label }}
            </ElButton>
          </div>
          <ElDatePicker
            v-model="query.timeRange"
            class="chat-history-page__filter-control is-range"
            type="daterange"
            value-format="YYYY-MM-DD HH:mm:ss"
            start-placeholder="开始时间"
            end-placeholder="结束时间"
            @change="handleTimeRangeChange"
          />
          <ElButton type="primary" @click="handleSearch">查询</ElButton>
        </div>
      </template>

      <div v-loading="loading" class="chat-history-page__content">
        <div v-if="sessions.length > 0" class="chat-history-page__table-shell">
          <ElTable
            :data="sessions"
            :current-row-key="selectedSessionId || undefined"
            row-key="id"
            class="chat-history-page__table"
            height="100%"
            highlight-current-row
            empty-text=""
            @row-click="(row) => openSession(row.id)"
          >
            <ElTableColumn label="会话信息" min-width="280">
              <template #default="{ row }">
                <div class="chat-history-page__session-cell">
                  <div class="chat-history-page__session-title">
                    {{ row.title || '未命名会话' }}
                  </div>
                  <div
                    v-if="row.lastMessagePreview"
                    class="chat-history-page__session-preview"
                  >
                    {{ row.lastMessagePreview }}
                  </div>
                </div>
              </template>
            </ElTableColumn>

            <ElTableColumn label="智能体" min-width="160">
              <template #default="{ row }">
                <span class="chat-history-page__assistant-chip">
                  {{ row.assistantName || '智能体' }}
                </span>
              </template>
            </ElTableColumn>

            <ElTableColumn prop="userAccount" label="聊天用户" min-width="160">
              <template #default="{ row }">
                <span class="chat-history-page__user-cell">
                  {{ row.userAccount || '-' }}
                </span>
              </template>
            </ElTableColumn>

            <ElTableColumn label="消息数" width="110" align="center">
              <template #default="{ row }">
                <span class="chat-history-page__count-pill">
                  {{ row.messageCount || 0 }}
                </span>
              </template>
            </ElTableColumn>

            <ElTableColumn label="最近活跃" width="180">
              <template #default="{ row }">
                <span class="chat-history-page__time-cell">
                  {{ formatTime(row.lastMessageAt || row.accessAt) }}
                </span>
              </template>
            </ElTableColumn>

            <ElTableColumn width="108" align="right">
              <template #default="{ row }">
                <ElButton
                  link
                  type="primary"
                  class="chat-history-page__detail-action"
                  @click.stop="openSession(row.id)"
                >
                  查看详情
                </ElButton>
              </template>
            </ElTableColumn>
          </ElTable>
        </div>

        <div v-else class="chat-history-page__empty">
          <ElEmpty description="暂无智能体聊天记录" />
        </div>

        <div v-if="pageState.total > 0" class="chat-history-page__pagination">
          <ElPagination
            background
            layout="total, sizes, prev, pager, next, jumper"
            :current-page="query.pageNumber"
            :page-size="query.pageSize"
            :page-sizes="pageSizeOptions"
            :total="pageState.total"
            @current-change="changePage"
            @size-change="changePageSize"
          />
        </div>
      </div>
    </ListPageShell>

    <Drawer>
      <ChatHistoryDetailDrawer
        :visible="true"
        :loading="drawerLoading"
        :session="currentSession"
        :messages="messageList"
        :has-more="hasMoreMessages"
        :on-load-more="() => loadMessages(false)"
        :switching-round-ids="currentSwitchingRoundIds()"
        @select-variant="handleSelectVariant($event.item, $event.direction)"
        @close="closeDetail"
      />
    </Drawer>
  </div>
</template>

<style scoped>
.chat-history-page__filters {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  align-items: center;
}

.chat-history-page__filter-control {
  min-width: 0;
}

.chat-history-page__filter-control.is-select,
.chat-history-page__filter-control.is-input {
  width: 220px;
}

.chat-history-page__filter-control.is-range {
  width: 360px;
}

.chat-history-page__quick-ranges {
  display: inline-flex;
  flex-wrap: wrap;
  gap: 8px;
}

.chat-history-page__quick-range-button {
  min-width: 88px;
}

.chat-history-page__content {
  display: flex;
  flex: 1;
  flex-direction: column;
  min-height: 0;
}

.chat-history-page__table-shell {
  flex: 1;
  min-height: 0;
  overflow: hidden;
  background: linear-gradient(
    180deg,
    hsl(var(--glass-border) / 28%) 0%,
    hsl(var(--glass-tint) / 40%) 14%,
    hsl(var(--surface-panel) / 94%) 100%
  );
  border: 1px solid hsl(var(--glass-border) / 42%);
  border-radius: 24px;
  box-shadow:
    inset 0 1px 0 hsl(var(--glass-border) / 54%),
    0 24px 42px -36px hsl(var(--foreground) / 16%);
}

.chat-history-page__table {
  height: 100%;
  background: transparent;
}

.chat-history-page__session-cell {
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-width: 0;
  padding: 2px 0;
}

.chat-history-page__session-title {
  overflow: hidden;
  text-overflow: ellipsis;
  font-size: 14px;
  font-weight: 600;
  color: hsl(var(--text-strong));
  white-space: nowrap;
}

.chat-history-page__session-preview {
  display: -webkit-box;
  overflow: hidden;
  -webkit-line-clamp: 1;
  font-size: 12px;
  line-height: 1.5;
  color: hsl(var(--text-muted));
  overflow-wrap: anywhere;
  -webkit-box-orient: vertical;
}

.chat-history-page__assistant-chip {
  display: inline-flex;
  align-items: center;
  max-width: 100%;
  min-height: 28px;
  padding: 0 12px;
  overflow: hidden;
  text-overflow: ellipsis;
  font-size: 12px;
  font-weight: 600;
  color: hsl(var(--nav-item-active-foreground));
  white-space: nowrap;
  background: hsl(var(--glass-tint) / 76%);
  border: 1px solid hsl(var(--glass-border) / 48%);
  border-radius: 999px;
}

.chat-history-page__user-cell,
.chat-history-page__time-cell {
  font-size: 13px;
  color: hsl(var(--text-secondary));
}

.chat-history-page__count-pill {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 42px;
  padding: 4px 10px;
  font-size: 12px;
  font-weight: 600;
  color: hsl(var(--text-strong));
  background: hsl(var(--surface-contrast-soft) / 88%);
  border-radius: 999px;
}

.chat-history-page__detail-action {
  padding-right: 2px;
}

.chat-history-page__empty {
  display: flex;
  flex: 1;
  align-items: center;
  justify-content: center;
  min-height: 360px;
}

.chat-history-page__pagination {
  display: flex;
  justify-content: flex-end;
  padding-top: 20px;
}

.chat-history-page__content :deep(.el-table),
.chat-history-page__content :deep(.el-table__inner-wrapper),
.chat-history-page__content :deep(.el-table__body-wrapper),
.chat-history-page__content :deep(.el-scrollbar),
.chat-history-page__content :deep(.el-scrollbar__wrap),
.chat-history-page__content :deep(.el-scrollbar__view) {
  height: 100%;
}

.chat-history-page__content :deep(.el-table::before) {
  display: none;
}

.chat-history-page__content :deep(.el-table tr) {
  cursor: pointer;
}

.chat-history-page__content :deep(.el-table th.el-table__cell) {
  height: 48px;
  font-size: 12px;
  font-weight: 600;
  color: hsl(var(--text-muted));
  background: hsl(var(--surface-contrast-soft) / 54%);
  border-bottom-color: hsl(var(--divider-faint) / 28%);
}

.chat-history-page__content :deep(.el-table td.el-table__cell) {
  padding: 14px 0;
  background: transparent;
  border-bottom-color: hsl(var(--divider-faint) / 22%);
}

.chat-history-page__content
  :deep(.el-table__body tr:hover > td.el-table__cell) {
  background: hsl(var(--primary) / 4%);
}

.chat-history-page__content
  :deep(.el-table__body tr.current-row > td.el-table__cell) {
  background: hsl(var(--primary) / 8%);
}

@media (max-width: 1024px) {
  .chat-history-page__filter-control.is-range {
    width: min(100%, 360px);
  }
}

@media (max-width: 768px) {
  .chat-history-page {
    padding: 16px;
  }

  .chat-history-page__filters {
    gap: 10px;
  }

  .chat-history-page__filter-control.is-select,
  .chat-history-page__filter-control.is-input,
  .chat-history-page__filter-control.is-range {
    width: 100%;
  }
}
</style>
