<script setup lang="ts">
import type { AIWorkflow } from '#/api/ai-platform/ai-platform';

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

import {
  ArrowLeft,
  BookOpen,
  Bot,
  Brain,
  CircleHelp,
  Clock,
  Code,
  Combine,
  Fullscreen,
  GitBranch,
  GitFork,
  Globe,
  HelpCircle,
  LayoutTemplate,
  ListChecks,
  Map,
  MessageSquareText,
  Minus,
  Play,
  Plus,
  Redo2,
  RefreshCw,
  Square,
  Undo2,
  Workflow,
} from '@vben/icons';
import { $t } from '@vben/locales';

import { MarkerType, useVueFlow, VueFlow } from '@vue-flow/core';
import { MiniMap } from '@vue-flow/minimap';
import {
  ElButton,
  ElInput,
  ElMessage,
  ElMessageBox,
  ElScrollbar,
} from 'element-plus';

import {
  getWorkflowDetailApi,
  publishWorkflowApi,
  runWorkflowStreamApi,
  updateWorkflowApi,
} from '#/api/ai-platform/ai-platform';
import { useAppContextStore } from '#/store/app-context';
import { useWorkflowEditorStore } from '#/store/workflow-editor';

import RunDialog from './components/RunDialog.vue';
import ContextMenu from './components/ContextMenu.vue';
import Panel from './components/Panel.vue';
import VersionHistoryPanel from './components/VersionHistoryPanel.vue';
import {
  workflowEdgeTypes,
  workflowNodeTypes,
} from '../shared/workflowNodeTypes';

import '@vue-flow/minimap/dist/style.css';
import '@vue-flow/core/dist/style.css';
import '@vue-flow/core/dist/theme-default.css';
import '@vue-flow/controls/dist/style.css';

const AiChatPanel = defineAsyncComponent(
  () => import('#/components/ai-chat-panel/AiChatPanel.vue'),
);

const appContextStore = useAppContextStore();

const route = useRoute();
const router = useRouter();
const workflowId = route.params.id as string;

// 使用 Pinia store 管理工作流编辑器状态
const workflowEditorStore = useWorkflowEditorStore();

const workflow = ref<AIWorkflow | null>(null);
const loading = ref(false);
const selectedNode = ref<any>(null);
const showRunPanel = ref(false);
const showRunDialog = ref(false);
const showVersionHistory = ref(false);
let cancelWorkflowStream: (() => void) | null = null;

const contextMenu = ref({
  show: false,
  x: 0,
  y: 0,
  node: null as any,
});

const nodeTypes = workflowNodeTypes;
const edgeTypes = workflowEdgeTypes;

const {
  addNodes,
  onConnect,
  addEdges,
  toObject,
  screenToFlowCoordinate,
  onNodeClick,
  onPaneClick,
  nodes,
  onNodeContextMenu,
  removeNodes,
  edges,
  removeEdges,
  onNodesChange,
  onEdgesChange,
  zoomIn,
  zoomOut,
  fitView,
  onViewportChange,
  updateEdge,
  onEdgeUpdateStart,
  onEdgeUpdateEnd,
  updateNodeInternals,
  setNodes,
  setEdges,
} = useVueFlow();

// 监听节点变化，检测循环节点删除并级联删除子节点
watch(
  () => nodes.value.map((n) => n.id),
  (newIds: string[], oldIds: string[] | undefined) => {
    if (!oldIds) return;

    const newIdSet = new Set(newIds);

    // 找出被删除的节点
    const deletedIds = oldIds.filter((id: string) => !newIdSet.has(id));

    if (deletedIds.length > 0) {
      // 查找需要级联删除的子节点（parentNode 指向已删除的节点）
      const orphanedChildren = nodes.value.filter(
        (n) => n.parentNode && deletedIds.includes(n.parentNode),
      );

      if (orphanedChildren.length > 0) {
        console.log(
          '[Loop Delete] Cascade deleting orphaned children:',
          orphanedChildren.map((n) => n.id),
        );
        nextTick(() => {
          removeNodes(orphanedChildren.map((n) => n.id));
        });
      }
    }
  },
  { deep: false },
);

// 当前缩放比例
const currentZoom = ref(100);

// 小地图显示状态
const showMiniMap = ref(true);

// 组件库弹出状态
const showNodePalette = ref(false);
const nodePaletteRef = ref<HTMLElement | null>(null);
const nodeSearchQuery = ref('');

// 节点上的快速添加弹出框
const quickAddPopup = ref<{
  position: { x: number; y: number };
  show: boolean;
  sourceNodeId: string;
}>({
  show: false,
  sourceNodeId: '',
  position: { x: 0, y: 0 },
});
const quickAddRef = ref<HTMLElement | null>(null);

// 边上的插入节点弹出框
const edgeAddPopup = ref<{
  edgeId: string;
  position: { x: number; y: number };
  show: boolean;
  source: string;
  target: string;
}>({
  show: false,
  edgeId: '',
  source: '',
  target: '',
  position: { x: 0, y: 0 },
});
const edgeAddRef = ref<HTMLElement | null>(null);

// 点击外部关闭组件面板
const handleClickOutside = (event: MouseEvent) => {
  const target = event.target as HTMLElement;

  // 关闭底部工具栏的组件面板
  if (
    showNodePalette.value &&
    nodePaletteRef.value &&
    !nodePaletteRef.value.contains(target)
  ) {
    showNodePalette.value = false;
  }

  // 关闭节点上的快速添加弹出框
  if (
    quickAddPopup.value.show &&
    quickAddRef.value &&
    !quickAddRef.value.contains(target)
  ) {
    quickAddPopup.value.show = false;
  }

  // 关闭边上的插入节点弹出框
  if (
    edgeAddPopup.value.show &&
    edgeAddRef.value &&
    !edgeAddRef.value.contains(target)
  ) {
    edgeAddPopup.value.show = false;
  }
};

// 节点配置列表
// 获取节点标签的辅助函数
const getNodeLabel = (type: string): string => {
  return $t(`ai-platform.workflow.editor.nodeLabels.${type}`) || type;
};

const nodeCategories = computed(() => [
  {
    name: $t('ai-platform.workflow.editor.categories.basic'),
    nodes: [
      {
        type: 'start',
        label: getNodeLabel('start'),
        icon: 'Play',
        color: 'blue',
      },
      { type: 'llm', label: getNodeLabel('llm'), icon: 'Bot', color: 'blue' },
      {
        type: 'http',
        label: getNodeLabel('http'),
        icon: 'Globe',
        color: 'blue',
      },
      {
        type: 'code',
        label: getNodeLabel('code'),
        icon: 'Code',
        color: 'blue',
      },
      {
        type: 'template',
        label: getNodeLabel('template'),
        icon: 'LayoutTemplate',
        color: 'blue',
      },
    ],
  },
  {
    name: $t('ai-platform.workflow.editor.categories.dialog'),
    nodes: [
      {
        type: 'message',
        label: getNodeLabel('message'),
        icon: 'MessageSquareText',
        color: 'blue',
      },
      {
        type: 'question',
        label: getNodeLabel('question'),
        icon: 'HelpCircle',
        color: 'blue',
      },
      {
        type: 'choice',
        label: getNodeLabel('choice'),
        icon: 'ListChecks',
        color: 'blue',
      },
      {
        type: 'confirm',
        label: getNodeLabel('confirm'),
        icon: 'CircleHelp',
        color: 'blue',
      },
    ],
  },
  {
    name: $t('ai-platform.workflow.editor.categories.flowControl'),
    nodes: [
      {
        type: 'condition',
        label: getNodeLabel('condition'),
        icon: 'GitBranch',
        color: 'purple',
      },
      {
        type: 'intent',
        label: getNodeLabel('intent'),
        icon: 'Brain',
        color: 'purple',
      },
      {
        type: 'parallel',
        label: getNodeLabel('parallel'),
        icon: 'GitFork',
        color: 'purple',
      },
      {
        type: 'merge',
        label: getNodeLabel('merge'),
        icon: 'Combine',
        color: 'purple',
      },
      {
        type: 'loop',
        label: getNodeLabel('loop'),
        icon: 'RefreshCw',
        color: 'purple',
      },
      {
        type: 'subflow',
        label: getNodeLabel('subflow'),
        icon: 'Workflow',
        color: 'purple',
      },
      {
        type: 'end',
        label: getNodeLabel('end'),
        icon: 'Square',
        color: 'purple',
      },
    ],
  },
  {
    name: $t('ai-platform.workflow.editor.categories.knowledge'),
    nodes: [
      {
        type: 'knowledge_retrieval',
        label: getNodeLabel('knowledge_retrieval'),
        icon: 'BookOpen',
        color: 'teal',
      },
    ],
  },
]);

// 图标映射
const iconComponents: Record<string, any> = {
  Play,
  Bot,
  Globe,
  Code,
  GitBranch,
  LayoutTemplate,
  GitFork,
  Combine,
  Square,
  // 对话交互节点图标
  MessageSquareText,
  HelpCircle,
  ListChecks,
  CircleHelp,
  // 意图识别节点图标
  Brain,
  // Snowflake Cortex 节点图标
  // 循环节点图标
  RefreshCw,
  // 表单节点图标
  // 应用节点图标
  // 仪表盘节点图标
  // 系统总结节点图标
  // 子流程节点图标
  Workflow,
  // 知识库节点图标
  BookOpen,
};

// 过滤后的节点分类
const filteredNodeCategories = computed(() => {
  const categories = nodeCategories.value;

  if (!nodeSearchQuery.value.trim()) {
    return categories;
  }

  const query = nodeSearchQuery.value.toLowerCase();
  return categories
    .map(
      (category: {
        name: string;
        nodes: { color: string; icon: string; label: string; type: string }[];
      }) => ({
        ...category,
        nodes: category.nodes.filter(
          (node: { label: string; type: string }) =>
            node.label.toLowerCase().includes(query) ||
            node.type.toLowerCase().includes(query),
        ),
      }),
    )
    .filter((category: { nodes: any[] }) => category.nodes.length > 0);
});

// 监听视口变化（包括滚轮缩放）
onViewportChange((viewport) => {
  currentZoom.value = Math.round(viewport.zoom * 100);
});

// 撤销/重做 - 使用 Pinia store
const canUndo = computed(() => workflowEditorStore.canUndo);
const canRedo = computed(() => workflowEditorStore.canRedo);

// 保存历史状态
const saveHistory = () => {
  if (workflowEditorStore.isUndoRedo) return;
  workflowEditorStore.saveHistory({
    nodes: nodes.value,
    edges: edges.value,
  });
};

// 撤销
const handleUndo = () => {
  const state = workflowEditorStore.undo();
  if (state) {
    // 使用 setNodes 和 setEdges 正确恢复状态
    setNodes(state.nodes);
    setEdges(state.edges);
    // 更新节点内部状态
    nextTick(() => {
      updateNodeInternals(nodes.value.map((n) => n.id));
    });
  }
};

// 重做
const handleRedo = () => {
  const state = workflowEditorStore.redo();
  if (state) {
    setNodes(state.nodes);
    setEdges(state.edges);
    nextTick(() => {
      updateNodeInternals(nodes.value.map((n) => n.id));
    });
  }
};

// 监听节点和边变化，保存历史并触发自动保存
onNodesChange((changes) => {
  // 处理子节点拖拽限制
  handleChildNodeDrag(changes);
  saveHistory();
  triggerAutoSave();
});

// 处理子节点拖拽，确保子节点不会被拖出循环体，并自动调整父节点大小
const handleChildNodeDrag = (changes: any[]) => {
  // 收集需要重新计算大小的父节点
  const parentNodesToResize = new Set<string>();
  // 收集需要级联删除的子节点
  const childNodesToDelete: string[] = [];

  for (const change of changes) {
    // 处理节点删除
    if (change.type === 'remove') {
      // 查找被删除节点的子节点（在节点被删除前，子节点的 parentNode 仍然指向它）
      const childNodes = nodes.value.filter((n) => n.parentNode === change.id);
      if (childNodes.length > 0) {
        console.log(
          '[Loop Delete] Found child nodes to delete:',
          childNodes.map((n) => n.id),
        );
        for (const child of childNodes) {
          childNodesToDelete.push(child.id);
        }
      }

      // 检查被删除的是否是子节点，需要重新计算父节点大小
      const node = nodes.value.find((n) => n.id === change.id);
      if (node?.parentNode) {
        parentNodesToResize.add(node.parentNode);
      }
      continue;
    }

    // 只处理位置变化
    if (change.type !== 'position') continue;

    const node = nodes.value.find((n) => n.id === change.id);
    if (!node || !node.parentNode) continue;

    // 找到父节点（循环节点）
    const parentNode = nodes.value.find((n) => n.id === node.parentNode);
    if (!parentNode || parentNode.type !== 'loop') continue;

    // 获取父节点的尺寸
    const parentWidth = parentNode.data?.width || 400;
    const parentHeight = parentNode.data?.height || 350;

    // 获取该父节点的所有子节点
    const childNodes = nodes.value.filter(
      (n) => n.parentNode === parentNode.id,
    );
    const childCount = childNodes.length;

    // 子节点的大约尺寸
    const childWidth = node.dimensions?.width || 280;
    const childHeight = node.dimensions?.height || 150;

    // 边距和头部区域（头部 56px + 配置信息区域 50px = 106px，加上一些余量）
    const padding = 20;
    const headerHeight = 120;

    // 当前位置
    const posX = change.position?.x ?? node.position.x;
    const posY = change.position?.y ?? node.position.y;

    if (childCount === 1) {
      // 单个子节点时，强制限制在父节点范围内
      const maxX = parentWidth - childWidth - padding;
      const maxY = parentHeight - childHeight - padding;
      const newX = Math.max(padding, Math.min(posX, maxX));
      const newY = Math.max(headerHeight, Math.min(posY, maxY));

      // 如果位置超出范围，强制修正
      if (posX !== newX || posY !== newY) {
        node.position = { x: newX, y: newY };
      }
    } else {
      // 多个子节点时，自动扩展父节点区域
      // 先限制不能拖到头部区域和左边界外
      const newX = Math.max(padding, posX);
      const newY = Math.max(headerHeight, posY);
      if (posX !== newX || posY !== newY) {
        node.position = { x: newX, y: newY };
      }

      // 计算需要的父节点尺寸
      const requiredWidth = newX + childWidth + padding;
      const requiredHeight = newY + childHeight + padding;

      // 扩展父节点区域
      if (requiredWidth > parentWidth || requiredHeight > parentHeight) {
        parentNode.data = {
          ...parentNode.data,
          width: Math.max(parentWidth, requiredWidth),
          height: Math.max(parentHeight, requiredHeight),
        };
      }

      // 拖拽结束时，标记需要重新计算大小
      if (!change.dragging) {
        parentNodesToResize.add(parentNode.id);
      }
    }
  }

  // 重新计算需要调整大小的父节点（使用 nextTick 确保节点状态已更新）
  if (parentNodesToResize.size > 0) {
    nextTick(() => {
      for (const parentId of parentNodesToResize) {
        resizeLoopNodeToFitChildren(parentId);
      }
    });
  }

  // 级联删除子节点
  if (childNodesToDelete.length > 0) {
    // 使用 nextTick 确保在当前变更处理完成后再删除
    nextTick(() => {
      removeNodes(childNodesToDelete);
    });
  }
};

// 根据子节点位置重新计算循环节点的最小尺寸
const resizeLoopNodeToFitChildren = (parentId: string) => {
  const parentNode = nodes.value.find((n) => n.id === parentId);
  if (!parentNode || parentNode.type !== 'loop') return;

  const childNodes = nodes.value.filter((n) => n.parentNode === parentId);

  // 默认最小尺寸
  const minWidth = 400;
  const minHeight = 350;
  const padding = 20;

  if (childNodes.length === 0) {
    // 没有子节点，恢复默认尺寸
    parentNode.data = {
      ...parentNode.data,
      width: minWidth,
      height: minHeight,
    };
    return;
  }

  // 计算所有子节点需要的最大边界
  let maxRight = 0;
  let maxBottom = 0;

  for (const child of childNodes) {
    const childWidth = child.dimensions?.width || 280;
    const childHeight = child.dimensions?.height || 150;
    const right = child.position.x + childWidth + padding;
    const bottom = child.position.y + childHeight + padding;

    maxRight = Math.max(maxRight, right);
    maxBottom = Math.max(maxBottom, bottom);
  }

  // 计算新的尺寸，但不小于最小尺寸
  const newWidth = Math.max(minWidth, maxRight);
  const newHeight = Math.max(minHeight, maxBottom);

  // 检查尺寸是否有变化
  const oldWidth = parentNode.data?.width || minWidth;
  const oldHeight = parentNode.data?.height || minHeight;
  const sizeChanged = newWidth !== oldWidth || newHeight !== oldHeight;

  if (!sizeChanged) return;

  // 更新父节点尺寸
  parentNode.data = {
    ...parentNode.data,
    width: newWidth,
    height: newHeight,
  };

  // 同时更新节点的 dimensions 属性，让 VueFlow 知道节点尺寸变化
  parentNode.dimensions = {
    width: newWidth,
    height: newHeight,
  };

  // 触发节点内部更新，重新计算连线位置
  // 使用双重 nextTick 确保 DOM 更新完成后再更新节点内部
  nextTick(() => {
    nextTick(() => {
      updateNodeInternals([parentId]);
    });
  });
};

onEdgesChange(() => {
  saveHistory();
  triggerAutoSave();
});

// 连线逻辑
onConnect((params) => {
  addEdges([{ ...params, type: 'addButton' }]);
});

// 边更新逻辑 - 拖拽边端点时
const edgeUpdateSuccessful = ref(true);

onEdgeUpdateStart(() => {
  edgeUpdateSuccessful.value = false;
});

onEdgeUpdateEnd(({ edge }) => {
  // 如果边没有成功更新（即拖拽到空白处），则删除该边
  if (!edgeUpdateSuccessful.value) {
    removeEdges([edge.id]);
  }
  edgeUpdateSuccessful.value = true;
});

// 边更新成功时的处理
const handleEdgeUpdate = ({
  edge,
  connection,
}: {
  connection: any;
  edge: any;
}) => {
  edgeUpdateSuccessful.value = true;
  // 更新边的连接
  updateEdge(edge, connection);
};

// 默认边样式 - 使用自定义边类型
const defaultEdgeOptions = {
  type: 'addButton', // 使用自定义边类型，支持悬停添加按钮
  animated: false,
  style: { strokeWidth: 2, stroke: '#94a3b8' },
  markerEnd: MarkerType.ArrowClosed,
  deletable: true,
};

// 选中的边
const selectedEdge = ref<null | string>(null);

// 选中逻辑
onNodeClick(({ node }) => {
  selectedNode.value = node;
  selectedEdge.value = null;
});

// 边点击 - 选中边
const onEdgeClick = (edgeEvent: { edge: any }) => {
  selectedEdge.value = edgeEvent.edge.id;
  selectedNode.value = null;
};

onPaneClick(() => {
  selectedNode.value = null;
  selectedEdge.value = null;
  contextMenu.value.show = false;
});

// 右键菜单
onNodeContextMenu(({ node, event }) => {
  event.preventDefault();
  const mouseEvent = event as MouseEvent;
  contextMenu.value = {
    show: true,
    x: mouseEvent.clientX,
    y: mouseEvent.clientY,
    node,
  };
});

const handleDuplicate = () => {
  if (!contextMenu.value.node) return;
  const sourceNode = contextMenu.value.node;
  const timestamp = Date.now();

  // 复制主节点
  const newNode = JSON.parse(JSON.stringify(sourceNode));
  newNode.id = `${newNode.type}-${timestamp}`;
  newNode.position = {
    x: newNode.position.x + 50,
    y: newNode.position.y + 50,
  };
  newNode.selected = false;

  // 如果是循环节点，需要复制内部的子节点和连线
  if (sourceNode.type === 'loop') {
    // 获取所有子节点（parentNode === sourceNode.id）
    const childNodes = nodes.value.filter(
      (n: any) => n.parentNode === sourceNode.id,
    );

    // 创建旧ID到新ID的映射
    const idMap: Record<string, string> = {
      [sourceNode.id]: newNode.id,
    };

    // 复制子节点
    const newChildNodes: any[] = [];
    for (const child of childNodes) {
      const newChildId = `${child.type}-${timestamp}-${Math.random().toString(36).slice(2, 8)}`;
      idMap[child.id] = newChildId;

      const newChild = JSON.parse(JSON.stringify(child));
      newChild.id = newChildId;
      newChild.parentNode = newNode.id;
      newChild.selected = false;
      // 清除执行状态
      if (newChild.data) {
        delete newChild.data.executionStatus;
        delete newChild.data.executionResult;
      }
      newChildNodes.push(newChild);
    }

    // 获取循环节点内部的连线（source和target都在子节点中）
    const childNodeIds = new Set(childNodes.map((n: any) => n.id));
    const internalEdges = edges.value.filter(
      (e: any) => childNodeIds.has(e.source) && childNodeIds.has(e.target),
    );

    // 复制内部连线
    const newEdges: any[] = [];
    for (const edge of internalEdges) {
      const newEdge = JSON.parse(JSON.stringify(edge));
      newEdge.id = `e-${idMap[edge.source]}-${idMap[edge.target]}`;
      newEdge.source = idMap[edge.source];
      newEdge.target = idMap[edge.target];
      newEdges.push(newEdge);
    }

    // 清除循环节点的执行状态
    if (newNode.data) {
      delete newNode.data.executionStatus;
      delete newNode.data.executionResult;
    }

    // 添加所有节点和连线
    addNodes([newNode, ...newChildNodes]);
    if (newEdges.length > 0) {
      addEdges(newEdges);
    }
  } else {
    // 非循环节点，直接复制
    // 清除执行状态
    if (newNode.data) {
      delete newNode.data.executionStatus;
      delete newNode.data.executionResult;
    }
    addNodes([newNode]);
  }
};

const handleDelete = () => {
  if (!contextMenu.value.node) return;
  removeNodes([contextMenu.value.node.id]);
};

const handleRename = async () => {
  if (!contextMenu.value.node) return;
  try {
    const { value } = await ElMessageBox.prompt(
      $t('ai-platform.workflow.editor.renamePrompt'),
      $t('ai-platform.workflow.editor.rename'),
      {
        confirmButtonText: $t('ai-platform.workflow.editor.confirmBtn'),
        cancelButtonText: $t('ai-platform.workflow.editor.cancelBtn'),
        inputValue:
          contextMenu.value.node.data.label || contextMenu.value.node.label,
      },
    );
    contextMenu.value.node.data.label = value;
    // 某些节点可能直接使用 label 属性
    contextMenu.value.node.label = value;
  } catch {
    // cancel
  }
};

// 节点上的快速添加按钮点击处理
const handleNodeAddClick = (
  sourceNodeId: string,
  position: { x: number; y: number },
) => {
  // 计算位置，防止超出视口
  const popupWidth = 256; // w-64 = 256px
  const popupHeight = 450; // h-80 = 320px
  const padding = 20;

  let x = position.x;
  let y = position.y;

  // 检查右侧边界
  if (x + popupWidth + padding > window.innerWidth) {
    x = window.innerWidth - popupWidth - padding;
  }

  // 检查底部边界
  if (y + popupHeight + padding > window.innerHeight) {
    y = window.innerHeight - popupHeight - padding;
  }

  quickAddPopup.value = {
    show: true,
    sourceNodeId,
    position: { x, y },
  };
};

// 提供给子组件使用
provide('onNodeAddClick', handleNodeAddClick);

// 快速添加节点并连接
const handleQuickAddNode = (nodeType: string) => {
  const sourceNode = nodes.value.find(
    (n) => n.id === quickAddPopup.value.sourceNodeId,
  );
  if (!sourceNode) return;

  const label = getNodeLabel(nodeType);
  const newNodeId = `${nodeType}-${Date.now()}`;

  // 检查源节点是否是循环体的子节点
  const parentNodeId = sourceNode.parentNode;

  // 在源节点右侧创建新节点
  const newNode: any = {
    id: newNodeId,
    type: nodeType,
    position: {
      x: sourceNode.position.x + 350,
      y: sourceNode.position.y,
    },
    label,
    data: { label },
  };

  // 如果源节点是循环体的子节点，新节点也应该成为循环体的子节点
  if (parentNodeId) {
    newNode.parentNode = parentNodeId;
    newNode.extent = 'parent';
    newNode.expandParent = true;
    newNode.zIndex = 10;
  }

  addNodes([newNode]);

  // 如果是循环体子节点，自动扩展父节点
  if (parentNodeId) {
    resizeLoopNodeToFitChildren(parentNodeId);
  }

  // 自动连接
  addEdges([
    {
      id: `e-${quickAddPopup.value.sourceNodeId}-${newNodeId}`,
      source: quickAddPopup.value.sourceNodeId,
      target: newNodeId,
      type: 'addButton',
    },
  ]);

  // 关闭弹出框
  quickAddPopup.value.show = false;
};

// 边上添加节点的点击处理
const handleEdgeAddClick = (
  edgeId: string,
  source: string,
  target: string,
  position: { x: number; y: number },
) => {
  // 计算位置，防止超出视口
  const popupWidth = 256; // w-64 = 256px
  const popupHeight = 450; // h-80 = 320px
  const padding = 20;

  let x = position.x;
  let y = position.y;

  // 检查右侧边界
  if (x + popupWidth + padding > window.innerWidth) {
    x = window.innerWidth - popupWidth - padding;
  }

  // 检查底部边界
  if (y + popupHeight + padding > window.innerHeight) {
    y = window.innerHeight - popupHeight - padding;
  }

  edgeAddPopup.value = {
    show: true,
    edgeId,
    source,
    target,
    position: { x, y },
  };
};

// 提供给边组件使用
provide('onEdgeAddClick', handleEdgeAddClick);

// 在边中间插入节点
const handleEdgeInsertNode = (nodeType: string) => {
  const sourceNode = nodes.value.find(
    (n) => n.id === edgeAddPopup.value.source,
  );
  const targetNode = nodes.value.find(
    (n) => n.id === edgeAddPopup.value.target,
  );
  if (!sourceNode || !targetNode) return;

  const label = getNodeLabel(nodeType);
  const newNodeId = `${nodeType}-${Date.now()}`;

  // 检查源节点或目标节点是否是循环体的子节点
  const parentNodeId = sourceNode.parentNode || targetNode.parentNode;

  // 在两个节点中间创建新节点
  const newNode: any = {
    id: newNodeId,
    type: nodeType,
    position: {
      x: (sourceNode.position.x + targetNode.position.x) / 2,
      y: (sourceNode.position.y + targetNode.position.y) / 2,
    },
    label,
    data: { label },
  };

  // 如果源节点或目标节点是循环体的子节点，新节点也应该成为循环体的子节点
  if (parentNodeId) {
    newNode.parentNode = parentNodeId;
    newNode.extent = 'parent'; // 限制子节点在父节点范围内
    newNode.expandParent = true; // 允许扩展父节点
    newNode.zIndex = 10; // 子节点设置较高的 zIndex
  }

  // 删除原来的边
  removeEdges([edgeAddPopup.value.edgeId]);

  // 添加新节点
  addNodes([newNode]);

  // 如果是循环体子节点，自动扩展父节点
  if (parentNodeId) {
    resizeLoopNodeToFitChildren(parentNodeId);
  }

  // 添加两条新边：源节点 -> 新节点 -> 目标节点
  addEdges([
    {
      id: `e-${edgeAddPopup.value.source}-${newNodeId}`,
      source: edgeAddPopup.value.source,
      target: newNodeId,
      type: 'addButton',
    },
    {
      id: `e-${newNodeId}-${edgeAddPopup.value.target}`,
      source: newNodeId,
      target: edgeAddPopup.value.target,
      type: 'addButton',
    },
  ]);

  // 关闭弹出框
  edgeAddPopup.value.show = false;
};

// 拖拽逻辑
const onDragStart = (event: DragEvent, nodeType: string) => {
  if (event.dataTransfer) {
    event.dataTransfer.setData('application/vueflow', nodeType);
    event.dataTransfer.effectAllowed = 'move';
  }
};

const onDragOver = (event: DragEvent) => {
  event.preventDefault();
  if (event.dataTransfer) {
    event.dataTransfer.dropEffect = 'move';
  }
};

const onDrop = (event: DragEvent) => {
  const type = event.dataTransfer?.getData('application/vueflow');
  if (!type) return;

  // 使用 VueFlow 的 screenToFlowCoordinate 方法正确转换屏幕坐标到画布坐标
  const position = screenToFlowCoordinate({
    x: event.clientX,
    y: event.clientY,
  });

  // 节点类型对应的默认名称
  const label = getNodeLabel(type);

  // 检查是否拖拽到循环容器内
  let parentNodeId: string | undefined;
  let relativePosition = position;

  // 查找是否在某个循环节点的范围内
  for (const node of nodes.value) {
    if (node.type === 'loop') {
      // 循环节点的最小尺寸
      const minLoopWidth = 400;
      const minLoopHeight = 350;

      // 使用 data 中存储的尺寸（由 LoopNode 组件计算并存储），或使用最小尺寸
      // 注意：不使用 dimensions，因为它可能不准确
      const nodeWidth = node.data?.width || minLoopWidth;
      const nodeHeight = node.data?.height || minLoopHeight;
      const nodeX = node.position.x;
      const nodeY = node.position.y;

      // 检查拖拽位置是否严格在循环节点的循环体区域内
      // 循环体区域 = 整个节点 - 头部区域（约 120px）
      const loopHeaderHeight = 120;

      // 严格检查：必须在循环节点的边界内
      const isInsideX = position.x > nodeX && position.x < nodeX + nodeWidth;
      const isInsideY =
        position.y > nodeY + loopHeaderHeight &&
        position.y < nodeY + nodeHeight;
      const isInside = isInsideX && isInsideY;

      console.log('[Loop Drop Check]', {
        dropPosition: position,
        loopNode: {
          id: node.id,
          x: nodeX,
          y: nodeY,
          width: nodeWidth,
          height: nodeHeight,
        },
        bounds: {
          left: nodeX,
          right: nodeX + nodeWidth,
          top: nodeY + loopHeaderHeight,
          bottom: nodeY + nodeHeight,
        },
        isInsideX,
        isInsideY,
        isInside,
      });

      if (isInside) {
        parentNodeId = node.id;
        // 计算相对于父节点的位置（子节点位置是相对于父节点左上角的）
        // 确保子节点在循环体区域内（头部 + 配置信息区域约 120px）
        // 限制位置在合理范围内
        const relX = position.x - nodeX;
        const relY = position.y - nodeY;
        relativePosition = {
          x: Math.min(Math.max(20, relX), nodeWidth - 280), // 限制在容器宽度内
          y: Math.min(Math.max(loopHeaderHeight, relY), nodeHeight - 50), // 限制在容器高度内
        };
        console.log('[Loop Child Position]', { relativePosition, relX, relY });
        break;
      }
    }
  }

  const newNode: any = {
    id: `${type}-${Date.now()}`,
    type,
    position: relativePosition,
    label,
    data: { label },
  };

  // 循环节点设置较低的 zIndex，让子节点和连线显示在上方
  if (type === 'loop') {
    newNode.zIndex = -1;
  }

  // 如果有父节点，设置 parentNode 和 extent
  if (parentNodeId) {
    newNode.parentNode = parentNodeId;
    newNode.extent = 'parent'; // 限制子节点在父节点范围内
    newNode.expandParent = true; // 允许扩展父节点
    newNode.zIndex = 10; // 子节点设置较高的 zIndex
  }

  addNodes([newNode]);

  if (parentNodeId) {
    ElMessage.success($t('ai-platform.workflow.editor.addedToLoop'));
    // 更新父节点的尺寸以容纳子节点
    const parentNode = nodes.value.find((n) => n.id === parentNodeId);
    if (parentNode) {
      const childWidth = 280; // 子节点大约宽度
      const childHeight = 150; // 子节点大约高度
      const newWidth = Math.max(
        parentNode.data?.width || 400,
        relativePosition.x + childWidth + 20,
      );
      const newHeight = Math.max(
        parentNode.data?.height || 200,
        relativePosition.y + childHeight + 20,
      );

      // 更新父节点数据
      parentNode.data = {
        ...parentNode.data,
        width: newWidth,
        height: newHeight,
      };
    }
  }

  // Auto-insert node on edge drop
  const element = document.elementFromPoint(event.clientX, event.clientY);
  const edgeGroup = element?.closest('.vue-flow__edge');

  if (edgeGroup) {
    const edgeId = edgeGroup.dataset.id;
    const edge = edges.value.find((e) => e.id === edgeId);

    if (
      edge && // 只有当新节点有输入和输出 handle 时才自动连接
      // Start 只有输出，End 只有输入，不能插入中间
      type !== 'start' &&
      type !== 'end'
    ) {
      removeEdges([edge.id]);
      addEdges([
        {
          id: `e-${edge.source}-${newNode.id}`,
          source: edge.source,
          sourceHandle: edge.sourceHandle,
          target: newNode.id,
        },
        {
          id: `e-${newNode.id}-${edge.target}`,
          source: newNode.id,
          target: edge.target,
          targetHandle: edge.targetHandle,
        },
      ]);
      ElMessage.success($t('ai-platform.workflow.editor.autoInserted'));
    }
  }
};

// 初始加载
// 加载工作流定义到画布
const loadDefinitionToCanvas = async (definition: any) => {
  // 清空当前画布
  removeNodes(nodes.value.map((n) => n.id));
  removeEdges(edges.value.map((e) => e.id));

  // 等待 VueFlow 内部状态更新完成
  await nextTick();

  if (!definition) return;

  if (definition.nodes && definition.nodes.length > 0) {
    // 清除节点的执行状态，并确保 position 格式正确
    const cleanedNodes = definition.nodes.map((node: any) => ({
      ...node,
      position: {
        x: Number(node.position?.x) || 0,
        y: Number(node.position?.y) || 0,
      },
      data: {
        ...node.data,
        executionStatus: undefined,
        executionResult: undefined,
      },
    }));
    addNodes(cleanedNodes);
  }

  // 等待节点添加完成后再添加边
  await nextTick();

  if (definition.edges) {
    const edgesWithType = definition.edges.map((edge: any) => ({
      ...edge,
      type: 'addButton',
      // 清除边的执行状态，避免刷新后显示绿色
      data: {
        ...edge.data,
        status: undefined,
      },
    }));
    addEdges(edgesWithType);
  }

  // 如果没有节点，添加默认节点
  if (!definition.nodes || definition.nodes.length === 0) {
    addNodes([
      {
        id: 'start-1',
        type: 'start',
        label: getNodeLabel('start'),
        position: { x: 100, y: 200 },
        data: {},
      },
      {
        id: 'end-1',
        type: 'end',
        label: getNodeLabel('end'),
        position: { x: 700, y: 200 },
        data: {},
      },
    ]);
  }

  // 最后调用 fitView 确保视图正确
  await nextTick();
  fitView();
};

onMounted(async () => {
  if (!workflowId) return;
  loading.value = true;
  try {
    const res = await getWorkflowDetailApi(workflowId);
    workflow.value = res;
    // 始终加载草稿定义进行编辑
    loadDefinitionToCanvas(res.definition);
  } catch (error) {
    console.error(error);
  } finally {
    loading.value = false;
  }
});

// 节点验证
interface ValidationError {
  nodeId: string;
  nodeLabel: string;
  message: string;
  severity?: 'error' | 'warning';
}

const validateWorkflow = (): ValidationError[] => {
  const errors: ValidationError[] = [];
  const nodeList = nodes.value;
  const edgeList = edges.value;

  // 检查是否有开始节点
  const startNodes = nodeList.filter((n) => n.type === 'start');
  if (startNodes.length === 0) {
    errors.push({
      nodeId: '',
      nodeLabel: '',
      message: $t('ai-platform.workflow.editor.validation.missingStart'),
    });
  } else if (startNodes.length > 1) {
    errors.push({
      nodeId: '',
      nodeLabel: '',
      message: $t('ai-platform.workflow.editor.validation.multipleStart'),
    });
  }

  // 检查是否有结束节点
  const endNodes = nodeList.filter((n) => n.type === 'end');
  if (endNodes.length === 0) {
    errors.push({
      nodeId: '',
      nodeLabel: '',
      message: $t('ai-platform.workflow.editor.validation.missingEnd'),
    });
  }

  // 检查每个节点的连接
  nodeList.forEach((node) => {
    const nodeLabel = node.data?.label || node.id;

    // 开始节点必须有出边
    if (node.type === 'start') {
      const outEdges = edgeList.filter((e) => e.source === node.id);
      if (outEdges.length === 0) {
        errors.push({
          nodeId: node.id,
          nodeLabel,
          message: $t(
            'ai-platform.workflow.editor.validation.startNoConnection',
          ),
        });
      }
    }

    // 结束节点必须有入边
    if (node.type === 'end') {
      const inEdges = edgeList.filter((e) => e.target === node.id);
      if (inEdges.length === 0) {
        errors.push({
          nodeId: node.id,
          nodeLabel,
          message: $t('ai-platform.workflow.editor.validation.endNoConnection'),
        });
      }
    }

    // LLM 节点必须配置模型
    if (node.type === 'llm' && !node.data?.model_id) {
      errors.push({
        nodeId: node.id,
        nodeLabel,
        message: $t('ai-platform.workflow.editor.validation.llmNoModel'),
      });
    }

    // HTTP 节点必须配置 URL
    if (node.type === 'http' && !node.data?.url) {
      errors.push({
        nodeId: node.id,
        nodeLabel,
        message: $t('ai-platform.workflow.editor.validation.httpNoUrl'),
      });
    }

    // 并行节点必须有多个出边
    if (node.type === 'parallel') {
      const outEdges = edgeList.filter((e) => e.source === node.id);
      if (outEdges.length < 2) {
        errors.push({
          nodeId: node.id,
          nodeLabel,
          message: $t(
            'ai-platform.workflow.editor.validation.parallelMinBranches',
          ),
        });
      }
    }

    // 合并节点必须有多个入边
    if (node.type === 'merge') {
      const inEdges = edgeList.filter((e) => e.target === node.id);
      if (inEdges.length < 2) {
        errors.push({
          nodeId: node.id,
          nodeLabel,
          message: $t('ai-platform.workflow.editor.validation.mergeMinInputs'),
        });
      }
    }
  });

  return errors;
};

// 自动保存状态
const autoSaveStatus = ref<'saved' | 'saving' | 'unsaved'>('saved');
const autoSaveTimer = ref<null | ReturnType<typeof setTimeout>>(null);

const handleSave = async (showMessage = true) => {
  if (!workflow.value) return;

  autoSaveStatus.value = 'saving';

  const flow = toObject();
  try {
    await updateWorkflowApi(workflowId, {
      name: workflow.value.name,
      workflow_type: workflow.value.workflow_type,
      description: workflow.value.description,
      definition: flow,
    } as any);
    autoSaveStatus.value = 'saved';
    if (showMessage) {
      ElMessage.success($t('ai-platform.workflow.saveSuccess'));
    }
  } catch (error) {
    autoSaveStatus.value = 'unsaved';
    console.error(error);
    if (showMessage) {
      ElMessage.error($t('ai-platform.workflow.editor.saveFailed'));
    }
  }
};

// 自动保存逻辑：节点或边变化后 2 秒自动保存
const triggerAutoSave = () => {
  autoSaveStatus.value = 'unsaved';
  if (autoSaveTimer.value) {
    clearTimeout(autoSaveTimer.value);
  }
  autoSaveTimer.value = setTimeout(() => {
    handleSave(false);
  }, 2000);
};

// 发布工作流
const handlePublish = async () => {
  // 发布前先保存
  await handleSave(false);

  // 验证工作流
  const errors = validateWorkflow();
  const blockingErrors = errors.filter((e) => e.severity !== 'warning');
  if (blockingErrors.length > 0) {
    const errorMessages = blockingErrors.map((e) =>
      e.nodeLabel ? `${e.nodeLabel}: ${e.message}` : e.message,
    );
    ElMessage.warning({
      message: `${$t('ai-platform.workflow.editor.workflowHasIssues')}\n${errorMessages.join('\n')}`,
      duration: 5000,
    });
    return;
  }

  try {
    // 弹出版本说明输入框
    const { value: description } = await ElMessageBox.prompt(
      $t('ai-platform.workflow.editor.publishPrompt'),
      $t('ai-platform.workflow.editor.publishTitle'),
      {
        confirmButtonText: $t('ai-platform.workflow.editor.publishBtn'),
        cancelButtonText: $t('ai-platform.workflow.editor.cancelBtn'),
        inputPlaceholder: $t('ai-platform.workflow.editor.publishPlaceholder'),
      },
    );

    // 调用发布 API
    const result = await publishWorkflowApi(workflowId, description || '');
    workflow.value = result;

    ElMessage.success(
      $t('ai-platform.workflow.editor.publishVersionSuccess', {
        version: result.published_version,
      }),
    );
  } catch (error: any) {
    if (error === 'cancel' || error?.message === 'cancel') {
      return; // 用户取消
    }
    console.error(error);
    ElMessage.error($t('ai-platform.workflow.editor.publishFailed'));
  }
};

const handleRun = async () => {
  // 运行前验证
  const errors = validateWorkflow();
  const blockingErrors = errors.filter((e) => e.severity !== 'warning');
  if (blockingErrors.length > 0) {
    ElMessage.error($t('ai-platform.workflow.editor.workflowIncomplete'));
    return;
  }

  const warnings = errors.filter((e) => e.severity === 'warning');
  if (warnings.length > 0) {
    ElMessage.warning({
      message: warnings
        .map((e) => (e.nodeLabel ? `${e.nodeLabel}: ${e.message}` : e.message))
        .join('\n'),
      duration: 4000,
    });
  }

  await handleSave();

  if (
    workflow.value?.workflow_type === 'data_process' ||
    workflow.value?.workflow_type === 'automation'
  ) {
    const startNode = nodes.value.find((n) => n.type === 'start');
    const variables = startNode?.data?.variables || [];
    if (variables.length > 0) {
      showRunDialog.value = true;
    } else {
      startWorkflowExecution({});
    }
  } else {
    showRunPanel.value = true;
  }
};

// 处理版本回滚
const handleVersionRollback = (rollbackedWorkflow: any) => {
  workflow.value = rollbackedWorkflow;
  // 使用统一的加载函数，确保边类型正确设置
  if (rollbackedWorkflow.definition) {
    loadDefinitionToCanvas(rollbackedWorkflow.definition);
  }
};

// 直接运行工作流（数据处理/自动化）
const startWorkflowExecution = (inputs: Record<string, any>) => {
  cancelWorkflowStream?.();
  clearExecutionStatus();

  cancelWorkflowStream = runWorkflowStreamApi(
    workflowId,
    inputs,
    (event: any) => {
      switch (event.type) {
        case 'node_start': {
          handleNodeStart(event);
          break;
        }
        case 'node_complete': {
          handleNodeComplete(event);
          break;
        }
        case 'llm_chunk': {
          handleNodeStreaming(event);
          break;
        }
        case 'node_event': {
          if (event.event?.type === 'message') {
            handleNodeStreaming(event);
          }
          break;
        }
        case 'complete':
        case 'error': {
          cancelWorkflowStream = null;
          break;
        }
      }
    },
    () => {
      cancelWorkflowStream = null;
    },
    () => {
      cancelWorkflowStream = null;
    },
    true,
  );
};

// 清除所有节点的执行状态
const clearExecutionStatus = () => {
  nodes.value.forEach((node) => {
    if (node.data) {
      node.data = {
        ...node.data,
        executionStatus: undefined,
        executionResult: undefined,
      };
    }
  });
  // 清除所有边的执行状态
  edges.value.forEach((edge) => {
    if (edge.data) {
      edge.data = { ...edge.data, status: undefined };
    }
  });
};

// 更新边的执行状态
const updateEdgeStatus = (
  sourceNodeId: string,
  status: 'completed' | 'failed' | 'running',
) => {
  // 找到从该节点出发的所有边
  edges.value.forEach((edge) => {
    if (edge.source === sourceNodeId) {
      edge.data = { ...edge.data, status };
    }
  });
};

// 设置进入节点的边的状态
const setIncomingEdgeStatus = (
  targetNodeId: string,
  status: 'completed' | 'failed' | 'running',
) => {
  edges.value.forEach((edge) => {
    if (edge.target === targetNodeId) {
      edge.data = { ...edge.data, status };
    }
  });
};

// 节点开始执行 - 显示 running 状态
const handleNodeStart = (event: {
  inputs?: any;
  loop_iteration?: number;
  node_id: string;
  node_label?: string;
  node_type: string;
}) => {
  const node = nodes.value.find((n) => n.id === event.node_id);
  if (node) {
    // 如果是循环内的子节点，保留之前的迭代日志
    const existingIterations = node.data?.executionResult?.iterations || [];

    node.data = {
      ...node.data,
      executionStatus: {
        status: 'running',
        loop_iteration: event.loop_iteration,
      },
      executionResult: {
        status: 'running',
        inputs: event.inputs,
        loop_iteration: event.loop_iteration,
        iterations: existingIterations,
      },
    };
    // 设置进入该节点的边为运行状态（数据正在传输）
    setIncomingEdgeStatus(event.node_id, 'running');
  }
};

// 节点流式输出 - 更新 streaming_content
const handleNodeStreaming = (event: {
  accumulated_content?: string;
  content?: string;
  node_id: string;
}) => {
  const node = nodes.value.find((n) => n.id === event.node_id);
  if (node && node.data?.executionResult) {
    node.data = {
      ...node.data,
      executionResult: {
        ...node.data.executionResult,
        streaming_content: event.accumulated_content || event.content,
      },
    };
  }
};

// 循环迭代事件 - 更新循环节点的迭代进度
const handleLoopIteration = (event: {
  error?: string;
  item?: any;
  iteration: number;
  node_id: string;
  output?: any;
  total?: number;
  type: 'complete' | 'error' | 'start';
}) => {
  const node = nodes.value.find((n) => n.id === event.node_id);
  if (node) {
    // 只在迭代开始时更新状态，避免覆盖循环完成后的状态
    if (event.type === 'start') {
      node.data = {
        ...node.data,
        executionStatus: {
          status: 'running',
          iteration: event.iteration,
          total: event.total,
        },
        executionResult: {
          ...node.data?.executionResult,
          status: 'running',
          iteration: event.iteration,
          total: event.total,
          current_item: event.item,
        },
      };
    } else {
      // 对于 complete 和 error 类型，只更新迭代信息，不改变状态
      node.data = {
        ...node.data,
        executionResult: {
          ...node.data?.executionResult,
          iteration: event.iteration,
          total: event.total,
          current_item: event.item,
        },
      };
    }
  }
};

// 工作流执行完成
const handleRunComplete = (result: any) => {
  // 结果已通过 handleNodeComplete 更新到各个节点
  // 这里可以添加额外的完成逻辑（如有需要）
};

// 节点执行完成 - 显示 success/failed 状态
const handleNodeComplete = (event: {
  elapsed_time: number;
  error_message?: string;
  inputs?: any;
  loop_iteration?: number;
  node_id: string;
  node_type: string;
  outputs?: any;
  status: string;
  tokens_used: number;
  warnings?: string[];
}) => {
  if (event.warnings?.includes('default_connection_write')) {
    ElMessage.warning({
      message: $t(
        'ai-platform.workflow.editor.dbConnection.defaultWriteWarning',
      ),
      duration: 4000,
    });
  }

  const node = nodes.value.find((n) => n.id === event.node_id);
  if (node) {
    const isSuccess = event.status === 'success';
    const isLoopChild = event.loop_iteration !== undefined;

    // 获取 inputs：优先使用事件中的 inputs，否则使用之前保存的
    const currentInputs = event.inputs || node.data?.executionResult?.inputs;

    // 当前迭代的结果
    const currentIterationResult = {
      iteration: event.loop_iteration,
      status: isSuccess ? 'success' : 'failed',
      elapsed_time: event.elapsed_time,
      tokens_used: event.tokens_used,
      error_message: event.error_message,
      output: event.outputs,
      inputs: currentInputs,
    };

    // 如果是循环内的子节点，保存到迭代日志数组
    let iterations = node.data?.executionResult?.iterations || [];
    if (isLoopChild) {
      // 检查是否已经存在相同迭代的记录（避免等待恢复时重复添加）
      const existingIndex = iterations.findIndex(
        (iter: any) => iter.iteration === event.loop_iteration,
      );
      if (existingIndex === -1) {
        // 添加新记录
        iterations = [...iterations, currentIterationResult];
      } else {
        // 更新现有记录
        iterations = [
          ...iterations.slice(0, existingIndex),
          currentIterationResult,
          ...iterations.slice(existingIndex + 1),
        ];
      }
    }

    // 提取子流程日志（如果是子流程节点）
    const subflowLogs =
      event.outputs?.subflow_result?.logs ||
      event.outputs?.output_variables?.subflow_result?.logs;

    node.data = {
      ...node.data,
      executionStatus: {
        status: isSuccess ? 'success' : 'failed',
        elapsed_time: event.elapsed_time,
        tokens_used: event.tokens_used,
        error: event.error_message,
        loop_iteration: event.loop_iteration,
      },
      executionResult: {
        status: isSuccess ? 'success' : 'failed',
        elapsed_time: event.elapsed_time,
        tokens_used: event.tokens_used,
        error_message: event.error_message,
        output: event.outputs,
        inputs: currentInputs,
        loop_iteration: event.loop_iteration,
        iterations: isLoopChild ? iterations : undefined,
        // 子流程日志
        subflow_logs: subflowLogs,
      },
    };
    // 节点执行完成，设置进入该节点的边为完成/失败状态
    setIncomingEdgeStatus(event.node_id, isSuccess ? 'completed' : 'failed');
  }
};

const handleBack = () => {
  router.push(appContextStore.getContextPath('/ai-platform/workflow'));
};

// 键盘快捷键
const handleKeydown = (e: KeyboardEvent) => {
  // Ctrl/Cmd + Z = 撤销
  if ((e.ctrlKey || e.metaKey) && e.key === 'z' && !e.shiftKey) {
    e.preventDefault();
    handleUndo();
  }
  // Ctrl/Cmd + Shift + Z 或 Ctrl/Cmd + Y = 重做
  if (
    (e.ctrlKey || e.metaKey) &&
    (e.key === 'y' || (e.key === 'z' && e.shiftKey))
  ) {
    e.preventDefault();
    handleRedo();
  }
};

// 缩放控制
const handleZoomIn = () => {
  zoomIn();
};

const handleZoomOut = () => {
  zoomOut();
};

const handleFitView = () => {
  fitView({ padding: 0.2 });
};

onMounted(() => {
  window.addEventListener('keydown', handleKeydown);
  window.addEventListener('click', handleClickOutside);
  // 初始化工作流编辑器 store
  workflowEditorStore.initWorkflow(workflowId);
});

onUnmounted(() => {
  window.removeEventListener('keydown', handleKeydown);
  window.removeEventListener('click', handleClickOutside);
  // 清理 store
  workflowEditorStore.cleanup();
});
</script>

<template>
  <div class="flex h-full w-full flex-col">
    <!-- Header -->
    <header
      class="bg-background-deep z-10 m-3 flex h-14 items-center justify-between rounded-[8px] px-4 shadow-sm"
    >
      <div class="flex items-center gap-4">
        <ElButton link :icon="ArrowLeft" @click="handleBack" />
        <div v-if="workflow" class="flex flex-col">
          <span class="text-foreground text-sm font-bold">{{
            workflow.name
          }}</span>
          <div class="flex items-center gap-2 text-xs">
            <span
              v-if="workflow.published_version"
              class="text-muted-foreground"
            >
              {{ $t('ai-platform.workflow.published') }} v{{
                workflow.published_version
              }}
            </span>
            <span v-else class="text-muted-foreground">{{
              $t('ai-platform.workflow.unpublished')
            }}</span>
            <span class="text-border">|</span>
            <span class="rounded bg-blue-100 px-1.5 py-0.5 text-blue-600">
              {{ $t('ai-platform.workflow.editing') }} v{{ workflow.version }}
            </span>
          </div>
        </div>
      </div>
      <div class="flex items-center gap-3">
        <span class="text-muted-foreground text-xs">
          <template v-if="autoSaveStatus === 'saving'">{{
            $t('ai-platform.workflow.editor.toolbar.saving')
          }}</template>
          <template v-else-if="autoSaveStatus === 'saved'">{{
            $t('ai-platform.workflow.editor.toolbar.saved')
          }}</template>
          <template v-else>{{
            $t('ai-platform.workflow.editor.toolbar.unsaved')
          }}</template>
        </span>
        <ElButton type="primary" plain :icon="Upload" @click="handlePublish">
          {{ $t('ai-platform.workflow.editor.toolbar.publish') }}
        </ElButton>
        <ElButton :icon="Clock" @click="showVersionHistory = true">
          {{ $t('ai-platform.workflow.editor.toolbar.versions') }}
        </ElButton>
        <ElButton :icon="Play" @click="handleRun">
          {{ $t('ai-platform.workflow.editor.toolbar.run') }}
        </ElButton>
      </div>
    </header>

    <!-- Main Editor -->
    <div
      class="bg-background-deep relative mx-3 mb-3 flex flex-1 overflow-hidden rounded-[8px]"
      v-loading="loading"
    >
      <!-- Canvas -->
      <main
        class="relative h-full w-full flex-1"
        @drop="onDrop"
        @dragover="onDragOver"
      >
        <VueFlow
          :node-types="nodeTypes"
          :edge-types="edgeTypes"
          :default-viewport="{ zoom: 1 }"
          :min-zoom="0.2"
          :max-zoom="4"
          :default-edge-options="defaultEdgeOptions"
          :snap-to-grid="true"
          :snap-grid="[20, 20]"
          :connection-line-style="{ stroke: '#94a3b8', strokeWidth: 2 }"
          :edges-updatable="true"
          :edges-focusable="true"
          :elevate-edges-on-select="true"
          fit-view-on-init
          @edge-click="onEdgeClick"
          @edge-update="handleEdgeUpdate"
        >
          <MiniMap
            v-if="showMiniMap"
            class="!border-border !absolute !bottom-16 !right-4 !rounded-lg !border !shadow-lg"
            :node-stroke-width="3"
            pannable
            zoomable
          />
        </VueFlow>

        <!-- 底部工具栏 -->
        <div
          class="border-border bg-card absolute bottom-4 left-1/2 flex -translate-x-1/2 items-center gap-1 rounded-lg border px-2 py-1.5 shadow-lg"
        >
          <!-- 撤销/重做 -->
          <button
            class="hover:bg-accent flex size-8 items-center justify-center rounded disabled:cursor-not-allowed disabled:opacity-40"
            :disabled="!canUndo"
            :title="`${$t('ai-platform.workflow.editor.toolbar.undo')} (Ctrl+Z)`"
            @click="handleUndo"
          >
            <Undo2 class="text-muted-foreground size-4" />
          </button>
          <button
            class="hover:bg-accent flex size-8 items-center justify-center rounded disabled:cursor-not-allowed disabled:opacity-40"
            :disabled="!canRedo"
            :title="`${$t('ai-platform.workflow.editor.toolbar.redo')} (Ctrl+Y)`"
            @click="handleRedo"
          >
            <Redo2 class="text-muted-foreground size-4" />
          </button>

          <div class="bg-border mx-1 h-5 w-px"></div>

          <!-- 缩放控制 -->
          <button
            class="hover:bg-accent flex size-8 items-center justify-center rounded"
            :title="$t('ai-platform.workflow.editor.toolbar.zoomOut')"
            @click="handleZoomOut"
          >
            <Minus class="text-muted-foreground size-4" />
          </button>
          <span
            class="text-muted-foreground w-12 text-center text-xs font-medium"
            >{{ currentZoom }}%</span
          >
          <button
            class="hover:bg-accent flex size-8 items-center justify-center rounded"
            :title="$t('ai-platform.workflow.editor.toolbar.zoomIn')"
            @click="handleZoomIn"
          >
            <Plus class="text-muted-foreground size-4" />
          </button>

          <div class="bg-border mx-1 h-5 w-px"></div>

          <!-- 适应屏幕 -->
          <button
            class="hover:bg-accent flex size-8 items-center justify-center rounded"
            :title="$t('ai-platform.workflow.editor.toolbar.fitView')"
            @click="handleFitView"
          >
            <Fullscreen class="text-muted-foreground size-4" />
          </button>

          <div class="bg-border mx-1 h-5 w-px"></div>

          <!-- 小地图开关 -->
          <button
            class="hover:bg-accent flex size-8 items-center justify-center rounded"
            :class="{ 'bg-primary/10': showMiniMap }"
            :title="$t('ai-platform.workflow.editor.toolbar.minimap')"
            @click="showMiniMap = !showMiniMap"
          >
            <Map
              class="size-4"
              :class="showMiniMap ? 'text-primary' : 'text-muted-foreground'"
            />
          </button>

          <div class="bg-border mx-1 h-5 w-px"></div>

          <!-- 添加组件按钮 -->
          <div
            ref="nodePaletteRef"
            class="relative"
            @mouseenter="showNodePalette = true"
          >
            <button
              class="hover:bg-accent flex h-8 items-center gap-1 rounded px-2"
              :class="{ 'bg-primary/10': showNodePalette }"
              :title="$t('ai-platform.workflow.editor.toolbar.addNode')"
            >
              <Plus
                class="size-4"
                :class="
                  showNodePalette ? 'text-primary' : 'text-muted-foreground'
                "
              />
              <span
                class="text-xs"
                :class="
                  showNodePalette ? 'text-primary' : 'text-muted-foreground'
                "
                >{{ $t('ai-platform.workflow.editor.toolbar.addNode') }}</span
              >
            </button>

            <!-- 节点选择弹出面板 -->
            <div
              v-show="showNodePalette"
              class="border-border bg-popover absolute bottom-full right-0 mb-2 flex h-[650px] w-72 flex-col rounded-lg border shadow-xl"
            >
              <div class="border-border shrink-0 p-3 pb-2">
                <div class="text-muted-foreground mb-2 text-xs font-semibold">
                  {{ $t('ai-platform.workflow.editor.toolbar.addNode') }}
                </div>
                <ElInput
                  v-model="nodeSearchQuery"
                  size="small"
                  :placeholder="
                    $t('ai-platform.workflow.editor.toolbar.searchNode')
                  "
                  clearable
                />
              </div>

              <ElScrollbar class="flex-1 px-3 py-2">
                <div
                  v-for="category in filteredNodeCategories"
                  :key="category.name"
                  class="mb-3 last:mb-0"
                >
                  <div
                    class="text-muted-foreground mb-1.5 px-1 text-[10px] font-medium"
                  >
                    {{ category.name }}
                  </div>
                  <div class="grid grid-cols-3 gap-1.5">
                    <div
                      v-for="node in category.nodes"
                      :key="node.type"
                      class="hover:border-primary hover:bg-primary/5 border-border flex cursor-grab select-none flex-col items-center gap-1 rounded-lg border p-2 transition-all"
                      :draggable="true"
                      @dragstart="(event) => onDragStart(event, node.type)"
                    >
                      <div
                        class="flex size-7 items-center justify-center rounded"
                        :class="{
                          'bg-purple-50 text-purple-600':
                            node.color === 'purple',
                          'bg-cyan-50 text-cyan-600': node.color === 'cyan',
                          'bg-blue-50 text-blue-600':
                            node.color !== 'purple' && node.color !== 'cyan',
                        }"
                      >
                        <component
                          :is="iconComponents[node.icon]"
                          class="size-3.5"
                        />
                      </div>
                      <span
                        class="text-muted-foreground text-center text-[10px] leading-tight"
                        >{{ node.label }}</span
                      >
                    </div>
                  </div>
                </div>
              </ElScrollbar>
            </div>
          </div>
        </div>
      </main>

      <!-- Properties Panel -->
      <div class="my-3 rounded-[8px] shadow-xl">
        <Panel
          v-if="selectedNode"
          v-model:node="selectedNode"
          @close="selectedNode = null"
        />
      </div>

      <!-- Chat Panel -->
      <div class="m-3 my-3 shadow-xl">
        <AiChatPanel
          v-if="showRunPanel"
          mode="workflow"
          :workflow-id="workflowId"
          :nodes="nodes"
          @close="showRunPanel = false"
          @clear-status="clearExecutionStatus"
          @node-start="handleNodeStart"
          @node-complete="handleNodeComplete"
          @node-streaming="handleNodeStreaming"
          @loop-iteration="handleLoopIteration"
        />
      </div>

      <!-- Run Dialog (for data_process/automation workflows) -->
      <RunDialog
        v-model:visible="showRunDialog"
        :nodes="nodes"
        :workflow-name="workflow?.name"
        @submit="startWorkflowExecution"
      />

      <!-- Context Menu -->
      <ContextMenu
        v-if="contextMenu.show"
        :x="contextMenu.x"
        :y="contextMenu.y"
        @close="contextMenu.show = false"
        @duplicate="handleDuplicate"
        @delete="handleDelete"
        @rename="handleRename"
      />

      <!-- 节点快速添加弹出框 -->
      <div
        v-if="quickAddPopup.show"
        ref="quickAddRef"
        class="border-border bg-popover fixed z-50 flex h-[450px] w-64 flex-col rounded-lg border shadow-xl"
        :style="{
          left: `${quickAddPopup.position.x}px`,
          top: `${quickAddPopup.position.y}px`,
        }"
      >
        <div class="border-border shrink-0 p-3 pb-2">
          <div class="text-muted-foreground mb-2 text-xs font-semibold">
            {{ $t('ai-platform.workflow.editor.toolbar.addNode') }}
          </div>
          <ElInput
            v-model="nodeSearchQuery"
            size="small"
            :placeholder="$t('ai-platform.workflow.editor.toolbar.searchNode')"
            clearable
          />
        </div>

        <ElScrollbar class="flex-1 px-3 py-2">
          <div
            v-for="category in filteredNodeCategories"
            :key="category.name"
            class="mb-3 last:mb-0"
          >
            <div
              class="text-muted-foreground mb-1.5 px-1 text-[10px] font-medium"
            >
              {{ category.name }}
            </div>
            <div class="grid grid-cols-3 gap-1.5">
              <button
                v-for="node in category.nodes"
                :key="node.type"
                class="hover:border-primary hover:bg-primary/5 border-border flex flex-col items-center gap-1 rounded-lg border p-2 transition-all"
                @click="handleQuickAddNode(node.type)"
              >
                <div
                  class="flex size-7 items-center justify-center rounded"
                  :class="{
                    'bg-purple-100 text-purple-600 dark:bg-purple-900 dark:text-purple-400':
                      node.color === 'purple',
                    'bg-cyan-100 text-cyan-600 dark:bg-cyan-900 dark:text-cyan-400':
                      node.color === 'cyan',
                    'bg-blue-100 text-blue-600 dark:bg-blue-900 dark:text-blue-400':
                      node.color !== 'purple' && node.color !== 'cyan',
                  }"
                >
                  <component :is="iconComponents[node.icon]" class="size-3.5" />
                </div>
                <span
                  class="text-muted-foreground text-center text-[10px] leading-tight"
                  >{{ node.label }}</span
                >
              </button>
            </div>
          </div>
        </ElScrollbar>
      </div>

      <!-- 边上插入节点弹出框 -->
      <div
        v-if="edgeAddPopup.show"
        ref="edgeAddRef"
        class="border-border bg-popover fixed z-50 flex h-[450px] w-64 flex-col rounded-lg border shadow-xl"
        :style="{
          left: `${edgeAddPopup.position.x}px`,
          top: `${edgeAddPopup.position.y}px`,
        }"
      >
        <div class="border-border shrink-0 p-3 pb-2">
          <div class="text-muted-foreground mb-2 text-xs font-semibold">
            {{ $t('ai-platform.workflow.editor.toolbar.addNode') }}
          </div>
          <ElInput
            v-model="nodeSearchQuery"
            size="small"
            :placeholder="$t('ai-platform.workflow.editor.toolbar.searchNode')"
            clearable
          />
        </div>

        <ElScrollbar class="flex-1 px-3 py-2">
          <div
            v-for="category in filteredNodeCategories"
            :key="category.name"
            class="mb-3 last:mb-0"
          >
            <div
              class="text-muted-foreground mb-1.5 px-1 text-[10px] font-medium"
            >
              {{ category.name }}
            </div>
            <div class="grid grid-cols-3 gap-1.5">
              <button
                v-for="node in category.nodes"
                :key="node.type"
                class="hover:border-primary hover:bg-primary/5 border-border flex flex-col items-center gap-1 rounded-lg border p-2 transition-all"
                @click="handleEdgeInsertNode(node.type)"
              >
                <div
                  class="flex size-7 items-center justify-center rounded"
                  :class="{
                    'bg-purple-100 text-purple-600 dark:bg-purple-900 dark:text-purple-400':
                      node.color === 'purple',
                    'bg-cyan-100 text-cyan-600 dark:bg-cyan-900 dark:text-cyan-400':
                      node.color === 'cyan',
                    'bg-blue-100 text-blue-600 dark:bg-blue-900 dark:text-blue-400':
                      node.color !== 'purple' && node.color !== 'cyan',
                  }"
                >
                  <component :is="iconComponents[node.icon]" class="size-3.5" />
                </div>
                <span
                  class="text-muted-foreground text-center text-[10px] leading-tight"
                  >{{ node.label }}</span
                >
              </button>
            </div>
          </div>
        </ElScrollbar>
      </div>
    </div>

    <!-- 版本历史面板 -->
    <VersionHistoryPanel
      v-model:visible="showVersionHistory"
      :workflow-id="workflowId"
      @rollback="handleVersionRollback"
    />
  </div>
</template>

<style scoped>
.vue-flow__container {
  height: 100%;
  width: 100%;
}

/* 点阵背景 - 支持浅色/深色模式 */
:deep(.vue-flow__pane) {
  --dot-color: rgb(0 0 0 / 15%);
  --dot-size: 1px;
  --dot-space: 24px;

  /* background-color: var(--el-color-info-light-9); */
  background-image: radial-gradient(
    circle,
    var(--dot-color) var(--dot-size),
    transparent var(--dot-size)
  );
  background-size: var(--dot-space) var(--dot-space);
}

/* 深色模式 */
.dark :deep(.vue-flow__pane) {
  --dot-color: rgb(255 255 255 / 15%);
}

/* MiniMap light/dark mode support */
.vue-flow__minimap {
  background-color: hsl(var(--background));
}

.dark .vue-flow__minimap {
  background-color: hsl(var(--card));
}

/* .vue-flow__minimap-mask {
  fill: hsl(var(--muted));
  opacity: 0.3;
} */

.dark .vue-flow__minimap-mask {
  fill: hsl(var(--muted));
  opacity: 0.4;
}

/* .vue-flow__minimap-node {
  fill: hsl(var(--primary));
  stroke: hsl(var(--primary));
  opacity: 0.8;
} */

.dark .vue-flow__minimap-node {
  fill: hsl(var(--second-background));
  stroke: hsl(var(--second-background));
  opacity: 0.7;
}
</style>
