/**
 * FlowChart 组件 - 纵向流程图
 *
 * 功能说明：
 * 1. 主链节点纵向排列（A -> B -> C）
 * 2. 支持递归展开 sub_goals 和 msgGroup
 * 3. 弧线连接有子内容的节点，直线连接无子内容的节点
 * 4. 点击弧线可以折叠/展开子节点
 * 5. 线条粗细根据嵌套层级递减
 */

import { useCallback, useEffect, useMemo, useRef, useState, forwardRef, useImperativeHandle } from "react";
import type { ForwardRefRenderFunction } from "react";
import type { Goal } from "../../types/goal";
import type { Edge as EdgeType, Message } from "../../types/message";
import { ArrowMarkers } from "./components/ArrowMarkers";
import styles from "./styles/FlowChart.module.css";
import { ImagePreviewModal } from "../ImagePreview/ImagePreviewModal";
import { extractImagesFromMessage } from "../../utils/imageExtraction";

/**
 * FlowChart 组件的 Props
 */
interface FlowChartProps {
  goals: Goal[]; // 目标节点列表
  msgGroups?: Record<string, Message[]>; // 消息组，key 是 goal_id
  invalidBranches?: Message[][]; // 失效分支列表
  onNodeClick?: (node: Goal | Message, edge?: EdgeType) => void; // 节点点击回调
  onSubTraceClick?: (parentGoal: Goal, entry: SubTraceEntry) => void; // 子追踪点击回调
}

/**
 * FlowChart 组件对外暴露的引用接口
 */
export interface FlowChartRef {
  expandAll: () => void;
  collapseAll: () => void;
}

/**
 * 子追踪条目类型
 */
export type SubTraceEntry = { id: string; mission?: string };

/**
 * 布局节点类型
 * 用于存储节点的位置、数据和层级信息
 */
interface LayoutNode {
  id: string; // 节点唯一标识
  x: number; // X 坐标
  y: number; // Y 坐标
  data: Goal | Message; // 节点数据
  type: "goal" | "subgoal" | "message"; // 节点类型
  level: number; // 嵌套层级（0 表示主链节点，1 表示子节点，2 表示孙节点...）
  parentId?: string; // 父节点 ID
  isInvalid?: boolean; // 是否为失效节点
}

/**
 * 连接线类型
 * 用于存储连接线的信息和状态
 */
interface LayoutEdge {
  id: string; // 连接线唯一标识
  source: LayoutNode; // 源节点
  target: LayoutNode; // 目标节点
  type: "arc" | "line"; // 连接线类型：弧线（有子内容）或直线（无子内容）
  level: number; // 嵌套层级，用于计算线条粗细
  collapsible: boolean; // 是否可折叠
  collapsed: boolean; // 是否已折叠
  children?: LayoutNode[]; // 折叠时隐藏的子节点列表
  isInvalid?: boolean; // 是否为失效连接线
}

const FlowChartComponent: ForwardRefRenderFunction<FlowChartRef, FlowChartProps> = (
  { goals, msgGroups = {}, invalidBranches, onNodeClick },
  ref,
) => {
  // 过滤掉有父节点的 goals，只保留主链节点
  goals = goals.filter((g) => !g.parent_id);

  // 确保 goals 中包含 END 节点，如果没有则自动添加
  const displayGoals = useMemo(() => {
    if (!goals) return [];
    const hasEnd = goals.some((g) => g.id === "END");
    if (hasEnd) return goals;
    const endGoal: Goal = {
      id: "END",
      description: "终止",
      status: "completed",
      created_at: new Date().toISOString(),
      reason: "",
    };
    return [...goals, endGoal];
  }, [goals]);

  // SVG 和容器的引用
  const svgRef = useRef<SVGSVGElement>(null);
  const containerRef = useRef<HTMLDivElement>(null);

  // 画布尺寸状态
  const [dimensions, setDimensions] = useState({ width: 1200, height: 800 });

  // 选中的节点 ID
  const [selectedNodeId, setSelectedNodeId] = useState<string | null>(null);

  // 折叠状态管理：使用 Set 存储已折叠的边的 ID
  const [collapsedEdges, setCollapsedEdges] = useState<Set<string>>(new Set());

  // 标记是否已经初始化过折叠状态
  const initializedRef = useRef(false);

  // 平移和缩放相关状态
  const [panOffset, setPanOffset] = useState({ x: 0, y: 0 }); // 平移偏移量
  const [isPanning, setIsPanning] = useState(false); // 是否正在平移
  const [zoom, setZoom] = useState(1); // 缩放比例
  const panStartRef = useRef({ x: 0, y: 0, originX: 0, originY: 0 }); // 平移起始位置
  const zoomRange = { min: 0.6, max: 2.4 }; // 缩放范围

  // 视图模式状态：scroll (滚动) 或 panzoom (拖拽缩放)
  const [viewMode, setViewMode] = useState<"scroll" | "panzoom">("scroll");

  // 图片预览状态
  const [previewImage, setPreviewImage] = useState<string | null>(null);

  // 限制缩放比例在允许范围内
  const clampZoom = (value: number) => Math.min(zoomRange.max, Math.max(zoomRange.min, value));

  // 重置视图到初始状态
  const resetView = () => {
    setZoom(1);
    setPanOffset({ x: 0, y: 0 });
  };

  // 监听容器尺寸变化，更新画布尺寸
  useEffect(() => {
    const element = containerRef.current;
    if (!element) return;
    const update = () => {
      const rect = element.getBoundingClientRect();
      const width = Math.max(800, rect.width || 0);
      const height = Math.max(600, rect.height || 0);
      setDimensions({ width, height });
    };
    update();
    if (typeof ResizeObserver === "undefined") {
      window.addEventListener("resize", update);
      return () => window.removeEventListener("resize", update);
    }
    const observer = new ResizeObserver(() => update());
    observer.observe(element);
    return () => observer.disconnect();
  }, []);

  // 处理平移操作
  useEffect(() => {
    if (!isPanning) return;
    const handleMove = (event: MouseEvent) => {
      const { x, y, originX, originY } = panStartRef.current;
      const nextX = originX + (event.clientX - x);
      const nextY = originY + (event.clientY - y);
      setPanOffset({ x: nextX, y: nextY });
    };
    const handleUp = () => {
      setIsPanning(false);
    };
    window.addEventListener("mousemove", handleMove);
    window.addEventListener("mouseup", handleUp);
    return () => {
      window.removeEventListener("mousemove", handleMove);
      window.removeEventListener("mouseup", handleUp);
    };
  }, [isPanning]);

  /**
   * 计算纵向布局
   *
   * 布局算法说明：
   * 1. 使用全局 Y 坐标确保所有节点纵向排列
   * 2. 递归展开每个主链节点的 sub_goals 和 msgGroup
   * 3. 子节点在父节点右侧水平偏移，形成层级结构
   * 4. 生成两种类型的连接线：
   *    - 弧线：连接有子内容的节点，可折叠/展开
   *    - 直线：连接无子内容的节点或子节点之间的连接
   */
  const layoutData = useMemo(() => {
    if (!displayGoals || displayGoals.length === 0) return { nodes: [], edges: [] };

    const nodes: LayoutNode[] = []; // 所有节点列表
    const edges: LayoutEdge[] = []; // 所有连接线列表

    const NODE_HEIGHT = 110; // 节点间距（纵向）
    const centerX = dimensions.width / 2; // 主链节点的 X 坐标（居中）
    const HORIZONTAL_OFFSET = 0; // 子节点水平偏移量

    let globalY = 100; // 全局 Y 坐标，确保所有节点纵向排列

    /**
     * 递归展开节点及其子节点
     *
     * @param goal - 当前目标节点
     * @param x - X 坐标
     * @param level - 嵌套层级（0 表示主链，1 表示子节点，2 表示孙节点...）
     * @param parentId - 父节点 ID
     * @returns 包含所有节点、第一个节点和最后一个节点的对象
     */
    const expandNode = (
      goal: Goal,
      x: number,
      level: number,
      parentId?: string,
    ): { nodes: LayoutNode[]; firstNode: LayoutNode; lastNode: LayoutNode } => {
      const nodeId = goal.id;

      // 创建当前节点
      const currentNode: LayoutNode = {
        id: nodeId,
        x,
        y: globalY,
        data: goal,
        type: "goal",
        level,
        parentId,
      };

      const localNodes: LayoutNode[] = [currentNode];
      let lastNode = currentNode; // 记录最后一个节点，用于连接到下一个主链节点
      globalY += NODE_HEIGHT; // 更新全局 Y 坐标

      // 1. 先展开 msgGroup（消息组）- 按照用户需求，消息显示在 sub_goals 之前
      const messages = msgGroups[nodeId];
      if (messages && messages.length > 0) {
        messages.forEach((msg) => {
          const msgNode: LayoutNode = {
            id: `${nodeId}-msg-${msg.message_id || Math.random()}`,
            x: x + HORIZONTAL_OFFSET, // 消息节点向右偏移
            y: globalY,
            data: msg,
            type: "message",
            level: level + 1,
            parentId: nodeId,
          };
          localNodes.push(msgNode);
          lastNode = msgNode; // 更新最后一个节点
          globalY += NODE_HEIGHT; // 更新全局 Y 坐标
        });
      }

      // 2. 再递归展开 sub_goals
      if (goal.sub_goals && goal.sub_goals.length > 0) {
        goal.sub_goals.forEach((subGoal) => {
          const subX = x + HORIZONTAL_OFFSET; // 子节点向右偏移
          const result = expandNode(subGoal, subX, level + 1, nodeId);
          localNodes.push(...result.nodes);
          lastNode = result.lastNode; // 更新最后一个节点
        });
      }

      return { nodes: localNodes, firstNode: currentNode, lastNode };
    };

    /**
     * 主链信息数组
     * 记录每个主链节点的第一个节点、最后一个节点和所有子节点
     */
    const mainChainInfo: Array<{
      goal: Goal;
      firstNode: LayoutNode;
      lastNode: LayoutNode;
      allNodes: LayoutNode[];
    }> = [];

    // 展开所有主链节点
    displayGoals.forEach((goal) => {
      const result = expandNode(goal, centerX, 0);
      nodes.push(...result.nodes);
      mainChainInfo.push({
        goal,
        firstNode: result.firstNode,
        lastNode: result.lastNode,
        allNodes: result.nodes,
      });
    });

    /**
     * 生成连接线
     *
     * 连接线生成规则：
     * 1. 主链节点之间：
     *    - 如果有子节点：绘制弧线（外层）+ 直线（内层子节点连接）
     *    - 如果无子节点：直接绘制直线
     * 2. 子节点之间：
     *    - 如果有孙节点：绘制弧线（外层）+ 直线（内层孙节点连接）
     *    - 如果无孙节点：直接绘制直线
     * 3. 递归处理所有层级的节点
     */

    for (let i = 0; i < mainChainInfo.length - 1; i++) {
      const current = mainChainInfo[i];
      const next = mainChainInfo[i + 1];

      const hasChildren = current.allNodes.length > 1; // 是否有子节点

      if (hasChildren) {
        // 情况 1：有子节点
        // 绘制弧线从第一个节点到下一个主链节点（外层包裹）
        const arcEdgeId = `arc-${current.firstNode.id}-${next.firstNode.id}`;
        edges.push({
          id: arcEdgeId,
          source: current.firstNode,
          target: next.firstNode,
          type: "arc",
          level: 0,
          collapsible: true, // 可折叠
          collapsed: collapsedEdges.has(arcEdgeId),
          children: current.allNodes.slice(1), // 除了第一个节点外的所有子节点
        });

        // 绘制直线连接子节点（内层）
        const childNodes = current.allNodes.slice(1);
        const directChildren = childNodes.filter((n) => n.parentId === current.firstNode.id);

        // Logic A: Messages Group Arc (A -> s1)
        // 检查是否有消息节点且后面跟着子目标节点
        // 如果有，绘制一条蓝色弧线从主节点到第一个子目标节点，包裹所有消息节点
        const firstSubgoalIndex = directChildren.findIndex((n) => n.type === "goal");
        if (firstSubgoalIndex > 0) {
          const target = directChildren[firstSubgoalIndex];
          const messages = directChildren.slice(0, firstSubgoalIndex);
          const arcId = `arc-msg-${current.firstNode.id}-${target.id}`;
          edges.push({
            id: arcId,
            source: current.firstNode,
            target: target,
            type: "arc",
            level: 1,
            collapsible: true,
            collapsed: collapsedEdges.has(arcId),
            children: messages,
          });
        }

        // 连接第一个节点到第一个子节点
        if (directChildren.length > 0) {
          edges.push({
            id: `line-${current.firstNode.id}-${directChildren[0].id}`,
            source: current.firstNode,
            target: directChildren[0],
            type: "line",
            level: 1,
            collapsible: false,
            collapsed: false,
          });
        }

        // 连接子节点之间
        for (let j = 0; j < directChildren.length - 1; j++) {
          const source = directChildren[j];
          const target = directChildren[j + 1];

          // 检查是否有孙节点
          const grandChildren = nodes.filter((n) => n.parentId === source.id);
          const hasGrandChildren = grandChildren.length > 0;

          if (hasGrandChildren) {
            // 情况 2：有孙节点
            // 绘制弧线从当前子节点到下一个子节点（第二层包裹）
            const arcId = `arc-${source.id}-${target.id}`;
            edges.push({
              id: arcId,
              source,
              target,
              type: "arc",
              level: source.level,
              collapsible: true, // 可折叠
              collapsed: collapsedEdges.has(arcId),
              children: grandChildren,
            });

            // 绘制孙节点之间的直线（内层）
            if (grandChildren.length > 0) {
              // 连接子节点到第一个孙节点
              edges.push({
                id: `line-${source.id}-${grandChildren[0].id}`,
                source,
                target: grandChildren[0],
                type: "line",
                level: source.level + 1,
                collapsible: false,
                collapsed: false,
              });

              // 连接孙节点之间
              for (let k = 0; k < grandChildren.length - 1; k++) {
                edges.push({
                  id: `line-${grandChildren[k].id}-${grandChildren[k + 1].id}`,
                  source: grandChildren[k],
                  target: grandChildren[k + 1],
                  type: "line",
                  level: source.level + 1,
                  collapsible: false,
                  collapsed: false,
                });
              }

              // 连接最后一个孙节点到下一个子节点
              edges.push({
                id: `line-${grandChildren[grandChildren.length - 1].id}-${target.id}`,
                source: grandChildren[grandChildren.length - 1],
                target,
                type: "line",
                level: source.level + 1,
                collapsible: false,
                collapsed: false,
              });
            }
          } else {
            // 情况 3：没有孙节点，直接绘制直线
            edges.push({
              id: `line-${source.id}-${target.id}`,
              source,
              target,
              type: "line",
              level: source.level,
              collapsible: false,
              collapsed: false,
            });
          }
        }

        // 连接最后一个子节点到下一个主链节点
        if (childNodes.length > 0) {
          edges.push({
            id: `line-${current.lastNode.id}-${next.firstNode.id}`,
            source: current.lastNode,
            target: next.firstNode,
            type: "line",
            level: 1,
            collapsible: false,
            collapsed: false,
          });

          // Logic C: Last Child Arc (s2 -> B)
          // 如果最后一个直接子节点是 sub_goal 且有子节点，绘制弧线连接到下一个主节点
          const lastChild = directChildren[directChildren.length - 1];
          const lastChildChildren = nodes.filter((n) => n.parentId === lastChild.id);

          if (lastChild.type === "goal" && lastChildChildren.length > 0) {
            const arcId = `arc-${lastChild.id}-${next.firstNode.id}`;
            edges.push({
              id: arcId,
              source: lastChild,
              target: next.firstNode,
              type: "arc",
              level: 1,
              collapsible: true,
              collapsed: collapsedEdges.has(arcId),
              children: lastChildChildren,
            });

            // 补充绘制最后一个子节点的内部连线（直线）
            // 1. 连接 lastChild 到第一个孙节点
            edges.push({
              id: `line-${lastChild.id}-${lastChildChildren[0].id}`,
              source: lastChild,
              target: lastChildChildren[0],
              type: "line",
              level: lastChild.level + 1,
              collapsible: false,
              collapsed: false,
            });

            // 2. 连接孙节点之间
            for (let k = 0; k < lastChildChildren.length - 1; k++) {
              edges.push({
                id: `line-${lastChildChildren[k].id}-${lastChildChildren[k + 1].id}`,
                source: lastChildChildren[k],
                target: lastChildChildren[k + 1],
                type: "line",
                level: lastChild.level + 1,
                collapsible: false,
                collapsed: false,
              });
            }
          }
        }
      } else {
        // 情况 4：没有子节点，直接绘制直线
        edges.push({
          id: `line-${current.firstNode.id}-${next.firstNode.id}`,
          source: current.firstNode,
          target: next.firstNode,
          type: "line",
          level: 0,
          collapsible: false,
          collapsed: false,
        });
      }
    }

    // 处理失效分支（invalidBranches）
    if (invalidBranches && invalidBranches.length > 0) {
      const validMsgMap = new Map<number, LayoutNode>();
      nodes.forEach((n) => {
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        const seq = (n.data as any).sequence;
        if (typeof seq === "number") {
          validMsgMap.set(seq, n);
        }
      });

      // Map to store invalid nodes by their anchor parent ID
      const invalidNodesByAnchor = new Map<string, LayoutNode[]>();

      invalidBranches.forEach((branch) => {
        if (branch.length === 0) return;
        const firstMsg = branch[0];
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        const pSeq = (firstMsg as any).parent_sequence;

        if (typeof pSeq === "number") {
          const parentNode = validMsgMap.get(pSeq);
          if (parentNode) {
            let currentParent = parentNode;
            const X_OFFSET = -200; // 向左偏移
            const currentBranchNodes: LayoutNode[] = [];

            branch.forEach((msg, idx) => {
              // Ensure we have a stable ID. If msg.id is missing, use a deterministic fallback.
              // Note: Using Math.random() is bad for React reconciliation, but here we need something unique if ID is missing.
              // Better fallback: `${parentNode.id}-branch-${idx}`
              const stableId = msg.id || `${parentNode.id}-branch-${idx}`;
              const nodeId = `invalid-${stableId}`;

              const node: LayoutNode = {
                id: nodeId,
                x: parentNode.x + X_OFFSET,
                y: parentNode.y + (idx + 1) * NODE_HEIGHT,
                data: msg,
                type: "message",
                level: parentNode.level,
                parentId: parentNode.id,
                isInvalid: true,
              };
              nodes.push(node);
              currentBranchNodes.push(node);

              edges.push({
                id: `edge-${currentParent.id}-${node.id}`,
                source: currentParent,
                target: node,
                type: "line",
                level: 0,
                collapsible: false,
                collapsed: false,
                isInvalid: true,
              });

              currentParent = node;
            });

            // Store in map
            if (!invalidNodesByAnchor.has(parentNode.id)) {
              invalidNodesByAnchor.set(parentNode.id, []);
            }
            invalidNodesByAnchor.get(parentNode.id)!.push(...currentBranchNodes);
          }
        }
      });

      // Associate invalid nodes with collapsible edges
      // If a parent node is hidden (part of a collapsed edge), its invalid children should also be hidden
      edges.forEach((edge) => {
        if (edge.collapsible && edge.children) {
          const extraChildren: LayoutNode[] = [];
          edge.children.forEach((child) => {
            if (invalidNodesByAnchor.has(child.id)) {
              extraChildren.push(...invalidNodesByAnchor.get(child.id)!);
            }
          });
          if (extraChildren.length > 0) {
            edge.children.push(...extraChildren);
          }
        }
      });
    }

    return { nodes, edges };
  }, [displayGoals, dimensions, msgGroups, collapsedEdges, invalidBranches]);

  // 暴露给父组件的方法
  useImperativeHandle(
    ref,
    () => ({
      expandAll: () => {
        setCollapsedEdges(new Set());
      },
      collapseAll: () => {
        const allCollapsible = new Set<string>();
        layoutData.edges.forEach((edge) => {
          if (edge.collapsible) {
            allCollapsible.add(edge.id);
          }
        });
        setCollapsedEdges(allCollapsible);
      },
    }),
    [layoutData],
  );

  // 初始化折叠状态：只展开第一个主链节点（A->B）之间的内容
  useEffect(() => {
    if (initializedRef.current || !layoutData || layoutData.edges.length === 0) return;

    // 找出所有可折叠的弧线（level === 0 表示主链节点之间的弧线）
    // const mainChainArcs = layoutData.edges.filter(
    //   (edge) => edge.type === "arc" && edge.collapsible && edge.level === 0,
    // );

    // if (mainChainArcs.length > 0) {
    //   // 除了第一个弧线外，其他都默认折叠
    //   const toCollapse = new Set<string>();
    //   mainChainArcs.slice(1).forEach((edge) => {
    //     toCollapse.add(edge.id);
    //   });

    //   setCollapsedEdges(toCollapse);
    //   initializedRef.current = true;
    // }
  }, [layoutData]);

  /**
   * 过滤掉被折叠的节点和边
   * 当弧线被折叠时，其子节点和相关的边都会被隐藏
   * 并且调整后续节点的位置，填补折叠后的空白
   */
  const visibleData = useMemo(() => {
    const NODE_HEIGHT = 110;

    // 1. 找出所有折叠的边
    const allCollapsedEdges = layoutData.edges.filter((e) => e.collapsed);

    // 筛选出“有效”的折叠边：如果一个折叠边被另一个更大的折叠边包含（即其节点被隐藏），则忽略它
    // 避免重复计算 shiftY
    const collapsedEdgesList = allCollapsedEdges.filter((edge) => {
      return !allCollapsedEdges.some((other) => {
        if (edge === other) return false;
        // 如果当前边的源节点或目标节点在另一个折叠边的子节点列表中，说明当前边是被包裹在内部的
        return other.children?.some((child) => child.id === edge.source.id || child.id === edge.target.id);
      });
    });

    // 2. 过滤节点：隐藏被折叠的节点
    const visibleNodesRaw = layoutData.nodes.filter((node) => {
      for (const edge of collapsedEdgesList) {
        if (edge.children?.some((child) => child.id === node.id)) {
          return false; // 节点在折叠的边的子节点中，隐藏
        }
      }
      return true; // 节点可见
    });

    // 3. 计算每个节点需要向上移动的距离
    // 逻辑：如果一个节点位于某个折叠区域的“下方”，它需要向上移动
    const shiftedNodes = visibleNodesRaw.map((node) => {
      let shiftY = 0;

      for (const edge of collapsedEdgesList) {
        // 如果当前节点位于折叠边的目标节点下方（或就是目标节点）
        if (node.y >= edge.target.y) {
          const originalDist = edge.target.y - edge.source.y;
          const collapsedDist = NODE_HEIGHT; // 折叠后只保留一个节点间距
          shiftY += originalDist - collapsedDist;
        }
      }

      return {
        ...node,
        y: node.y - shiftY,
      };
    });

    // 创建一个 id -> node 的映射，方便查找
    const nodeMap = new Map(shiftedNodes.map((n) => [n.id, n]));

    // 4. 过滤并更新边
    const visibleEdges = layoutData.edges
      .filter((edge) => {
        const sourceNode = nodeMap.get(edge.source.id);
        const targetNode = nodeMap.get(edge.target.id);
        // 只显示源节点和目标节点都可见的边，或者虽然中间节点被隐藏但作为连接线的边（折叠后的弧线）
        // 对于折叠的弧线，它的 source 和 target 应该是可见的（因为它们是折叠区域的边界）
        return sourceNode && targetNode;
      })
      .map((edge) => ({
        ...edge,
        source: nodeMap.get(edge.source.id)!,
        target: nodeMap.get(edge.target.id)!,
      }));

    return { nodes: shiftedNodes, edges: visibleEdges };
  }, [layoutData]);

  /**
   * 计算内容尺寸（用于滚动模式）
   * 根据节点位置计算包围盒，确保内容完整显示
   */
  const contentSize = useMemo(() => {
    if (visibleData.nodes.length === 0) return { width: 0, height: 0, minX: 0 };

    let minX = Infinity;
    let maxX = -Infinity;
    let maxY = -Infinity;

    visibleData.nodes.forEach((node) => {
      minX = Math.min(minX, node.x);
      maxX = Math.max(maxX, node.x);
      maxY = Math.max(maxY, node.y);
    });

    // 增加一些内边距，确保边缘不被遮挡
    const startX = Math.min(0, minX - 50);
    const endX = maxX + 150;

    // 在 scroll 模式下，使用实际内容尺寸，不受容器尺寸限制
    // 这样可以保持 1:1 比例，通过滚动条查看超出部分
    return {
      width: endX - startX,
      height: maxY + 150,
      minX: startX,
    };
  }, [visibleData]);

  /**
   * 切换视图模式
   * 在滚动模式和拖拽缩放模式之间切换
   * 切换时重置视图状态，防止位置突变
   */
  const toggleView = () => {
    if (viewMode === "scroll") {
      setViewMode("panzoom");
      // 切换到 panzoom 时重置视图
      resetView();
    } else {
      setViewMode("scroll");
      // 切换回 scroll 时也重置视图
      resetView();
    }
  };

  /**
   * 切换折叠状态
   * 点击弧线时调用，切换该弧线的折叠/展开状态
   */
  const toggleCollapse = useCallback((edgeId: string) => {
    setCollapsedEdges((prev) => {
      const next = new Set(prev);
      if (next.has(edgeId)) {
        next.delete(edgeId); // 已折叠，展开
      } else {
        next.add(edgeId); // 未折叠，折叠
      }
      return next;
    });
  }, []);

  /**
   * 绘制弧线路径
   * 使用二次贝塞尔曲线（Quadratic Bezier Curve）
   *
   * @param source - 源节点
   * @param target - 目标节点
   * @param level - 嵌套层级，用于计算弧线偏移量
   * @returns SVG 路径字符串
   */
  const getArcPath = (source: LayoutNode, target: LayoutNode, level: number) => {
    const sx = source.x;
    const sy = source.y + 25; // 从节点底部出发
    const tx = target.x;
    const ty = target.y - 25; // 到节点顶部结束

    // 弧线向右偏移，偏移量根据层级递减
    // 外层弧线偏移量大，内层弧线偏移量小
    // 增加基础偏移量，让弧线更明显
    const offset = 800 - level * 30;
    const midY = (sy + ty) / 2;

    // Q: 二次贝塞尔曲线，控制点在 (sx + offset, midY)
    return `M ${sx},${sy} Q ${sx + offset},${midY} ${tx},${ty}`;
  };

  /**
   * 绘制直线路径
   *
   * @param source - 源节点
   * @param target - 目标节点
   * @returns SVG 路径字符串
   */
  const getLinePath = (source: LayoutNode, target: LayoutNode) => {
    return `M ${source.x},${source.y + 25} L ${target.x},${target.y - 25}`;
  };

  /**
   * 计算线条粗细
   * 根据嵌套层级递减，外层线条更粗，内层线条更细
   *
   * @param level - 嵌套层级
   * @returns 线条粗细（像素）
   */
  const getStrokeWidth = (level: number) => {
    return Math.max(0.1, 6 - level * 2);
  };

  /**
   * 节点点击处理
   * 区分主链节点和子节点，触发不同的回调
   */
  const handleNodeClick = useCallback(
    (node: LayoutNode) => {
      if (node.type === "goal") {
        // const goalData = node.data as Goal;

        // // 只有具有 sub_trace_ids 的子目标节点（agent 委托执行）才触发 trace 切换
        // // 普通的 sub_goal 节点（蓝色节点）没有 sub_trace_ids，应该打开 DetailPanel
        // const hasSubTraces = goalData.sub_trace_ids && goalData.sub_trace_ids.length > 0;

        // if (node.parentId && onSubTraceClick && hasSubTraces) {
        //   const parentNode = layoutData.nodes.find((n) => n.id === node.parentId);
        //   if (parentNode && parentNode.type === "goal") {
        //     // 取第一个 sub_trace_id 作为跳转目标（使用 trace_id，而非 goal.id）
        //     const firstEntry = goalData.sub_trace_ids![0];
        //     const entry: SubTraceEntry =
        //       typeof firstEntry === "string"
        //         ? { id: firstEntry }
        //         : { id: firstEntry.trace_id, mission: firstEntry.mission };
        //     onSubTraceClick(parentNode.data as Goal, entry);
        //     return;
        //   }
        // }

        // 主链节点 或 没有 sub_trace_ids 的普通子目标节点 → 打开 DetailPanel
        setSelectedNodeId(node.id);
        onNodeClick?.(node.data as Goal);
      } else if (node.type === "message") {
        setSelectedNodeId(node.id);
        onNodeClick?.(node.data as Message);
      }
    },
    [onNodeClick],
  );

  if (!layoutData) return <div>Loading...</div>;

  return (
    <div
      className={styles.container}
      ref={containerRef}
    >
      <div
        className={styles.scrollContainer}
        style={{
          overflow: viewMode === "scroll" ? "auto" : "hidden",
        }}
      >
        <svg
          ref={svgRef}
          viewBox={
            viewMode === "scroll"
              ? `${contentSize.width / 2} 0 ${contentSize.width} ${contentSize.height}`
              : `0 0 ${dimensions.width} ${dimensions.height}`
          }
          preserveAspectRatio="xMidYMid meet"
          className={`${styles.svg} ${isPanning ? styles.panning : ""}`}
          style={{
            cursor: viewMode === "scroll" ? "default" : isPanning ? "grabbing" : "grab",
            width: viewMode === "scroll" ? "100%" : "100%",
            height: viewMode === "scroll" ? contentSize.height : "100%",
            minWidth: viewMode === "scroll" ? contentSize.width : undefined,
            minHeight: viewMode === "scroll" ? contentSize.height : undefined,
          }}
          onWheel={(event) => {
            if (viewMode === "scroll") return; // 滚动模式下使用原生滚动

            // 鼠标滚轮缩放
            event.preventDefault();
            const rect = svgRef.current?.getBoundingClientRect();
            if (!rect) return;
            const cursorX = event.clientX - rect.left;
            const cursorY = event.clientY - rect.top;
            const nextZoom = clampZoom(zoom * (event.deltaY > 0 ? 0.92 : 1.08));
            if (nextZoom === zoom) return;
            const scale = nextZoom / zoom;
            // 以鼠标位置为中心缩放
            setPanOffset((prev) => ({
              x: cursorX - (cursorX - prev.x) * scale,
              y: cursorY - (cursorY - prev.y) * scale,
            }));
            setZoom(nextZoom);
          }}
          onDoubleClick={() => resetView()} // 双击重置视图
          onMouseDown={(event) => {
            if (viewMode === "scroll") return; // 滚动模式下禁用拖拽

            // 开始平移
            if (event.button !== 0) return; // 只响应左键
            const target = event.target as Element;
            // 如果点击的是节点或连接线，不触发平移
            if (target.closest(`.${styles.nodes}`) || target.closest(`.${styles.links}`)) return;
            panStartRef.current = {
              x: event.clientX,
              y: event.clientY,
              originX: panOffset.x,
              originY: panOffset.y,
            };
            setIsPanning(true);
          }}
        >
          <defs>
            <ArrowMarkers /> {/* 箭头标记定义 */}
          </defs>

          {/* 应用平移和缩放变换（仅在 panzoom 模式下） */}
          <g transform={viewMode === "scroll" ? undefined : `translate(${panOffset.x},${panOffset.y}) scale(${zoom})`}>
            {/* 绘制连接线 */}
            <g className={styles.links}>
              {visibleData.edges.map((edge) => {
                // 根据连接线类型选择路径
                const path =
                  edge.type === "arc" && !edge.collapsed
                    ? getArcPath(edge.source, edge.target, edge.level)
                    : getLinePath(edge.source, edge.target);

                const strokeWidth = getStrokeWidth(edge.level); // 根据层级计算线条粗细

                // 根据节点类型决定颜色
                let color = "#2196F3"; // 默认蓝色

                // 判断连接线连接的节点类型
                const sourceIsMessage = edge.source.type === "message";
                const targetIsMessage = edge.target.type === "message";
                const sourceIsMainGoal = edge.source.type === "goal" && edge.source.level === 0;
                const targetIsMainGoal = edge.target.type === "goal" && edge.target.level === 0;

                if (sourceIsMessage || targetIsMessage) {
                  // msgGroup 相关的连接线用灰色
                  color = "#94a3b8"; // Slate 400
                } else if (sourceIsMainGoal && targetIsMainGoal) {
                  // 主节点之间的连接线用绿色
                  color = "#10b981"; // Emerald 500
                } else {
                  // sub_goals 之间的连接线用蓝色
                  color = "#3b82f6"; // Blue 500
                }

                return (
                  <g key={edge.id}>
                    <path
                      d={path}
                      fill="none"
                      stroke={edge.isInvalid ? "#cbd5e1" : color} // 失效边使用浅灰色 (Slate 300)
                      strokeWidth={strokeWidth}
                      strokeDasharray={edge.isInvalid ? "5,5" : undefined} // 失效边使用虚线
                      markerEnd={edge.isInvalid ? undefined : "url(#arrow-default)"} // 失效边不显示箭头
                      style={{ cursor: edge.collapsible ? "pointer" : "default" }} // 可折叠的显示手型光标
                      onClick={() => edge.collapsible && toggleCollapse(edge.id)} // 点击切换折叠状态
                    />
                    {/* 折叠状态提示徽章 */}
                    {edge.collapsed && (
                      <g
                        transform={`translate(${(edge.source.x + edge.target.x) / 2},${
                          (edge.source.y + edge.target.y) / 2
                        })`}
                        onClick={(e) => {
                          e.stopPropagation();
                          toggleCollapse(edge.id);
                        }}
                        style={{ cursor: "pointer" }}
                      >
                        <circle
                          r={10}
                          fill="#FFFFFF"
                          stroke={color}
                          strokeWidth={1}
                        />
                        <text
                          x={0}
                          y={4}
                          fontSize={10}
                          fill={color}
                          textAnchor="middle"
                          fontWeight="bold"
                        >
                          {edge.children ? edge.children.length : "+"}
                        </text>
                      </g>
                    )}
                  </g>
                );
              })}
            </g>

            {/* 绘制节点 */}
            <g className={styles.nodes}>
              {visibleData.nodes.map((node) => {
                const isGoal = node.type === "goal";
                const data = node.data as Goal;
                const text = isGoal ? data.description : (node.data as Message).description || "";

                let thumbnail: string | null = null;
                if (node.type === "message") {
                  const images = extractImagesFromMessage(node.data as Message);
                  if (images.length > 0) thumbnail = images[0].url;
                }

                let textColor = "#3b82f6"; // Blue 500
                if (node.type === "message") {
                  textColor = "#64748b"; // Slate 500
                } else if (node.type === "goal" && node.level === 0) {
                  textColor = "#10b981"; // Emerald 500
                }

                if (node.isInvalid) {
                  textColor = "#94a3b8"; // Slate 400
                }

                return (
                  <g
                    key={node.id}
                    transform={`translate(${node.x},${node.y})`}
                    onClick={() => handleNodeClick(node)}
                    style={{ cursor: "pointer" }}
                  >
                    {/* 节点矩形 */}
                    <rect
                      x={-70}
                      y={-25}
                      width={150}
                      height={50}
                      rx={8}
                      fill={isGoal ? "#eff6ff" : "#f8fafc"} // Blue 50 / Slate 50
                      stroke={selectedNodeId === node.id ? "#3b82f6" : node.isInvalid ? "#cbd5e1" : "#e2e8f0"} // Selected: Blue 500, Invalid: Slate 300, Default: Slate 200
                      strokeWidth={selectedNodeId === node.id ? 2 : 1}
                      strokeDasharray={node.isInvalid ? "5,5" : undefined} // 失效节点虚线边框
                      style={{
                        filter:
                          selectedNodeId === node.id
                            ? "drop-shadow(0 4px 6px rgb(59 130 246 / 0.3))"
                            : "drop-shadow(0 1px 2px rgb(0 0 0 / 0.05))",
                      }}
                    />
                    {/* 节点文本 */}
                    <foreignObject
                      x={-70}
                      y={-25}
                      width={150}
                      height={50}
                    >
                      <div
                        className="w-full h-full overflow-hidden flex items-center px-2 gap-2"
                        style={{
                          color: textColor,
                          justifyContent: thumbnail ? "space-between" : "center",
                        }}
                      >
                        <span className={`text-xs line-clamp-3 ${thumbnail ? "flex-1 text-left" : "text-center"}`}>
                          {text}
                        </span>
                        {thumbnail && (
                          <img
                            src={thumbnail}
                            alt="thumb"
                            className="w-8 h-8 object-cover rounded border border-gray-200 bg-white flex-shrink-0 hover:scale-110 transition-transform"
                            loading="lazy"
                            onClick={(e) => {
                              e.stopPropagation();
                              setPreviewImage(thumbnail);
                            }}
                          />
                        )}
                      </div>
                    </foreignObject>
                  </g>
                );
              })}
            </g>
          </g>
        </svg>
      </div>

      {/* 控制按钮 */}
      <div className={styles.controls}>
        {viewMode === "panzoom" && (
          <>
            <button
              type="button"
              className={styles.controlButton}
              onClick={() => setZoom((prev) => clampZoom(prev * 1.1))}
            >
              + {/* 放大 */}
            </button>
            <button
              type="button"
              className={styles.controlButton}
              onClick={() => setZoom((prev) => clampZoom(prev * 0.9))}
            >
              − {/* 缩小 */}
            </button>
            <button
              type="button"
              className={styles.controlButton}
              onClick={resetView}
            >
              复位
            </button>
          </>
        )}
        <button
          type="button"
          className={styles.controlButton}
          onClick={toggleView}
        >
          {viewMode === "scroll" ? "切换拖拽" : "切换滚动"}
        </button>
      </div>

      <ImagePreviewModal
        visible={!!previewImage}
        onClose={() => setPreviewImage(null)}
        src={previewImage || ""}
      />
    </div>
  );
};

export const FlowChart = forwardRef(FlowChartComponent);
