<script setup lang="ts">
import type {
  ChatTimelineItem,
  ChatTimelineMessageItem,
  ChatTimelineToolApprovalPayload,
} from '@easyflow/common-ui';

import type { AgentInfo } from '../agents/types';
import type { AgentChatSessionView } from './api';

import type {
  ChatInputTriggerGroup,
  ChatInputTriggerItem,
} from '#/components/chat-workspace/input-triggers/types';

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

import {
  ChatImageAttachments,
  ChatTimeline,
  ChatTimelineBuilder,
} from '@easyflow/common-ui';
import { copyTextToClipboard } from '@easyflow/utils';

import {
  Delete,
  EditPen,
  MoreFilled,
  Paperclip,
  Plus,
  Promotion,
} from '@element-plus/icons-vue';
import {
  ElButton,
  ElDropdown,
  ElDropdownItem,
  ElDropdownMenu,
  ElIcon,
  ElInput,
  ElMessage,
  ElMessageBox,
  ElOption,
  ElSelect,
} from 'element-plus';

import ChatCapabilityMenu from '#/components/chat-workspace/ChatCapabilityMenu.vue';
import ChatInputTriggerPanel from '#/components/chat-workspace/ChatInputTriggerPanel.vue';
import { useChatInputTrigger } from '#/components/chat-workspace/input-triggers/useChatInputTrigger';
import { useAgentComposerDraft } from '#/components/ai-chat/useAgentComposerDraft';
import { loadAgentChatImage } from '#/components/ai-chat/mediaApi';

import AgentWelcomeState from '../agents/components/AgentWelcomeState.vue';
import { resolveInteractionDisplay } from '../agents/interaction-config';
import { recordsToTimelineItems } from './adapters/agentTimelineAdapter';
import { agentChatRuntimeManager } from './agentChatRuntimeManager';
import {
  approveAgentRun,
  deleteAgentSession,
  getAgentConversation,
  getAgentSession,
  getAgentSessions,
  getPublishedAgents,
  getPublishedKnowledges,
  rejectAgentRun,
  renameAgentSession,
  saveAgentSessionExtraKnowledges,
} from './api';
import {
  isMissingAgentSessionError,
  resolveAgentSessionErrorMessage,
} from './sessionRecovery';

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

const agents = ref<AgentInfo[]>([]);
const sessions = ref<AgentChatSessionView[]>([]);
const timelineItems = ref<ChatTimelineItem[]>([]);
const selectedAgentId = ref('');
const currentSessionId = ref('');
const composer = useAgentComposerDraft('FORMAL');
const promptText = composer.text;
const promptInputRef = ref();
const imageFileInputRef = ref<HTMLInputElement>();
const composerDragActive = ref(false);
const loadingAgents = ref(false);
const agentLoadError = ref('');
const loadingSessions = ref(false);
const loadingConversation = ref(false);
const loadingKnowledges = ref(false);
const savingExtraKnowledges = ref(false);
const sending = ref(false);
const runtimeRunning = ref(false);
const approvalLoadingKey = ref('');
const knowledgeOptions = ref<{ label: string; value: string }[]>([]);
const knowledgeMap = ref(new Map<string, { id: string; title: string }>());
const extraKnowledgeIds = ref<string[]>([]);
const runtimeSendingState = new Map<string, boolean>();
const MAX_EXTRA_KNOWLEDGE_COUNT = 3;
let runtimeUnsubscribe: (() => void) | undefined;

const selectedAgent = computed(() =>
  agents.value.find((agent) => String(agent.id) === selectedAgentId.value),
);
const selectedAgentImageSupport = computed(() =>
  Boolean(
    selectedAgent.value?.publishedSnapshotJson?.modelSummary?.supportImage,
  ),
);
const interactionDisplay = computed(() =>
  resolveInteractionDisplay(selectedAgent.value),
);
const currentSession = computed(() =>
  sessions.value.find(
    (session) => String(session.sessionId) === currentSessionId.value,
  ),
);
const canStopRuntime = computed(() => sending.value || runtimeRunning.value);
const canSend = computed(
  () =>
    (Boolean(promptText.value.trim()) ||
      composer.images.readyItems.value.length > 0) &&
    !composer.images.uploading.value &&
    !composer.images.items.value.some((item) => item.status === 'error') &&
    (selectedAgentImageSupport.value !== false ||
      composer.images.readyItems.value.length === 0) &&
    Boolean(selectedAgentId.value) &&
    !sending.value &&
    !runtimeRunning.value,
);
const composerPlaceholder = computed(() =>
  selectedAgent.value
    ? interactionDisplay.value.inputPlaceholder
    : '请选择智能体',
);
const selectedExtraKnowledges = computed(() => {
  const knowledges: { id: string; title: string }[] = [];
  for (const id of extraKnowledgeIds.value) {
    const knowledge = knowledgeMap.value.get(String(id));
    if (knowledge) {
      knowledges.push(knowledge);
    }
  }
  return knowledges;
});
const capabilityDisabled = computed(
  () =>
    sending.value ||
    runtimeRunning.value ||
    savingExtraKnowledges.value ||
    !selectedAgentId.value,
);
const isWelcomeState = computed(
  () =>
    Boolean(selectedAgent.value) &&
    !loadingAgents.value &&
    !loadingConversation.value &&
    !currentSessionId.value &&
    timelineItems.value.length === 0,
);
const agentSelectWidth = computed(() => {
  const name = selectedAgent.value?.name || '选择智能体';
  const textWidth = [...name].reduce(
    (total, char) => total + (/[\u4E00-\u9FFF]/.test(char) ? 15 : 8),
    0,
  );
  return `${Math.min(Math.max(textWidth + 36, 116), 320)}px`;
});
const triggerGroups = computed<ChatInputTriggerGroup[]>(() => [
  {
    items: knowledgeOptions.value.map((item) => {
      const selected = extraKnowledgeIds.value.includes(String(item.value));
      return {
        disabled:
          !selected &&
          extraKnowledgeIds.value.length >= MAX_EXTRA_KNOWLEDGE_COUNT,
        id: item.value,
        label: item.label,
      };
    }),
    label: '知识库',
    symbol: '@',
  },
]);
const chatInputTrigger = useChatInputTrigger({
  disabled: capabilityDisabled,
  groups: triggerGroups,
  inputRef: promptInputRef,
  text: promptText,
});

function formatDate(value?: string) {
  if (!value) {
    return '';
  }
  const date = new Date(value);
  if (Number.isNaN(date.getTime())) {
    return '';
  }
  return date.toLocaleString(undefined, {
    day: '2-digit',
    hour: '2-digit',
    minute: '2-digit',
    month: '2-digit',
  });
}

function sessionTitle(session: AgentChatSessionView) {
  return session.title || session.lastMessagePreview || '新对话';
}

function sessionPreview(session: AgentChatSessionView) {
  return session.lastMessagePreview || session.assistantName || '暂无消息';
}

function copyMessageText(item: ChatTimelineMessageItem) {
  return item.parts
    .filter((part) => part.type === 'text')
    .map((part) => part.content)
    .join('')
    .trim();
}

function canCopyMessage(item: ChatTimelineMessageItem) {
  if (item.role === 'user') {
    return Boolean(copyMessageText(item));
  }
  return Boolean(item.roundId && item.roundCompleted && copyMessageText(item));
}

async function syncSessionRoute(sessionId?: string) {
  const nextQuery = { ...route.query };
  if (sessionId) {
    nextQuery.sessionId = sessionId;
  } else {
    delete nextQuery.sessionId;
  }
  if (String(route.query.sessionId || '') === String(sessionId || '')) {
    return;
  }
  await router.replace({ query: nextQuery });
}

async function removeSessionFromPage(sessionId: string) {
  sessions.value = sessions.value.filter(
    (item) => String(item.sessionId) !== sessionId,
  );
  const isCurrentSession =
    currentSessionId.value === sessionId ||
    String(route.query.sessionId || '') === sessionId;
  if (isCurrentSession) {
    try {
      await composer.clear();
    } catch (error) {
      ElMessage.warning(
        error instanceof Error ? error.message : '会话草稿清理失败',
      );
    }
    await createNewSession();
  }
}

async function loadAgents() {
  loadingAgents.value = true;
  agentLoadError.value = '';
  try {
    const res = await getPublishedAgents();
    if (res.errorCode !== 0) {
      throw new Error(res.message || '智能体加载失败');
    }
    agents.value = Array.isArray(res.data) ? res.data : [];
    if (!selectedAgentId.value && agents.value[0]?.id) {
      selectedAgentId.value = String(agents.value[0].id);
    }
  } catch (error) {
    agentLoadError.value =
      error instanceof Error ? error.message : '智能体加载失败';
    ElMessage.error(agentLoadError.value);
  } finally {
    loadingAgents.value = false;
  }
}

async function loadSessions() {
  loadingSessions.value = true;
  try {
    const res = await getAgentSessions({ pageNumber: 1, pageSize: 80 });
    if (res.errorCode !== 0) {
      throw new Error(res.message || '历史会话加载失败');
    }
    sessions.value = Array.isArray(res.data?.records) ? res.data.records : [];
  } catch (error) {
    ElMessage.error(
      error instanceof Error ? error.message : '历史会话加载失败',
    );
  } finally {
    loadingSessions.value = false;
  }
}

async function loadKnowledges() {
  loadingKnowledges.value = true;
  try {
    const res = await getPublishedKnowledges();
    if (res.errorCode !== 0) {
      throw new Error(res.message || '知识库加载失败');
    }
    const records = Array.isArray(res.data) ? res.data : [];
    knowledgeOptions.value = records
      .filter((item) => item?.id)
      .map((item) => ({
        label: item.title || item.alias || String(item.id),
        value: String(item.id),
      }));
    knowledgeMap.value = new Map(
      records
        .filter((item) => item?.id)
        .map((item) => [
          String(item.id),
          {
            id: String(item.id),
            title: item.title || item.alias || String(item.id),
          },
        ]),
    );
  } catch (error) {
    ElMessage.error(error instanceof Error ? error.message : '知识库加载失败');
  } finally {
    loadingKnowledges.value = false;
  }
}

function resolveSessionSortTime(session: AgentChatSessionView) {
  const time = new Date(
    session.lastMessageAt || session.accessAt || '',
  ).getTime();
  return Number.isFinite(time) ? time : Number.NEGATIVE_INFINITY;
}

function sortSessionRecords(records: AgentChatSessionView[]) {
  return [...records].sort((a, b) => {
    const timeDiff = resolveSessionSortTime(b) - resolveSessionSortTime(a);
    if (timeDiff !== 0) {
      return timeDiff;
    }
    return String(b.sessionId || '').localeCompare(String(a.sessionId || ''));
  });
}

function upsertSessionRecord(session: AgentChatSessionView) {
  const sessionId = String(session.sessionId || '');
  if (!sessionId) {
    return;
  }
  const next = [...sessions.value];
  const currentIndex = next.findIndex(
    (item) => String(item.sessionId) === sessionId,
  );
  if (currentIndex === -1) {
    next.push({
      ...session,
      sessionId,
    });
  } else {
    next.splice(currentIndex, 1, {
      ...next[currentIndex],
      ...session,
      sessionId,
    });
  }
  sessions.value = sortSessionRecords(next);
}

function sleep(delay: number) {
  return new Promise((resolve) => setTimeout(resolve, delay));
}

async function refreshSessionRecord(sessionId?: string) {
  const currentId = String(sessionId || '');
  if (!currentId) {
    return;
  }
  let lastError: unknown;
  for (const delay of [0, 240, 480]) {
    if (delay > 0) {
      await sleep(delay);
    }
    try {
      const res = await getAgentSession(currentId);
      if (res.errorCode !== 0 || !res.data) {
        throw new Error(res.message || '会话信息刷新失败');
      }
      upsertSessionRecord({
        ...res.data,
        sessionId: currentId,
      });
      return;
    } catch (error) {
      lastError = error;
    }
  }
  ElMessage.error(
    lastError instanceof Error ? lastError.message : '会话信息刷新失败',
  );
}

function maybeRefreshCompletedRuntimeSession(snapshot: {
  sending: boolean;
  sessionId: string;
}) {
  const wasSending = runtimeSendingState.get(snapshot.sessionId) ?? false;
  runtimeSendingState.set(snapshot.sessionId, snapshot.sending);
  if (wasSending && !snapshot.sending) {
    void refreshSessionRecord(snapshot.sessionId);
  }
}

function buildOptimisticSession(
  sessionId: string,
  prompt: string,
): AgentChatSessionView {
  return {
    assistantId: selectedAgentId.value,
    assistantName: selectedAgent.value?.name,
    continuable: true,
    lastMessageAt: new Date().toISOString(),
    lastMessagePreview: prompt || '发送了图片',
    messageCount: 1,
    sessionId,
    title: prompt.slice(0, 48) || '图片对话',
  };
}

function syncRuntimeSnapshot(sessionId = currentSessionId.value) {
  runtimeRunning.value = agentChatRuntimeManager.hasRunning();
  const snapshot = sessionId
    ? agentChatRuntimeManager.getSnapshot(sessionId)
    : undefined;
  if (!snapshot) {
    sending.value = false;
    return false;
  }
  currentSessionId.value = snapshot.sessionId;
  selectedAgentId.value = String(snapshot.agentId);
  timelineItems.value = snapshot.items;
  sending.value = snapshot.sending;
  maybeRefreshCompletedRuntimeSession(snapshot);
  if (snapshot.prompt && !currentSession.value) {
    upsertSessionRecord(
      buildOptimisticSession(snapshot.sessionId, snapshot.prompt),
    );
  }
  void syncSessionRoute(snapshot.sessionId);
  return true;
}

function persistCurrentRuntimeItems() {
  if (currentSessionId.value) {
    agentChatRuntimeManager.replaceItems(
      currentSessionId.value,
      timelineItems.value,
    );
  }
}

async function loadConversation(sessionId: string) {
  if (!sessionId) {
    timelineItems.value = [];
    sending.value = false;
    return;
  }
  const runtimeSnapshot = agentChatRuntimeManager.getSnapshot(sessionId);
  if (runtimeSnapshot?.sending) {
    syncRuntimeSnapshot(sessionId);
    await syncSessionRoute(sessionId);
    return;
  }
  loadingConversation.value = true;
  try {
    const detailRes = await getAgentSession(sessionId);
    const res = await getAgentConversation(sessionId);
    const latestRuntimeSnapshot =
      agentChatRuntimeManager.getSnapshot(sessionId);
    if (latestRuntimeSnapshot?.sending) {
      syncRuntimeSnapshot(sessionId);
      await syncSessionRoute(sessionId);
      return;
    }
    if (res.errorCode !== 0) {
      throw new Error(res.message || '会话加载失败');
    }
    timelineItems.value = recordsToTimelineItems(res.data?.records || []);
    const session =
      detailRes.errorCode === 0 && detailRes.data
        ? detailRes.data
        : sessions.value.find((item) => String(item.sessionId) === sessionId);
    if (session?.assistantId) {
      selectedAgentId.value = String(session.assistantId);
    }
    if (detailRes.errorCode === 0 && detailRes.data) {
      extraKnowledgeIds.value = (detailRes.data.extraKnowledges || [])
        .map((item) => String(item.id || ''))
        .filter(Boolean);
      for (const item of detailRes.data.extraKnowledges || []) {
        if (!item.id) {
          continue;
        }
        knowledgeMap.value.set(String(item.id), {
          id: String(item.id),
          title: item.title || item.alias || String(item.id),
        });
      }
      if ((detailRes.data.removedExtraKnowledgeNames || []).length > 0) {
        const removedNames = detailRes.data.removedExtraKnowledgeNames || [];
        ElMessage.warning(`以下知识库已失效并移除：${removedNames.join('、')}`);
      }
    }
    currentSessionId.value = sessionId;
    if (selectedAgentId.value) {
      await activateComposer(selectedAgentId.value, sessionId);
    }
    sending.value = false;
    await syncSessionRoute(sessionId);
  } catch (error) {
    if (isMissingAgentSessionError(error)) {
      await removeSessionFromPage(sessionId);
      return;
    }
    ElMessage.error(resolveAgentSessionErrorMessage(error) || '会话加载失败');
  } finally {
    loadingConversation.value = false;
  }
}

async function createNewSession() {
  currentSessionId.value = '';
  timelineItems.value = [];
  extraKnowledgeIds.value = [];
  sending.value = false;
  if (selectedAgentId.value) {
    try {
      await composer.startNew(selectedAgentId.value);
    } catch (error) {
      ElMessage.warning(
        error instanceof Error ? error.message : '新会话创建失败',
      );
    }
  }
  await syncSessionRoute();
}

async function bindCreatedSession(sessionId: string, prompt: string) {
  if (!sessionId) {
    return;
  }
  currentSessionId.value = sessionId;
  const existingIndex = sessions.value.findIndex(
    (session) => String(session.sessionId) === sessionId,
  );
  const nextSession = buildOptimisticSession(sessionId, prompt);
  if (existingIndex === -1) {
    sessions.value = [nextSession, ...sessions.value];
  } else {
    upsertSessionRecord(nextSession);
  }
  await syncSessionRoute(sessionId);
}

async function handleAgentChange() {
  extraKnowledgeIds.value = [];
  if (timelineItems.value.length > 0 || currentSessionId.value) {
    await createNewSession();
  } else {
    await activateComposer(selectedAgentId.value);
  }
  if (
    selectedAgentImageSupport.value === false &&
    composer.images.items.value.length > 0
  ) {
    ElMessage.warning('当前智能体不支持图片，请先移除图片');
  }
}

async function handleExtraKnowledgeIdsChange(value: string[]) {
  const previousIds = [...extraKnowledgeIds.value];
  const nextIds = value.map(String);
  extraKnowledgeIds.value = nextIds;
  if (!currentSessionId.value) {
    return;
  }
  savingExtraKnowledges.value = true;
  try {
    const res = await saveAgentSessionExtraKnowledges(
      currentSessionId.value,
      nextIds,
    );
    if (res.errorCode !== 0 || !res.data) {
      throw new Error(res.message || '知识库保存失败');
    }
    extraKnowledgeIds.value = (res.data.extraKnowledges || [])
      .map((item) => String(item.id || ''))
      .filter(Boolean);
    upsertSessionRecord({
      ...res.data,
      sessionId: currentSessionId.value,
    });
  } catch (error) {
    extraKnowledgeIds.value = previousIds;
    ElMessage.error(error instanceof Error ? error.message : '知识库保存失败');
  } finally {
    savingExtraKnowledges.value = false;
  }
}

async function handleTriggerSelect(item: ChatInputTriggerItem) {
  if (item.disabled) {
    return;
  }
  if (chatInputTrigger.activePanel.value?.symbol !== '@') {
    await chatInputTrigger.replaceTriggerText('');
    return;
  }
  const nextIds = extraKnowledgeIds.value.map(String);
  if (!nextIds.includes(String(item.id))) {
    nextIds.push(String(item.id));
    await handleExtraKnowledgeIdsChange(nextIds);
  }
  await chatInputTrigger.replaceTriggerText('');
}

function buildCapabilities() {
  return [
    {
      resourceIds: [...extraKnowledgeIds.value],
      type: 'KNOWLEDGE' as const,
    },
  ];
}

async function sendContent(rawContent: string) {
  const content = rawContent.trim();
  if (
    (!content && composer.images.readyItems.value.length === 0) ||
    !selectedAgentId.value ||
    sending.value
  ) {
    return;
  }
  if (runtimeRunning.value) {
    ElMessage.warning('当前回复完成后再发送新消息');
    return;
  }
  if (composer.images.uploading.value) {
    ElMessage.warning('图片上传完成后再发送');
    return;
  }
  const failedImage = composer.images.items.value.find(
    (item) => item.status === 'error',
  );
  if (failedImage) {
    ElMessage.error(failedImage.error || '请处理上传失败的图片');
    return;
  }
  await composer.flush();
  sending.value = true;
  try {
    const sessionId = await agentChatRuntimeManager.start({
      agentId: selectedAgentId.value,
      agentName: selectedAgent.value?.name,
      baseItems: timelineItems.value,
      capabilities: buildCapabilities(),
      imageUploadIds: composer.images.uploadIds.value,
      images: composer.images.readyItems.value.map((item) => ({ ...item })),
      onInputAccepted: () =>
        composer.markAccepted().catch(() => {
          ElMessage.warning('消息已发送，草稿将在过期后自动清理');
        }),
      prompt: content,
      sessionId: composer.sessionId.value,
    });
    await bindCreatedSession(sessionId, content);
    syncRuntimeSnapshot(sessionId);
  } catch (error) {
    sending.value = false;
    ElMessage.error(
      error instanceof Error ? error.message : '发送失败，请稍后再试',
    );
  }
}

async function handleSend() {
  await sendContent(promptText.value);
}

function handleSuggestedQuestion(question: string) {
  void sendContent(question);
}

function handlePromptInput() {
  chatInputTrigger.sync();
  composer.scheduleSave();
}

async function activateComposer(agentId: string, sessionId?: string) {
  if (!agentId) return;
  try {
    await composer.activate(agentId, sessionId);
  } catch (error) {
    ElMessage.warning(
      error instanceof Error ? error.message : '输入草稿恢复失败',
    );
  }
}

function chooseImageFiles() {
  imageFileInputRef.value?.click();
}

async function addImageFiles(files: File[]) {
  if (!selectedAgentId.value) {
    ElMessage.warning('请先选择智能体');
    return;
  }
  if (selectedAgentImageSupport.value === false) {
    ElMessage.warning('当前智能体不支持图片');
    return;
  }
  await composer.ensureSession();
  const rejected = await composer.images.addFiles(files, {
    agentId: selectedAgentId.value,
    mode: 'FORMAL',
    sessionId: composer.sessionId.value,
  });
  composer.scheduleSave();
  if (rejected > 0) {
    ElMessage.warning('每次最多添加 5 张图片');
  }
}

function handleImageFiles(event: Event) {
  const target = event.target as HTMLInputElement;
  const files = [...(target.files || [])];
  if (files.length) void addImageFiles(files);
  target.value = '';
}

function handleImagePaste(event: ClipboardEvent) {
  const files = [...(event.clipboardData?.files || [])].filter((file) =>
    file.type.startsWith('image/'),
  );
  if (!files.length) return;
  event.preventDefault();
  void addImageFiles(files);
}

function handleImageDragEnter(event: DragEvent) {
  if (
    selectedAgentImageSupport.value !== false &&
    !capabilityDisabled.value &&
    composer.images.items.value.length < 5 &&
    [...(event.dataTransfer?.types || [])].includes('Files')
  ) {
    composerDragActive.value = true;
  }
}

function handleImageDragLeave(event: DragEvent) {
  const container = event.currentTarget as HTMLElement;
  if (
    !(event.relatedTarget instanceof Node) ||
    !container.contains(event.relatedTarget)
  ) {
    composerDragActive.value = false;
  }
}

function handleImageDrop(event: DragEvent) {
  composerDragActive.value = false;
  if (
    selectedAgentImageSupport.value === false ||
    capabilityDisabled.value ||
    composer.images.items.value.length >= 5
  )
    return;
  const files = [...(event.dataTransfer?.files || [])].filter((file) =>
    file.type.startsWith('image/'),
  );
  if (files.length) void addImageFiles(files);
}

async function retryImage(item: any) {
  await composer.images.retry(item, {
    agentId: selectedAgentId.value,
    mode: 'FORMAL',
    sessionId: composer.sessionId.value,
  });
  composer.scheduleSave();
}

async function removeImage(item: any) {
  try {
    await composer.images.remove(item);
    composer.scheduleSave();
  } catch (error) {
    ElMessage.error(error instanceof Error ? error.message : '图片删除失败');
  }
}

function handlePromptKeyup() {
  chatInputTrigger.sync();
}

function handlePromptClick() {
  chatInputTrigger.sync();
}

function handlePromptKeydown(event: Event | KeyboardEvent) {
  if (!(event instanceof KeyboardEvent)) {
    return;
  }
  if (chatInputTrigger.activePanel.value) {
    if (event.key === 'ArrowDown') {
      event.preventDefault();
      chatInputTrigger.move(1);
      return;
    }
    if (event.key === 'ArrowUp') {
      event.preventDefault();
      chatInputTrigger.move(-1);
      return;
    }
    if (event.key === 'Escape') {
      event.preventDefault();
      chatInputTrigger.close();
      return;
    }
    if (event.key === 'Enter' && !event.shiftKey) {
      const item =
        chatInputTrigger.visibleItems.value[chatInputTrigger.activeIndex.value];
      if (item) {
        event.preventDefault();
        void handleTriggerSelect(item);
        return;
      }
    }
  }

  if (event.key === 'Enter' && !event.shiftKey) {
    event.preventDefault();
    void handleSend();
  }
}

function handleStop() {
  if (!canStopRuntime.value) {
    return;
  }
  const runningSessionId =
    agentChatRuntimeManager.getSnapshot(currentSessionId.value)?.sending &&
    currentSessionId.value
      ? currentSessionId.value
      : agentChatRuntimeManager.getLatestSnapshot()?.sessionId;
  agentChatRuntimeManager.stop(runningSessionId);
  if (runningSessionId === currentSessionId.value) {
    syncRuntimeSnapshot(currentSessionId.value);
  } else {
    runtimeRunning.value = agentChatRuntimeManager.hasRunning();
  }
  void refreshSessionRecord(runningSessionId);
}

async function handleCopyMessage(item: ChatTimelineMessageItem) {
  const text = copyMessageText(item);
  if (!text) {
    return false;
  }
  try {
    await copyTextToClipboard(text);
    return true;
  } catch (error) {
    console.error('复制消息失败:', error);
    ElMessage.error('复制失败');
    return false;
  }
}

async function handleRenameSession(session: AgentChatSessionView) {
  const sessionId = String(session.sessionId || '');
  if (!sessionId) {
    return;
  }
  try {
    const { value } = await ElMessageBox.prompt('请输入会话名称', '重命名', {
      confirmButtonText: '保存',
      inputValue: sessionTitle(session),
      inputValidator: (value) => Boolean(String(value || '').trim()),
      inputErrorMessage: '名称不能为空',
    });
    const title = String(value || '').trim();
    const res = await renameAgentSession(sessionId, title);
    if (res.errorCode !== 0) {
      throw new Error(res.message || '重命名失败');
    }
    await loadSessions();
    ElMessage.success('已重命名');
  } catch (error) {
    if (error !== 'cancel') {
      ElMessage.error(error instanceof Error ? error.message : '重命名失败');
    }
  }
}

async function handleDeleteSession(session: AgentChatSessionView) {
  const sessionId = String(session.sessionId || '');
  if (!sessionId) {
    return;
  }
  try {
    await ElMessageBox.confirm('删除后不可恢复，确认删除该会话？', '删除会话', {
      confirmButtonText: '删除',
      cancelButtonText: '取消',
      type: 'warning',
    });
    const res = await deleteAgentSession(sessionId);
    if (res.errorCode !== 0) {
      throw new Error(res.message || '删除失败');
    }
    await removeSessionFromPage(sessionId);
    ElMessage.success('已删除');
  } catch (error) {
    if (error !== 'cancel') {
      if (isMissingAgentSessionError(error)) {
        await removeSessionFromPage(sessionId);
        return;
      }
      ElMessage.error(resolveAgentSessionErrorMessage(error) || '删除失败');
    }
  }
}

async function handleApprove(payload: ChatTimelineToolApprovalPayload) {
  approvalLoadingKey.value = payload.toolCallId || payload.requestId;
  ChatTimelineBuilder.markToolApproving(timelineItems.value, payload);
  persistCurrentRuntimeItems();
  try {
    const res = await approveAgentRun(payload.requestId, payload.resumeToken);
    if (res.errorCode !== 0) {
      throw new Error(res.message || '批准失败');
    }
    ElMessage.success('已批准');
  } catch (error) {
    ChatTimelineBuilder.appendToolApproval(timelineItems.value, payload);
    persistCurrentRuntimeItems();
    ElMessage.error(error instanceof Error ? error.message : '批准失败');
  } finally {
    approvalLoadingKey.value = '';
  }
}

async function handleReject(payload: ChatTimelineToolApprovalPayload) {
  approvalLoadingKey.value = payload.toolCallId || payload.requestId;
  ChatTimelineBuilder.markToolApproving(timelineItems.value, payload);
  persistCurrentRuntimeItems();
  try {
    const res = await rejectAgentRun(
      payload.requestId,
      payload.resumeToken,
      '用户拒绝执行',
    );
    if (res.errorCode !== 0) {
      throw new Error(res.message || '拒绝失败');
    }
    ChatTimelineBuilder.markToolRejected(timelineItems.value, {
      ...payload,
      reason: '用户拒绝执行',
    });
    persistCurrentRuntimeItems();
  } catch (error) {
    ChatTimelineBuilder.appendToolApproval(timelineItems.value, payload);
    persistCurrentRuntimeItems();
    ElMessage.error(error instanceof Error ? error.message : '拒绝失败');
  } finally {
    approvalLoadingKey.value = '';
  }
}

async function bootstrap() {
  await Promise.all([loadAgents(), loadSessions(), loadKnowledges()]);
  const routeSessionId = String(route.query.sessionId || '');
  if (routeSessionId) {
    await loadConversation(routeSessionId);
    return;
  }
  const latestSnapshot = agentChatRuntimeManager.getLatestSnapshot();
  if (latestSnapshot?.items.length) {
    syncRuntimeSnapshot(latestSnapshot.sessionId);
    await activateComposer(selectedAgentId.value, latestSnapshot.sessionId);
    return;
  }
  await activateComposer(selectedAgentId.value);
}

onMounted(() => {
  runtimeUnsubscribe = agentChatRuntimeManager.subscribe(() => {
    syncRuntimeSnapshot();
  });
  void bootstrap();
});

onBeforeUnmount(() => {
  runtimeUnsubscribe?.();
  runtimeUnsubscribe = undefined;
});
</script>

<template>
  <section class="agent-chat">
    <aside class="agent-chat__history">
      <div class="agent-chat__history-head">
        <div class="agent-chat__title">智能体聊天</div>
        <ElButton
          :icon="Plus"
          circle
          text
          aria-label="新建会话"
          title="新建会话"
          @click="createNewSession"
        />
      </div>
      <div v-if="loadingSessions" class="agent-chat__state">加载中</div>
      <div v-else-if="sessions.length === 0" class="agent-chat__state">
        暂无历史会话
      </div>
      <div v-else class="agent-chat__session-list">
        <button
          v-for="session in sessions"
          :key="String(session.sessionId)"
          type="button"
          class="agent-chat__session"
          :class="{
            'is-active': String(session.sessionId) === currentSessionId,
          }"
          @click="loadConversation(String(session.sessionId))"
        >
          <span class="agent-chat__session-main">
            <span class="agent-chat__session-title">{{
              sessionTitle(session)
            }}</span>
            <span class="agent-chat__session-preview">
              {{ sessionPreview(session) }}
            </span>
          </span>
          <span class="agent-chat__session-meta">
            {{ formatDate(session.lastMessageAt || session.accessAt) }}
          </span>
          <ElDropdown
            trigger="click"
            class="agent-chat__session-more"
            @click.stop
          >
            <ElButton
              :icon="MoreFilled"
              circle
              text
              aria-label="会话操作"
              @click.stop
            />
            <template #dropdown>
              <ElDropdownMenu>
                <ElDropdownItem @click="handleRenameSession(session)">
                  <ElIcon><EditPen /></ElIcon>
                  重命名
                </ElDropdownItem>
                <ElDropdownItem @click="handleDeleteSession(session)">
                  <ElIcon><Delete /></ElIcon>
                  删除
                </ElDropdownItem>
              </ElDropdownMenu>
            </template>
          </ElDropdown>
        </button>
      </div>
    </aside>

    <main class="agent-chat__main">
      <header class="agent-chat__main-head">
        <div>
          <div class="agent-chat__main-title">
            {{ selectedAgent?.name || '智能体聊天' }}
          </div>
          <div class="agent-chat__main-subtitle">
            {{ currentSession ? sessionTitle(currentSession) : '新会话' }}
          </div>
        </div>
      </header>

      <div
        class="agent-chat__timeline-wrap"
        :class="{ 'is-welcome': isWelcomeState }"
      >
        <div
          v-if="loadingConversation || loadingAgents"
          class="agent-chat__state is-center"
        >
          加载中
        </div>
        <div
          v-else-if="
            agentLoadError && !currentSessionId && timelineItems.length === 0
          "
          class="agent-chat__state is-center"
        >
          {{ agentLoadError }}
        </div>
        <div
          v-else-if="
            !selectedAgent && !currentSessionId && timelineItems.length === 0
          "
          class="agent-chat__state is-center"
        >
          暂无已发布智能体
        </div>
        <AgentWelcomeState
          v-else-if="isWelcomeState"
          :agent-name="selectedAgent?.name || '智能体'"
          :avatar="selectedAgent?.avatar"
          :disabled="sending || runtimeRunning"
          :suggested-questions="interactionDisplay.suggestedQuestions"
          :welcome-message="interactionDisplay.welcomeMessage"
          @select-question="handleSuggestedQuestion"
        />
        <ChatTimeline
          v-else
          :items="timelineItems"
          :image-loader="loadAgentChatImage"
          empty-text="选择智能体后开始对话"
          :approval-loading="Boolean(approvalLoadingKey)"
          :copy-action="handleCopyMessage"
          :copyable="canCopyMessage"
          @approve="handleApprove"
          @reject="handleReject"
          @select-next-variant="() => undefined"
          @select-previous-variant="() => undefined"
        />
      </div>

      <div
        class="agent-chat__composer"
        :class="{ 'is-dragging': composerDragActive }"
        @dragenter.prevent="handleImageDragEnter"
        @dragover.prevent
        @dragleave.prevent="handleImageDragLeave"
        @drop.prevent="handleImageDrop"
      >
        <ChatCapabilityMenu
          :disabled="capabilityDisabled"
          :extra-knowledge-ids="extraKnowledgeIds"
          :knowledge-options="knowledgeOptions"
          :loading="loadingKnowledges"
          :selected-knowledges="selectedExtraKnowledges"
          :show-trigger="false"
          @update:extra-knowledge-ids="handleExtraKnowledgeIdsChange"
        />
        <ChatInputTriggerPanel
          v-if="chatInputTrigger.activePanel.value"
          class="agent-chat__trigger-panel"
          :active-index="chatInputTrigger.activeIndex.value"
          :group-label="chatInputTrigger.activePanel.value.groupLabel"
          :items="chatInputTrigger.visibleItems.value"
          :keyword="chatInputTrigger.activePanel.value.keyword"
          @select="handleTriggerSelect"
          @set-active="chatInputTrigger.setActiveIndex"
        />
        <ChatImageAttachments
          v-if="composer.images.items.value.length"
          :items="composer.images.items.value"
          :image-loader="loadAgentChatImage"
          removable
          retryable
          @remove="removeImage"
          @retry="retryImage"
        />
        <ElInput
          ref="promptInputRef"
          v-model="promptText"
          class="agent-chat__composer-input"
          type="textarea"
          :autosize="{ minRows: 2, maxRows: 6 }"
          resize="none"
          :placeholder="composerPlaceholder"
          :disabled="sending || runtimeRunning || !selectedAgentId"
          @click="handlePromptClick"
          @input="handlePromptInput"
          @keydown="handlePromptKeydown"
          @keyup="handlePromptKeyup"
          @paste="handleImagePaste"
        />
        <div class="agent-chat__composer-footer">
          <div class="agent-chat__composer-tools">
            <template v-if="selectedAgentImageSupport !== false">
              <input
                ref="imageFileInputRef"
                class="agent-chat__image-file-input"
                type="file"
                accept=".png,.jpg,.jpeg,.webp,.gif,.bmp,image/png,image/jpeg,image/webp,image/gif,image/bmp"
                multiple
                @change="handleImageFiles"
              />
              <ElButton
                :icon="Paperclip"
                circle
                text
                :disabled="
                  capabilityDisabled || composer.images.items.value.length >= 5
                "
                aria-label="添加图片"
                title="添加图片"
                @click="chooseImageFiles"
              />
            </template>
            <ChatCapabilityMenu
              class="agent-chat__capability-entry"
              :disabled="capabilityDisabled"
              :extra-knowledge-ids="extraKnowledgeIds"
              :knowledge-options="knowledgeOptions"
              :loading="loadingKnowledges"
              :selected-knowledges="[]"
              @update:extra-knowledge-ids="handleExtraKnowledgeIdsChange"
            />
            <ElSelect
              v-model="selectedAgentId"
              :loading="loadingAgents"
              placeholder="选择智能体"
              class="agent-chat__agent-select"
              :style="{ width: agentSelectWidth }"
              @change="handleAgentChange"
            >
              <ElOption
                v-for="agent in agents"
                :key="String(agent.id)"
                :label="agent.name || String(agent.id)"
                :value="String(agent.id)"
              />
            </ElSelect>
          </div>
          <div class="agent-chat__composer-actions">
            <ElButton
              v-if="canStopRuntime"
              type="primary"
              circle
              aria-label="中止"
              title="中止"
              class="agent-chat__send-button is-stop"
              @click="handleStop"
            >
              <span class="agent-chat__stop-glyph"></span>
            </ElButton>
            <ElButton
              v-else
              type="primary"
              circle
              :icon="Promotion"
              aria-label="发送"
              title="发送"
              class="agent-chat__send-button"
              :disabled="!canSend"
              @click="handleSend"
            />
          </div>
        </div>
      </div>
    </main>
  </section>
</template>

<style scoped>
.agent-chat {
  box-sizing: border-box;
  display: grid;
  grid-template-columns: 280px minmax(0, 1fr);
  height: var(--easyflow-content-height, 100%);
  min-height: 0;
  max-height: var(--easyflow-content-height, 100%);
  overflow: hidden;
  background: var(--el-bg-color-page);
}

.agent-chat__history {
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 0;
  overflow: hidden;
  background: var(--el-bg-color);
  border-right: 1px solid var(--el-border-color-lighter);
}

.agent-chat__history-head,
.agent-chat__main-head {
  display: flex;
  gap: 12px;
  align-items: center;
  justify-content: space-between;
  min-height: 56px;
  padding: 12px 16px;
  border-bottom: 1px solid var(--el-border-color-lighter);
}

.agent-chat__title,
.agent-chat__main-title {
  overflow: hidden;
  text-overflow: ellipsis;
  font-size: 16px;
  font-weight: 600;
  color: var(--el-text-color-primary);
  white-space: nowrap;
}

.agent-chat__main-subtitle {
  margin-top: 4px;
  overflow: hidden;
  text-overflow: ellipsis;
  font-size: 12px;
  color: var(--el-text-color-secondary);
  white-space: nowrap;
}

.agent-chat__session-list {
  display: flex;
  flex: 1;
  flex-direction: column;
  gap: 4px;
  min-height: 0;
  padding: 8px;
  overflow: auto;
}

.agent-chat__session {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 4px 8px;
  width: 100%;
  min-height: 72px;
  padding: 10px 44px 10px 10px;
  color: inherit;
  text-align: left;
  cursor: pointer;
  background: transparent;
  border: 0;
  border-radius: 8px;
}

.agent-chat__session:hover,
.agent-chat__session.is-active {
  background: var(--el-fill-color-light);
}

.agent-chat__session-main {
  min-width: 0;
  padding-right: 8px;
}

.agent-chat__session-title,
.agent-chat__session-preview {
  display: block;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.agent-chat__session-title {
  font-size: 13px;
  font-weight: 600;
  color: var(--el-text-color-primary);
}

.agent-chat__session-preview,
.agent-chat__session-meta,
.agent-chat__state {
  font-size: 12px;
  color: var(--el-text-color-secondary);
}

.agent-chat__session-preview {
  margin-top: 6px;
}

.agent-chat__session-meta {
  white-space: nowrap;
}

.agent-chat__session-more {
  position: absolute;
  top: 50%;
  right: 10px;
  transform: translateY(-50%);
}

.agent-chat__main {
  position: relative;
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 0;
  overflow: hidden;
}

.agent-chat__timeline-wrap {
  box-sizing: border-box;
  display: flex;
  flex: 1;
  flex-direction: column;
  min-height: 0;
  overflow: hidden;
}

.agent-chat__timeline-wrap.is-welcome {
  padding: 0 min(8vw, 96px);
  overflow: hidden auto;
}

.agent-chat__timeline-wrap :deep(.chat-timeline) {
  box-sizing: border-box;
  height: 100%;
  min-height: 0;
  padding: 24px min(8vw, 96px);
  overflow: auto;
}

.agent-chat__composer {
  position: relative;
  display: flex;
  flex: none;
  flex-direction: column;
  gap: 8px;
  padding: 16px;
  margin: 0 min(8vw, 96px) 24px;
  background: var(--el-bg-color);
  border: 1px solid var(--el-border-color-lighter);
  border-radius: 24px;
  box-shadow: var(--el-box-shadow-light);
}

.agent-chat__composer.is-dragging {
  background: var(--el-color-primary-light-9);
  border-color: var(--el-color-primary-light-5);
}

.agent-chat__trigger-panel {
  position: absolute;
  bottom: calc(100% + 10px);
  left: 0;
  z-index: 5;
  width: 100%;
}

.agent-chat__composer-input :deep(.el-textarea__inner) {
  min-height: 48px !important;
  padding: 0;
  line-height: 22px;
  background: transparent;
  border: 0;
  box-shadow: none;
}

.agent-chat__composer-footer {
  display: flex;
  gap: 16px;
  align-items: flex-end;
  justify-content: space-between;
}

.agent-chat__composer-tools {
  display: inline-flex;
  gap: 4px;
  align-items: center;
  min-width: 0;
  max-width: calc(100% - 64px);
}

.agent-chat__image-file-input {
  display: none;
}

.agent-chat__capability-entry {
  flex: none;
}

.agent-chat__agent-select {
  max-width: min(320px, calc(100vw - 240px));
}

.agent-chat__agent-select :deep(.el-select__wrapper) {
  min-height: 36px;
  padding: 0 4px 0 0;
  background: transparent;
  border: 0;
  box-shadow: none;
}

.agent-chat__agent-select :deep(.el-select__wrapper.is-focused) {
  box-shadow: none;
}

.agent-chat__agent-select :deep(.el-select__placeholder),
.agent-chat__agent-select :deep(.el-select__selected-item) {
  min-width: 0;
  max-width: none;
  overflow: hidden;
  text-overflow: ellipsis;
  font-size: 14px;
  font-weight: 400;
  color: var(--el-text-color-secondary);
  white-space: nowrap;
}

.agent-chat__agent-select :deep(.el-select__caret) {
  margin-left: 6px;
  color: var(--el-color-primary);
}

.agent-chat__composer-actions {
  display: inline-flex;
  flex: none;
  align-items: center;
}

.agent-chat__send-button {
  width: 42px;
  min-width: 42px;
  height: 42px;
  padding: 0;
  border: 0;
  border-radius: 999px;
  box-shadow: none;
}

.agent-chat__send-button :deep(.el-icon) {
  font-size: 15px;
}

.agent-chat__send-button.is-stop {
  border: 0;
}

.agent-chat__stop-glyph {
  display: block;
  width: 12px;
  height: 12px;
  background: currentcolor;
  border-radius: 3px;
}

.agent-chat__state {
  padding: 16px;
}

.agent-chat__state.is-center {
  display: flex;
  flex: 1;
  align-items: center;
  justify-content: center;
}

@media (max-width: 900px) {
  .agent-chat {
    grid-template-rows: auto minmax(0, 1fr);
    grid-template-columns: 1fr;
  }

  .agent-chat__history {
    max-height: 168px;
    border-right: 0;
    border-bottom: 1px solid var(--el-border-color-lighter);
  }

  .agent-chat__session-list {
    flex-direction: row;
    overflow-x: auto;
  }

  .agent-chat__session {
    flex: 0 0 240px;
  }

  .agent-chat__timeline-wrap {
    padding-bottom: 0;
  }

  .agent-chat__timeline-wrap.is-welcome {
    padding: 0 16px;
  }

  .agent-chat__timeline-wrap.is-welcome :deep(.agent-welcome) {
    flex: 0 0 auto;
    min-height: 100%;
    padding-bottom: 16px;
  }

  .agent-chat__timeline-wrap :deep(.chat-timeline) {
    padding: 16px;
  }

  .agent-chat__composer {
    margin: 0 16px 16px;
  }

  .agent-chat__composer-footer {
    align-items: flex-end;
  }

  .agent-chat__agent-select {
    width: min(280px, calc(100% - 58px));
    max-width: calc(100vw - 128px);
  }

  .agent-chat__composer-actions {
    justify-content: flex-end;
  }
}
</style>
