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

import type {
  AgentInfo,
  AgentKnowledgeBinding,
  AgentSkillBinding,
  AgentToolBinding,
} from '../types';

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

import {
  CHAT_DOCUMENT_SUPPORTED_FORMATS,
  ChatTimeline,
  defaultAssistantAvatar,
} from '@easyflow/common-ui';
import { BrushCleaning } from '@easyflow/icons';
import { copyTextToClipboard } from '@easyflow/utils';

import { FullScreen } from '@element-plus/icons-vue';
import { ElButton, ElMessage } from 'element-plus';

import AiChatPanel from '#/components/ai-chat/AiChatPanel.vue';
import {
  createAgentArtifactLoader,
  loadAgentChatDocument,
  loadAgentChatImage,
} from '#/components/ai-chat/mediaApi';
import { useAgentComposerDraft } from '#/components/ai-chat/useAgentComposerDraft';
import { CHAT_IMAGE_SUPPORTED_FORMATS } from '#/components/ai-chat/useChatImageUploads';

import { approveAgentRun, rejectAgentRun } from '../api';
import { useAgentTryoutStream } from '../composables/useAgentTryoutStream';
import { resolveInteractionDisplay } from '../interaction-config';
import AgentWelcomeState from './AgentWelcomeState.vue';

const props = defineProps<{
  agent: AgentInfo;
  expanded?: boolean;
  imageEnabled?: boolean;
  knowledgeBindings: AgentKnowledgeBinding[];
  skillBindings: AgentSkillBinding[];
  toolBindings: AgentToolBinding[];
}>();

const emit = defineEmits<{
  close: [];
  'update:expanded': [value: boolean];
}>();

const {
  loading,
  clearDraftSession,
  dispose,
  markToolApproving,
  markToolRejected,
  copyMessageText,
  selectVariant,
  syncDraftContext,
  timelineItems,
  sendDraft,
  stop,
} = useAgentTryoutStream();
const approvalLoading = ref(false);
const clearing = ref(false);
const composer = useAgentComposerDraft('DRAFT');
const loadDraftAgentArtifact = createAgentArtifactLoader(() => ({
  agentId: String(props.agent.id || ''),
  mode: 'DRAFT',
  runtimeSessionId: composer.sessionId.value,
}));
const interactionDisplay = computed(() =>
  resolveInteractionDisplay(props.agent),
);

function getDraftContext() {
  return {
    agent: props.agent,
    skillBindings: props.skillBindings,
    toolBindings: props.toolBindings,
    knowledgeBindings: props.knowledgeBindings,
  };
}

function markComposerInputAccepted() {
  return composer.markAccepted().catch(() => {
    ElMessage.warning('消息已发送，草稿将在过期后自动清理');
  });
}

function syncCurrentDraftContext(restore = false) {
  syncDraftContext(
    getDraftContext(),
    restore,
    composer.sessionId.value,
    markComposerInputAccepted,
  );
}

async function activateComposer() {
  const agentId = String(props.agent.id || '');
  if (!agentId) return;
  try {
    await composer.activate(agentId, `agent-draft-${agentId}`);
    syncCurrentDraftContext(true);
  } catch (error) {
    ElMessage.warning(
      error instanceof Error ? error.message : '输入草稿恢复失败',
    );
  }
}

onMounted(() => void activateComposer());
onBeforeUnmount(() => {
  dispose();
});

watch(
  () => [props.agent.id, props.agent.localId],
  () => void activateComposer(),
);

watch(
  () => [
    props.agent,
    props.knowledgeBindings,
    props.skillBindings,
    props.toolBindings,
  ],
  () => {
    syncCurrentDraftContext();
  },
  { deep: true },
);

async function handleSend(prompt: string) {
  if (loading.value || approvalLoading.value) return;
  if (composer.images.uploading.value) {
    ElMessage.warning('图片上传完成后再发送');
    return;
  }
  if (composer.documents.processing.value) {
    ElMessage.warning('文档读取完成后再发送');
    return;
  }
  const failedImage = composer.images.items.value.find(
    (item) => item.status === 'error',
  );
  if (failedImage) {
    ElMessage.error(failedImage.error || '请处理上传失败的图片');
    return;
  }
  const failedDocument = composer.documents.items.value.find(
    (item) => item.status === 'error',
  );
  if (failedDocument) {
    ElMessage.error(failedDocument.error || '请处理读取失败的文档');
    return;
  }
  await composer.flush();
  await sendDraft({
    ...getDraftContext(),
    documentUploadIds: composer.documents.uploadIds.value,
    documents: composer.documents.readyItems.value.map((item) => ({
      ...item,
    })),
    prompt,
    imageUploadIds: composer.images.uploadIds.value,
    images: composer.images.readyItems.value.map((item) => ({ ...item })),
    sessionId: composer.sessionId.value,
    onAccepted: markComposerInputAccepted,
  });
}

function handleDraftTextInput() {
  composer.scheduleSave();
}

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

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

function canRegenerateMessage(item: ChatTimelineMessageItem) {
  return (
    item.role === 'assistant' &&
    Boolean(item.roundId) &&
    Boolean(item.roundCompleted)
  );
}

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

async function handleRegenerateMessage(item: ChatTimelineMessageItem) {
  void item;
}

function handleSelectPreviousVariant(item: ChatTimelineMessageItem) {
  selectVariant(item, 'previous');
}

function handleSelectNextVariant(item: ChatTimelineMessageItem) {
  selectVariant(item, 'next');
}

async function handleClearSession() {
  if (loading.value || approvalLoading.value || clearing.value) {
    return;
  }
  clearing.value = true;
  try {
    await clearDraftSession();
    await composer.clear();
    await activateComposer();
    ElMessage.success('已清理会话');
  } catch (error) {
    ElMessage.error(error instanceof Error ? error.message : '清理会话失败');
  } finally {
    clearing.value = false;
  }
}

async function handleAddImageFiles(files: File[]) {
  if (!props.agent.id) {
    ElMessage.warning('请先保存智能体');
    return;
  }
  await composer.ensureSession();
  const { rejectedCount, unsupportedCount } = await composer.images.addFiles(
    files,
    {
      agentId: String(props.agent.id),
      mode: 'DRAFT',
      sessionId: composer.sessionId.value,
    },
  );
  if (unsupportedCount > 0) {
    ElMessage.warning({
      grouping: true,
      message: `不支持该图片格式，支持：${CHAT_IMAGE_SUPPORTED_FORMATS}`,
    });
  }
  if (rejectedCount > 0) {
    ElMessage.warning('每次最多添加 5 张图片');
  }
  composer.scheduleSave();
}

async function handleAddDocumentFiles(files: File[]) {
  if (!props.agent.id) {
    ElMessage.warning('请先保存智能体');
    return;
  }
  await composer.ensureSession();
  const { rejectedCount, unsupportedCount } = await composer.documents.addFiles(
    files,
    {
      agentId: String(props.agent.id),
      mode: 'DRAFT',
      sessionId: composer.sessionId.value,
    },
  );
  if (unsupportedCount > 0) {
    ElMessage.warning({
      grouping: true,
      message: `不支持该文件格式，支持：${CHAT_DOCUMENT_SUPPORTED_FORMATS}`,
    });
  }
  if (rejectedCount > 0) {
    ElMessage.warning('每轮最多添加 3 份文档');
  }
  composer.scheduleSave();
}

async function handleRetryImage(item: ChatImageAttachment) {
  await composer.images.retry(item, {
    agentId: String(props.agent.id),
    mode: 'DRAFT',
    sessionId: composer.sessionId.value,
  });
  composer.scheduleSave();
}

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

async function handleRetryDocument(item: ChatDocumentAttachment) {
  await composer.documents.retry(item, {
    agentId: String(props.agent.id),
    mode: 'DRAFT',
    sessionId: composer.sessionId.value,
  });
  composer.scheduleSave();
}

async function handleRemoveDocument(item: ChatDocumentAttachment) {
  try {
    await composer.documents.remove(item);
    composer.scheduleSave();
  } catch (error) {
    ElMessage.error(error instanceof Error ? error.message : '文档删除失败');
  }
}

async function handleStop() {
  if (!loading.value) {
    return;
  }
  try {
    await stop();
  } catch (error) {
    ElMessage.error(error instanceof Error ? error.message : '停止失败');
  }
}

async function handleApprove(payload: ChatTimelineToolApprovalPayload) {
  approvalLoading.value = true;
  markToolApproving(payload);
  try {
    const res = await approveAgentRun(payload.approvalId);
    if (res.errorCode === 0) {
      ElMessage.success('已批准');
    }
  } catch (error) {
    markToolRejected({
      ...payload,
      reason: error instanceof Error ? error.message : '批准失败',
    });
    throw error;
  } finally {
    approvalLoading.value = false;
  }
}

async function handleReject(payload: ChatTimelineToolApprovalPayload) {
  approvalLoading.value = true;
  markToolRejected({
    ...payload,
    reason: '用户拒绝执行',
  });
  try {
    await rejectAgentRun(payload.approvalId, '用户拒绝执行');
  } finally {
    approvalLoading.value = false;
  }
}
</script>

<template>
  <AiChatPanel
    v-model="composer.text.value"
    :title="agent.name || '草稿试运行'"
    empty-text="输入问题试运行当前智能体"
    closable
    :messages="[]"
    :loading="loading"
    :documents="composer.documents.items.value"
    :document-loader="loadAgentChatDocument"
    :images="composer.images.items.value"
    :image-enabled="imageEnabled"
    :image-loader="loadAgentChatImage"
    :placeholder="interactionDisplay.inputPlaceholder"
    :approval-loading="approvalLoading"
    @send="handleSend"
    @update:model-value="handleDraftTextInput"
    @add-files="handleAddImageFiles"
    @add-document-files="handleAddDocumentFiles"
    @remove-document="handleRemoveDocument"
    @remove-image="handleRemoveImage"
    @retry-document="handleRetryDocument"
    @retry-image="handleRetryImage"
    @stop="handleStop"
    @approve="handleApprove"
    @reject="handleReject"
    @close="emit('close')"
  >
    <template #headerActions>
      <ElButton
        :icon="FullScreen"
        circle
        text
        :aria-label="expanded ? '收起试运行' : '展开试运行'"
        :aria-pressed="Boolean(expanded)"
        :title="expanded ? '收起试运行' : '展开试运行'"
        @click="emit('update:expanded', !expanded)"
      />
      <ElButton
        :icon="BrushCleaning"
        circle
        text
        :disabled="loading || approvalLoading || clearing"
        :loading="clearing"
        aria-label="清理会话"
        :title="loading ? '输出结束后可清理会话' : '清理会话'"
        @click="handleClearSession"
      />
    </template>
    <div class="agent-tryout__conversation">
      <AgentWelcomeState
        v-if="timelineItems.length === 0"
        :agent-name="agent.name || '智能体'"
        :avatar="agent.avatar"
        :disabled="loading || approvalLoading"
        :suggested-questions="interactionDisplay.suggestedQuestions"
        :welcome-message="interactionDisplay.welcomeMessage"
        @select-question="handleSuggestedQuestion"
      />
      <ChatTimeline
        v-else
        :assistant-avatar="agent.avatar || defaultAssistantAvatar"
        :artifact-loader="loadDraftAgentArtifact"
        :items="timelineItems"
        :document-loader="loadAgentChatDocument"
        :image-loader="loadAgentChatImage"
        empty-text="输入问题试运行当前智能体"
        :approval-loading="approvalLoading"
        :copy-action="handleCopyMessage"
        :copyable="canCopyMessage"
        :regenerable="canRegenerateMessage"
        :regenerate-disabled="true"
        @approve="handleApprove"
        @regenerate-message="handleRegenerateMessage"
        @reject="handleReject"
        @select-next-variant="handleSelectNextVariant"
        @select-previous-variant="handleSelectPreviousVariant"
      />
    </div>
  </AiChatPanel>
</template>

<style scoped>
.agent-tryout__conversation {
  display: flex;
  flex: 1;
  min-height: 0;
  padding: 0 var(--space-4);
  overflow: auto;
  overscroll-behavior: contain;
}

.agent-tryout__conversation :deep(.chat-timeline) {
  width: 100%;
  min-height: 0;
}
</style>
