#!/usr/bin/env node

const fs = require('fs');
const path = require('path');
const { build } = require('esbuild');

// 读取命令行参数
const args = process.argv.slice(2);
if (args.length === 0) {
  console.error('Usage: node index.js <path-to-query_graph.json> [output.html]');
  process.exit(1);
}

const inputFile = args[0];
const outputFile = args[1] || 'query_graph_output.html';

// 读取输入数据
const data = JSON.parse(fs.readFileSync(inputFile, 'utf-8'));

// 创建临时 React 组件文件
const reactComponentPath = path.join(__dirname, 'temp_flow_component_v2.jsx');
const reactComponent = `
import React, { useState, useCallback, useMemo, useEffect } from 'react';
import { createRoot } from 'react-dom/client';
import {
  ReactFlow,
  Controls,
  Background,
  useNodesState,
  useEdgesState,
  Handle,
  Position,
  useReactFlow,
  ReactFlowProvider,
} from '@xyflow/react';
import '@xyflow/react/dist/style.css';

const data = ${JSON.stringify(data, null, 2)};

// 查询节点组件 - 卡片样式
function QueryNode({ id, data, sourcePosition, targetPosition }) {
  // 所有节点默认展开
  const expanded = true;

  return (
    <div>
      <Handle
        type="target"
        position={targetPosition || Position.Left}
        style={{ background: '#667eea', width: 8, height: 8 }}
      />
      <div
        style={{
          padding: '12px',
          borderRadius: '8px',
          border: data.isHighlighted ? '3px solid #667eea' :
                  data.isCollapsed ? '2px solid #667eea' :
                  data.isSelected === false ? '2px dashed #d1d5db' :
                  data.level === 0 ? '2px solid #8b5cf6' : '1px solid #e5e7eb',
          background: data.isHighlighted ? '#eef2ff' :
                      data.isSelected === false ? '#f9fafb' : 'white',
          minWidth: '200px',
          maxWidth: '280px',
          boxShadow: data.isHighlighted ? '0 0 0 4px rgba(102, 126, 234, 0.25), 0 4px 16px rgba(102, 126, 234, 0.4)' :
                     data.isCollapsed ? '0 4px 12px rgba(102, 126, 234, 0.15)' :
                     data.level === 0 ? '0 4px 12px rgba(139, 92, 246, 0.15)' : '0 2px 6px rgba(0, 0, 0, 0.06)',
          transition: 'all 0.3s ease',
          cursor: 'pointer',
          position: 'relative',
          opacity: data.isSelected === false ? 0.6 : 1,
        }}
      >
        {/* 折叠当前节点按钮 - 左边 */}
        <div
          style={{
            position: 'absolute',
            top: '6px',
            left: '6px',
            width: '20px',
            height: '20px',
            borderRadius: '50%',
            background: '#f59e0b',
            color: 'white',
            display: 'flex',
            alignItems: 'center',
            justifyContent: 'center',
            fontSize: '11px',
            fontWeight: 'bold',
            cursor: 'pointer',
            transition: 'all 0.2s ease',
            zIndex: 10,
          }}
          onClick={(e) => {
            e.stopPropagation();
            if (data.onHideSelf) {
              data.onHideSelf();
            }
          }}
          onMouseEnter={(e) => {
            e.currentTarget.style.background = '#d97706';
          }}
          onMouseLeave={(e) => {
            e.currentTarget.style.background = '#f59e0b';
          }}
          title="隐藏当前节点"
        >
          ×
        </div>

        {/* 聚焦按钮 - 右上角 */}
        <div
          style={{
            position: 'absolute',
            top: '6px',
            right: '6px',
            width: '20px',
            height: '20px',
            borderRadius: '50%',
            background: data.isFocused ? '#10b981' : '#e5e7eb',
            color: data.isFocused ? 'white' : '#6b7280',
            display: 'flex',
            alignItems: 'center',
            justifyContent: 'center',
            fontSize: '11px',
            fontWeight: 'bold',
            cursor: 'pointer',
            transition: 'all 0.2s ease',
            zIndex: 10,
          }}
          onClick={(e) => {
            e.stopPropagation();
            if (data.onFocus) {
              data.onFocus();
            }
          }}
          onMouseEnter={(e) => {
            if (!data.isFocused) {
              e.currentTarget.style.background = '#d1d5db';
            }
          }}
          onMouseLeave={(e) => {
            if (!data.isFocused) {
              e.currentTarget.style.background = '#e5e7eb';
            }
          }}
          title={data.isFocused ? '取消聚焦' : '聚焦到此节点'}
        >
          🎯
        </div>

        {/* 折叠/展开子节点按钮 - 右边第二个位置 */}
        {data.hasChildren && (
          <div
            style={{
              position: 'absolute',
              top: '6px',
              right: '30px',
              width: '20px',
              height: '20px',
              borderRadius: '50%',
              background: data.isCollapsed ? '#667eea' : '#e5e7eb',
              color: data.isCollapsed ? 'white' : '#6b7280',
              display: 'flex',
              alignItems: 'center',
              justifyContent: 'center',
              fontSize: '11px',
              fontWeight: 'bold',
              cursor: 'pointer',
              transition: 'all 0.2s ease',
              zIndex: 10,
            }}
            onClick={(e) => {
              e.stopPropagation();
              data.onToggleCollapse();
            }}
            title={data.isCollapsed ? '展开子节点' : '折叠子节点'}
          >
            {data.isCollapsed ? '+' : '−'}
          </div>
        )}

        {/* 卡片内容 */}
        <div>
          {/* 标题行 */}
          <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: '8px', paddingLeft: '24px', paddingRight: data.hasChildren ? '54px' : '28px' }}>
            <div style={{ flex: 1 }}>
              <div style={{ display: 'flex', alignItems: 'center', gap: '4px', marginBottom: '3px' }}>
                <div style={{
                  fontSize: '13px',
                  fontWeight: data.level === 0 ? '700' : '600',
                  color: data.level === 0 ? '#6b21a8' : '#1f2937',
                  lineHeight: '1.3',
                  flex: 1,
                }}>
                  {data.title}
                </div>
                {data.isSelected === false && (
                  <div style={{
                    fontSize: '9px',
                    padding: '1px 4px',
                    borderRadius: '3px',
                    background: '#fee2e2',
                    color: '#991b1b',
                    fontWeight: '500',
                    flexShrink: 0,
                  }}>
                    未选中
                  </div>
                )}
              </div>
            </div>
          </div>

        {/* 展开的详细信息 - 始终显示 */}
        <div style={{ fontSize: '11px', lineHeight: 1.4 }}>
            <div style={{ display: 'flex', gap: '4px', marginBottom: '6px', flexWrap: 'wrap' }}>
              <span style={{
                display: 'inline-block',
                padding: '1px 6px',
                borderRadius: '10px',
                background: '#eff6ff',
                color: '#3b82f6',
                fontSize: '10px',
                fontWeight: '500',
              }}>
                Lv.{data.level}
              </span>
              <span style={{
                display: 'inline-block',
                padding: '1px 6px',
                borderRadius: '10px',
                background: '#f0fdf4',
                color: '#16a34a',
                fontSize: '10px',
                fontWeight: '500',
              }}>
                {data.score}
              </span>
              {data.strategy && data.strategy !== 'root' && (
                <span style={{
                  display: 'inline-block',
                  padding: '1px 6px',
                  borderRadius: '10px',
                  background: '#fef3c7',
                  color: '#92400e',
                  fontSize: '10px',
                  fontWeight: '500',
                }}>
                  {data.strategy}
                </span>
              )}
            </div>

            {data.parent && (
              <div style={{ color: '#6b7280', fontSize: '10px', marginTop: '4px', paddingTop: '4px', borderTop: '1px solid #f3f4f6' }}>
                <strong>Parent:</strong> {data.parent}
              </div>
            )}
            {data.evaluationReason && (
              <div style={{
                marginTop: '6px',
                paddingTop: '6px',
                borderTop: '1px solid #f3f4f6',
                fontSize: '10px',
                color: '#6b7280',
                lineHeight: '1.5',
              }}>
                <strong style={{ color: '#4b5563' }}>评估:</strong>
                <div style={{ marginTop: '2px' }}>{data.evaluationReason}</div>
              </div>
            )}
          </div>
        </div>
      </div>
      <Handle
        type="source"
        position={sourcePosition || Position.Right}
        style={{ background: '#667eea', width: 8, height: 8 }}
      />
    </div>
  );
}

// 笔记节点组件 - 卡片样式，带轮播图
function NoteNode({ id, data, sourcePosition, targetPosition }) {
  const [currentImageIndex, setCurrentImageIndex] = useState(0);
  const expanded = true;
  const hasImages = data.imageList && data.imageList.length > 0;

  const nextImage = (e) => {
    e.stopPropagation();
    if (hasImages) {
      setCurrentImageIndex((prev) => (prev + 1) % data.imageList.length);
    }
  };

  const prevImage = (e) => {
    e.stopPropagation();
    if (hasImages) {
      setCurrentImageIndex((prev) => (prev - 1 + data.imageList.length) % data.imageList.length);
    }
  };

  return (
    <div>
      <Handle
        type="target"
        position={targetPosition || Position.Left}
        style={{ background: '#ec4899', width: 8, height: 8 }}
      />
      <div
        style={{
          padding: '14px',
          borderRadius: '20px',
          border: data.isHighlighted ? '3px solid #ec4899' : '2px solid #fce7f3',
          background: data.isHighlighted ? '#eef2ff' : 'white',
          minWidth: '220px',
          maxWidth: '300px',
          boxShadow: data.isHighlighted ? '0 0 0 4px rgba(236, 72, 153, 0.25), 0 4px 16px rgba(236, 72, 153, 0.4)' : '0 4px 12px rgba(236, 72, 153, 0.15)',
          transition: 'all 0.3s ease',
          cursor: 'pointer',
        }}
      >
        {/* 笔记图标和标题 */}
        <div style={{ display: 'flex', alignItems: 'flex-start', marginBottom: '8px' }}>
          <span style={{ fontSize: '16px', marginRight: '8px' }}>📝</span>
          <div style={{ flex: 1 }}>
            <div style={{
              fontSize: '13px',
              fontWeight: '600',
              color: '#831843',
              lineHeight: '1.4',
              marginBottom: '4px',
            }}>
              {data.title}
            </div>
          </div>
        </div>

        {/* 轮播图 */}
        {hasImages && (
          <div style={{
            position: 'relative',
            marginBottom: '8px',
            borderRadius: '12px',
            overflow: 'hidden',
          }}>
            <img
              src={data.imageList[currentImageIndex].image_url}
              alt={\`Image \${currentImageIndex + 1}\`}
              style={{
                width: '100%',
                height: '160px',
                objectFit: 'cover',
                display: 'block',
              }}
              onError={(e) => {
                e.target.style.display = 'none';
              }}
            />
            {data.imageList.length > 1 && (
              <>
                {/* 左右切换按钮 */}
                <button
                  onClick={prevImage}
                  style={{
                    position: 'absolute',
                    left: '4px',
                    top: '50%',
                    transform: 'translateY(-50%)',
                    background: 'rgba(0, 0, 0, 0.5)',
                    color: 'white',
                    border: 'none',
                    borderRadius: '50%',
                    width: '24px',
                    height: '24px',
                    cursor: 'pointer',
                    display: 'flex',
                    alignItems: 'center',
                    justifyContent: 'center',
                    fontSize: '14px',
                  }}
                >
                  ‹
                </button>
                <button
                  onClick={nextImage}
                  style={{
                    position: 'absolute',
                    right: '4px',
                    top: '50%',
                    transform: 'translateY(-50%)',
                    background: 'rgba(0, 0, 0, 0.5)',
                    color: 'white',
                    border: 'none',
                    borderRadius: '50%',
                    width: '24px',
                    height: '24px',
                    cursor: 'pointer',
                    display: 'flex',
                    alignItems: 'center',
                    justifyContent: 'center',
                    fontSize: '14px',
                  }}
                >
                  ›
                </button>
                {/* 图片计数 */}
                <div style={{
                  position: 'absolute',
                  bottom: '4px',
                  right: '4px',
                  background: 'rgba(0, 0, 0, 0.6)',
                  color: 'white',
                  padding: '2px 6px',
                  borderRadius: '10px',
                  fontSize: '10px',
                }}>
                  {currentImageIndex + 1}/{data.imageList.length}
                </div>
              </>
            )}
          </div>
        )}

        {/* 标签 */}
        <div style={{ display: 'flex', gap: '6px', marginBottom: '8px', flexWrap: 'wrap' }}>
          <span style={{
            display: 'inline-block',
            padding: '2px 8px',
            borderRadius: '12px',
            background: '#fff1f2',
            color: '#be123c',
            fontSize: '10px',
            fontWeight: '500',
          }}>
            {data.matchLevel}
          </span>
          <span style={{
            display: 'inline-block',
            padding: '2px 8px',
            borderRadius: '12px',
            background: '#fff7ed',
            color: '#c2410c',
            fontSize: '10px',
            fontWeight: '500',
          }}>
            Score: {data.score}
          </span>
        </div>

        {/* 描述 */}
        {expanded && data.description && (
          <div style={{
            fontSize: '11px',
            color: '#9f1239',
            lineHeight: '1.5',
            paddingTop: '8px',
            borderTop: '1px solid #fbcfe8',
          }}>
            {data.description}
          </div>
        )}

        {/* 评估理由 */}
        {expanded && data.evaluationReason && (
          <div style={{
            fontSize: '10px',
            color: '#831843',
            lineHeight: '1.5',
            paddingTop: '8px',
            marginTop: '8px',
            borderTop: '1px solid #fbcfe8',
          }}>
            <strong style={{ color: '#9f1239' }}>评估:</strong>
            <div style={{ marginTop: '2px' }}>{data.evaluationReason}</div>
          </div>
        )}
      </div>
      <Handle
        type="source"
        position={sourcePosition || Position.Right}
        style={{ background: '#ec4899', width: 8, height: 8 }}
      />
    </div>
  );
}

const nodeTypes = {
  query: QueryNode,
  note: NoteNode,
};

// 根据 score 获取颜色
function getScoreColor(score) {
  if (score >= 0.7) return '#10b981'; // 绿色 - 高分
  if (score >= 0.4) return '#f59e0b'; // 橙色 - 中分
  return '#ef4444'; // 红色 - 低分
}

// 截断文本，保留头尾，中间显示省略号
function truncateMiddle(text, maxLength = 20) {
  if (!text || text.length <= maxLength) return text;
  const headLength = Math.ceil(maxLength * 0.4);
  const tailLength = Math.floor(maxLength * 0.4);
  const head = text.substring(0, headLength);
  const tail = text.substring(text.length - tailLength);
  return \`\${head}...\${tail}\`;
}

// 根据策略获取颜色
function getStrategyColor(strategy) {
  const strategyColors = {
    '初始分词': '#10b981',
    '调用sug': '#06b6d4',
    '同义改写': '#f59e0b',
    '加词': '#3b82f6',
    '抽象改写': '#8b5cf6',
    '基于部分匹配改进': '#ec4899',
    '结果分支-抽象改写': '#a855f7',
    '结果分支-同义改写': '#fb923c',
  };
  return strategyColors[strategy] || '#9ca3af';
}

// 树节点组件
function TreeNode({ node, level, children, isCollapsed, onToggle, isSelected, onSelect }) {
  const hasChildren = children && children.length > 0;
  const score = node.data.score ? parseFloat(node.data.score) : 0;
  const strategy = node.data.strategy || '';
  const strategyColor = getStrategyColor(strategy);

  return (
    <div style={{ marginLeft: level * 12 + 'px' }}>
      <div
        style={{
          padding: '6px 8px',
          borderRadius: '4px',
          cursor: 'pointer',
          background: 'transparent',
          border: isSelected ? '1px solid #3b82f6' : '1px solid transparent',
          display: 'flex',
          alignItems: 'center',
          gap: '6px',
          transition: 'all 0.2s ease',
          position: 'relative',
          overflow: 'visible',
        }}
        onMouseEnter={(e) => {
          if (!isSelected) e.currentTarget.style.background = '#f9fafb';
        }}
        onMouseLeave={(e) => {
          if (!isSelected) e.currentTarget.style.background = 'transparent';
        }}
      >
        {/* 策略类型竖线 */}
        <div style={{
          width: '3px',
          height: '20px',
          background: strategyColor,
          borderRadius: '2px',
          flexShrink: 0,
          position: 'relative',
          zIndex: 1,
        }} />

        {hasChildren && (
          <span
            style={{
              fontSize: '10px',
              color: '#6b7280',
              cursor: 'pointer',
              width: '16px',
              textAlign: 'center',
              position: 'relative',
              zIndex: 1,
            }}
            onClick={(e) => {
              e.stopPropagation();
              onToggle();
            }}
          >
            {isCollapsed ? '▶' : '▼'}
          </span>
        )}
        {!hasChildren && <span style={{ width: '16px', position: 'relative', zIndex: 1 }}></span>}

        <div
          style={{
            flex: 1,
            fontSize: '12px',
            color: '#374151',
            position: 'relative',
            zIndex: 1,
            minWidth: 0,
            display: 'flex',
            flexDirection: 'column',
            gap: '4px',
          }}
          onClick={onSelect}
        >
          <div style={{
            display: 'flex',
            alignItems: 'center',
            gap: '8px',
          }}>
            {/* 节点类型图标 */}
            <span style={{
              fontSize: '12px',
              flexShrink: 0,
            }}>
              {node.type === 'note' ? '📝' : '🔍'}
            </span>

            <div style={{
              fontWeight: level === 0 ? '600' : '400',
              maxWidth: '180px',
              flex: 1,
              minWidth: 0,
              color: (node.type === 'note' ? node.data.matchLevel === 'unsatisfied' : node.data.isSelected === false) ? '#ef4444' : '#374151',
            }}
            title={node.data.title || node.id}
            >
              {truncateMiddle(node.data.title || node.id, 18)}
            </div>

            {/* 分数显示 */}
            <span style={{
              fontSize: '11px',
              color: '#6b7280',
              fontWeight: '500',
              flexShrink: 0,
            }}>
              {score.toFixed(2)}
            </span>
          </div>

          {/* 分数下划线 */}
          <div style={{
            width: (score * 100) + '%',
            height: '2px',
            background: getScoreColor(score),
            borderRadius: '1px',
          }} />
        </div>
      </div>

      {hasChildren && !isCollapsed && (
        <div>
          {children}
        </div>
      )}
    </div>
  );
}

// 使用 dagre 自动布局
function getLayoutedElements(nodes, edges, direction = 'LR') {
  console.log('🎯 Starting layout with dagre...');
  console.log('Input:', nodes.length, 'nodes,', edges.length, 'edges');

  // 检查 dagre 是否加载
  if (typeof window === 'undefined' || typeof window.dagre === 'undefined') {
    console.warn('⚠️ Dagre not loaded, using fallback layout');
    // 降级到简单布局
    const levelGroups = {};
    nodes.forEach(node => {
      const level = node.data.level || 0;
      if (!levelGroups[level]) levelGroups[level] = [];
      levelGroups[level].push(node);
    });

    Object.entries(levelGroups).forEach(([level, nodeList]) => {
      const x = parseInt(level) * 350;
      nodeList.forEach((node, index) => {
        node.position = { x, y: index * 150 };
        node.targetPosition = 'left';
        node.sourcePosition = 'right';
      });
    });

    return { nodes, edges };
  }

  try {
    const dagreGraph = new window.dagre.graphlib.Graph();
    dagreGraph.setDefaultEdgeLabel(() => ({}));

    const isHorizontal = direction === 'LR';
    dagreGraph.setGraph({
      rankdir: direction,
      nodesep: 120,   // 垂直间距 - 增加以避免节点重叠
      ranksep: 280,  // 水平间距 - 增加以容纳更宽的节点
    });

    // 添加节点 - 根据节点类型设置不同的尺寸
    nodes.forEach((node) => {
      let nodeWidth = 280;
      let nodeHeight = 180;

      // note 节点有轮播图，需要更大的空间
      if (node.type === 'note') {
        nodeWidth = 320;
        nodeHeight = 350;  // 增加高度以容纳轮播图
      }

      dagreGraph.setNode(node.id, { width: nodeWidth, height: nodeHeight });
    });

    // 添加边
    edges.forEach((edge) => {
      dagreGraph.setEdge(edge.source, edge.target);
    });

    // 计算布局
    window.dagre.layout(dagreGraph);
    console.log('✅ Dagre layout completed');

    // 更新节点位置和 handle 位置
    nodes.forEach((node) => {
      const nodeWithPosition = dagreGraph.node(node.id);

      if (!nodeWithPosition) {
        console.warn('Node position not found for:', node.id);
        return;
      }

      node.targetPosition = isHorizontal ? 'left' : 'top';
      node.sourcePosition = isHorizontal ? 'right' : 'bottom';

      // 根据节点类型获取尺寸
      let nodeWidth = 280;
      let nodeHeight = 180;
      if (node.type === 'note') {
        nodeWidth = 320;
        nodeHeight = 350;
      }

      // 将 dagre 的中心点位置转换为 React Flow 的左上角位置
      node.position = {
        x: nodeWithPosition.x - nodeWidth / 2,
        y: nodeWithPosition.y - nodeHeight / 2,
      };
    });

    console.log('✅ Layout completed, sample node:', nodes[0]);
    return { nodes, edges };
  } catch (error) {
    console.error('❌ Error in dagre layout:', error);
    console.error('Error details:', error.message, error.stack);

    // 降级处理
    console.log('Using fallback layout...');
    const levelGroups = {};
    nodes.forEach(node => {
      const level = node.data.level || 0;
      if (!levelGroups[level]) levelGroups[level] = [];
      levelGroups[level].push(node);
    });

    Object.entries(levelGroups).forEach(([level, nodeList]) => {
      const x = parseInt(level) * 350;
      nodeList.forEach((node, index) => {
        node.position = { x, y: index * 150 };
        node.targetPosition = 'left';
        node.sourcePosition = 'right';
      });
    });

    return { nodes, edges };
  }
}

function transformData(data) {
  const nodes = [];
  const edges = [];

  const originalIdToCanvasId = {}; // 原始ID -> 画布ID的映射
  const canvasIdToNodeData = {}; // 避免重复创建相同的节点

  // 创建节点
  Object.entries(data.nodes).forEach(([originalId, node]) => {
    if (node.type === 'query') {
      // 使用 query_level 作为唯一ID
      const canvasId = node.query + '_' + node.level;
      originalIdToCanvasId[originalId] = canvasId;

      // 如果这个 canvasId 还没有创建过节点，则创建
      if (!canvasIdToNodeData[canvasId]) {
        canvasIdToNodeData[canvasId] = true;
        nodes.push({
          id: canvasId, // 使用 query_level 格式
          originalId: originalId, // 保留原始ID用于调试
          type: 'query',
          data: {
            title: node.query,
            level: node.level,
            score: node.relevance_score.toFixed(2),
            strategy: node.strategy,
            parent: node.parent_query,
            isSelected: node.is_selected,
            evaluationReason: node.evaluation_reason || '',
          },
          position: { x: 0, y: 0 }, // 初始位置，会被 dagre 覆盖
        });
      }
    } else if (node.type === 'note') {
      // note节点直接使用原始ID
      originalIdToCanvasId[originalId] = originalId;

      if (!canvasIdToNodeData[originalId]) {
        canvasIdToNodeData[originalId] = true;
        nodes.push({
          id: originalId,
          originalId: originalId,
          type: 'note',
          data: {
            title: node.title,
            matchLevel: node.match_level,
            score: node.relevance_score.toFixed(2),
            description: node.desc,
            isSelected: node.is_selected !== undefined ? node.is_selected : true,
            imageList: node.image_list || [], // 添加图片列表
            noteUrl: node.note_url || '', // 添加帖子链接
            evaluationReason: node.evaluation_reason || '', // 添加评估理由
          },
          position: { x: 0, y: 0 },
        });
      }
    }
  });

  // 创建边 - 使用虚线样式，映射到画布ID
  data.edges.forEach((edge, index) => {
    const edgeColors = {
      '初始分词': '#10b981',
      '调用sug': '#06b6d4',
      '同义改写': '#f59e0b',
      '加词': '#3b82f6',
      '抽象改写': '#8b5cf6',
      '基于部分匹配改进': '#ec4899',
      '结果分支-抽象改写': '#a855f7',
      '结果分支-同义改写': '#fb923c',
      'query_to_note': '#ec4899',
    };

    const color = edgeColors[edge.strategy] || edgeColors[edge.edge_type] || '#d1d5db';
    const isNoteEdge = edge.edge_type === 'query_to_note';

    edges.push({
      id: \`edge-\${index}\`,
      source: originalIdToCanvasId[edge.from], // 使用画布ID
      target: originalIdToCanvasId[edge.to],   // 使用画布ID
      type: 'simplebezier', // 使用简单贝塞尔曲线
      animated: isNoteEdge,
      style: {
        stroke: color,
        strokeWidth: isNoteEdge ? 2.5 : 2,
        strokeDasharray: isNoteEdge ? '5,5' : '8,4',
      },
      markerEnd: {
        type: 'arrowclosed',
        color: color,
        width: 20,
        height: 20,
      },
    });
  });

  // 使用 dagre 自动计算布局 - 从左到右
  return getLayoutedElements(nodes, edges, 'LR');
}

function FlowContent() {
  const { nodes: initialNodes, edges: initialEdges } = useMemo(() => {
    console.log('🔍 Transforming data...');
    const result = transformData(data);
    console.log('✅ Transformed:', result.nodes.length, 'nodes,', result.edges.length, 'edges');
    return result;
  }, []);

  // 初始化：找出所有有子节点的节点，默认折叠（画布节点）
  const initialCollapsedNodes = useMemo(() => {
    const nodesWithChildren = new Set();
    initialEdges.forEach(edge => {
      nodesWithChildren.add(edge.source);
    });
    // 排除根节点（level 0），让根节点默认展开
    const rootNode = initialNodes.find(n => n.data.level === 0);
    if (rootNode) {
      nodesWithChildren.delete(rootNode.id);
    }
    return nodesWithChildren;
  }, [initialNodes, initialEdges]);

  // 树节点的折叠状态需要在树构建后初始化
  const [collapsedNodes, setCollapsedNodes] = useState(() => initialCollapsedNodes);
  const [collapsedTreeNodes, setCollapsedTreeNodes] = useState(new Set());
  const [selectedNodeId, setSelectedNodeId] = useState(null);
  const [hiddenNodes, setHiddenNodes] = useState(new Set()); // 用户手动隐藏的节点
  const [focusMode, setFocusMode] = useState(false); // 全局聚焦模式，默认关闭
  const [focusedNodeId, setFocusedNodeId] = useState(null); // 单独聚焦的节点ID

  // 获取 React Flow 实例以控制画布
  const { setCenter, fitView } = useReactFlow();

  // 获取某个节点的所有后代节点ID
  const getDescendants = useCallback((nodeId) => {
    const descendants = new Set();
    const queue = [nodeId];

    while (queue.length > 0) {
      const current = queue.shift();
      initialEdges.forEach(edge => {
        if (edge.source === current && !descendants.has(edge.target)) {
          descendants.add(edge.target);
          queue.push(edge.target);
        }
      });
    }

    return descendants;
  }, [initialEdges]);

  // 获取直接父节点
  const getDirectParents = useCallback((nodeId) => {
    const parents = [];
    initialEdges.forEach(edge => {
      if (edge.target === nodeId) {
        parents.push(edge.source);
      }
    });
    return parents;
  }, [initialEdges]);

  // 获取直接子节点
  const getDirectChildren = useCallback((nodeId) => {
    const children = [];
    initialEdges.forEach(edge => {
      if (edge.source === nodeId) {
        children.push(edge.target);
      }
    });
    return children;
  }, [initialEdges]);

  // 切换节点折叠状态
  const toggleNodeCollapse = useCallback((nodeId) => {
    setCollapsedNodes(prev => {
      const newSet = new Set(prev);
      const descendants = getDescendants(nodeId);

      if (newSet.has(nodeId)) {
        // 展开：移除此节点，但保持其他折叠的节点
        newSet.delete(nodeId);
      } else {
        // 折叠：添加此节点
        newSet.add(nodeId);
      }

      return newSet;
    });
  }, [getDescendants]);

  // 过滤可见的节点和边，并重新计算布局
  const { nodes, edges } = useMemo(() => {
    const nodesToHide = new Set();

    // 判断使用哪个节点ID进行聚焦：优先使用单独聚焦的节点，否则使用全局聚焦模式的选中节点
    const effectiveFocusNodeId = focusedNodeId || (focusMode ? selectedNodeId : null);

    // 聚焦模式：只显示聚焦节点、其父节点和直接子节点
    if (effectiveFocusNodeId) {
      const visibleInFocus = new Set([effectiveFocusNodeId]);

      // 添加所有父节点
      initialEdges.forEach(edge => {
        if (edge.target === effectiveFocusNodeId) {
          visibleInFocus.add(edge.source);
        }
      });

      // 添加所有直接子节点
      initialEdges.forEach(edge => {
        if (edge.source === effectiveFocusNodeId) {
          visibleInFocus.add(edge.target);
        }
      });

      // 隐藏不在聚焦范围内的节点
      initialNodes.forEach(node => {
        if (!visibleInFocus.has(node.id)) {
          nodesToHide.add(node.id);
        }
      });
    } else {
      // 非聚焦模式：使用原有的折叠逻辑
      // 收集所有被折叠节点的后代
      collapsedNodes.forEach(collapsedId => {
        const descendants = getDescendants(collapsedId);
        descendants.forEach(id => nodesToHide.add(id));
      });
    }

    // 添加用户手动隐藏的节点
    hiddenNodes.forEach(id => nodesToHide.add(id));

    const visibleNodes = initialNodes
      .filter(node => !nodesToHide.has(node.id))
      .map(node => ({
        ...node,
        data: {
          ...node.data,
          isCollapsed: collapsedNodes.has(node.id),
          hasChildren: initialEdges.some(e => e.source === node.id),
          onToggleCollapse: () => toggleNodeCollapse(node.id),
          onHideSelf: () => {
            setHiddenNodes(prev => {
              const newSet = new Set(prev);
              newSet.add(node.id);
              return newSet;
            });
          },
          onFocus: () => {
            // 切换聚焦状态
            if (focusedNodeId === node.id) {
              setFocusedNodeId(null); // 如果已经聚焦，则取消聚焦
            } else {
              // 先取消之前的聚焦，然后聚焦到当前节点
              setFocusedNodeId(node.id);

              // 延迟聚焦视图到该节点
              setTimeout(() => {
                fitView({
                  nodes: [{ id: node.id }],
                  duration: 800,
                  padding: 0.3,
                });
              }, 100);
            }
          },
          isFocused: focusedNodeId === node.id,
          isHighlighted: selectedNodeId === node.id,
        }
      }));

    const visibleEdges = initialEdges.filter(
      edge => !nodesToHide.has(edge.source) && !nodesToHide.has(edge.target)
    );

    // 重新计算布局 - 只对可见节点
    if (typeof window !== 'undefined' && typeof window.dagre !== 'undefined') {
      try {
        const dagreGraph = new window.dagre.graphlib.Graph();
        dagreGraph.setDefaultEdgeLabel(() => ({}));

        dagreGraph.setGraph({
          rankdir: 'LR',
          nodesep: 120,   // 垂直间距 - 增加以避免节点重叠
          ranksep: 280,  // 水平间距 - 增加以容纳更宽的节点
        });

        visibleNodes.forEach((node) => {
          let nodeWidth = 280;
          let nodeHeight = 180;

          // note 节点有轮播图，需要更大的空间
          if (node.type === 'note') {
            nodeWidth = 320;
            nodeHeight = 350;
          }

          dagreGraph.setNode(node.id, { width: nodeWidth, height: nodeHeight });
        });

        visibleEdges.forEach((edge) => {
          dagreGraph.setEdge(edge.source, edge.target);
        });

        window.dagre.layout(dagreGraph);

        visibleNodes.forEach((node) => {
          const nodeWithPosition = dagreGraph.node(node.id);
          if (nodeWithPosition) {
            // 根据节点类型获取对应的尺寸
            let nodeWidth = 280;
            let nodeHeight = 180;
            if (node.type === 'note') {
              nodeWidth = 320;
              nodeHeight = 350;
            }

            node.position = {
              x: nodeWithPosition.x - nodeWidth / 2,
              y: nodeWithPosition.y - nodeHeight / 2,
            };
            node.targetPosition = 'left';
            node.sourcePosition = 'right';
          }
        });

        console.log('✅ Dynamic layout recalculated for', visibleNodes.length, 'visible nodes');
      } catch (error) {
        console.error('❌ Error in dynamic layout:', error);
      }
    }

    return { nodes: visibleNodes, edges: visibleEdges };
  }, [initialNodes, initialEdges, collapsedNodes, hiddenNodes, focusMode, focusedNodeId, getDescendants, toggleNodeCollapse, selectedNodeId]);

  // 构建树形结构 - 允许一个节点有多个父节点
  const buildTree = useCallback(() => {
    const nodeMap = new Map();
    initialNodes.forEach(node => {
      nodeMap.set(node.id, node);
    });

    // 为每个节点创建树节点的副本（允许多次出现）
    const createTreeNode = (nodeId, pathKey) => {
      const node = nodeMap.get(nodeId);
      if (!node) return null;

      return {
        ...node,
        treeKey: pathKey, // 唯一的树路径key，用于React key
        children: []
      };
    };

    // 构建父子关系映射：记录每个节点的所有父节点，去重边
    const parentToChildren = new Map();
    const childToParents = new Map();

    initialEdges.forEach(edge => {
      // 记录父->子关系（去重：同一个父节点到同一个子节点只记录一次）
      if (!parentToChildren.has(edge.source)) {
        parentToChildren.set(edge.source, []);
      }
      const children = parentToChildren.get(edge.source);
      if (!children.includes(edge.target)) {
        children.push(edge.target);
      }

      // 记录子->父关系（用于判断是否有多个父节点，也去重）
      if (!childToParents.has(edge.target)) {
        childToParents.set(edge.target, []);
      }
      const parents = childToParents.get(edge.target);
      if (!parents.includes(edge.source)) {
        parents.push(edge.source);
      }
    });

    // 递归构建树
    const buildSubtree = (nodeId, pathKey, visitedInPath) => {
      // 避免循环引用：如果当前路径中已经访问过这个节点，跳过
      if (visitedInPath.has(nodeId)) {
        return null;
      }

      const treeNode = createTreeNode(nodeId, pathKey);
      if (!treeNode) return null;

      const newVisitedInPath = new Set(visitedInPath);
      newVisitedInPath.add(nodeId);

      const children = parentToChildren.get(nodeId) || [];
      treeNode.children = children
        .map((childId, index) => buildSubtree(childId, pathKey + '-' + childId + '-' + index, newVisitedInPath))
        .filter(child => child !== null);

      return treeNode;
    };

    // 找出所有根节点（没有入边的节点）
    const hasParent = new Set();
    initialEdges.forEach(edge => {
      hasParent.add(edge.target);
    });

    const roots = [];
    initialNodes.forEach((node, index) => {
      if (!hasParent.has(node.id)) {
        const treeNode = buildSubtree(node.id, 'root-' + node.id + '-' + index, new Set());
        if (treeNode) roots.push(treeNode);
      }
    });

    return roots;
  }, [initialNodes, initialEdges]);

  const treeRoots = useMemo(() => buildTree(), [buildTree]);

  // 初始化树节点折叠状态
  useEffect(() => {
    const getAllTreeKeys = (nodes) => {
      const keys = new Set();
      const traverse = (node) => {
        if (node.children && node.children.length > 0) {
          // 排除根节点
          if (node.data.level !== 0) {
            keys.add(node.treeKey);
          }
          node.children.forEach(traverse);
        }
      };
      nodes.forEach(traverse);
      return keys;
    };

    setCollapsedTreeNodes(getAllTreeKeys(treeRoots));
  }, [treeRoots]);

  const renderTree = useCallback((treeNodes, level = 0) => {
    return treeNodes.map(node => {
      // 使用 treeKey 来区分树中的不同实例
      const isCollapsed = collapsedTreeNodes.has(node.treeKey);
      const isSelected = selectedNodeId === node.id;

      return (
        <TreeNode
          key={node.treeKey}
          node={node}
          level={level}
          isCollapsed={isCollapsed}
          isSelected={isSelected}
          onToggle={() => {
            setCollapsedTreeNodes(prev => {
              const newSet = new Set(prev);
              if (newSet.has(node.treeKey)) {
                newSet.delete(node.treeKey);
              } else {
                newSet.add(node.treeKey);
              }
              return newSet;
            });
          }}
          onSelect={() => {
            const nodeId = node.id;

            // 展开所有祖先节点
            const ancestorIds = [nodeId];
            const findAncestors = (id) => {
              initialEdges.forEach(edge => {
                if (edge.target === id && !ancestorIds.includes(edge.source)) {
                  ancestorIds.push(edge.source);
                  findAncestors(edge.source);
                }
              });
            };
            findAncestors(nodeId);

            // 如果节点或其祖先被隐藏，先恢复它们
            setHiddenNodes(prev => {
              const newSet = new Set(prev);
              ancestorIds.forEach(id => newSet.delete(id));
              return newSet;
            });

            setSelectedNodeId(nodeId);

            // 获取选中节点的直接子节点
            const childrenIds = [];
            initialEdges.forEach(edge => {
              if (edge.source === nodeId) {
                childrenIds.push(edge.target);
              }
            });

            setCollapsedNodes(prev => {
              const newSet = new Set(prev);
              // 展开所有祖先节点
              ancestorIds.forEach(id => newSet.delete(id));
              // 展开选中节点本身
              newSet.delete(nodeId);
              // 展开选中节点的直接子节点
              childrenIds.forEach(id => newSet.delete(id));
              return newSet;
            });

            // 延迟聚焦，等待节点展开和布局重新计算
            setTimeout(() => {
              fitView({
                nodes: [{ id: nodeId }],
                duration: 800,
                padding: 0.3,
              });
            }, 300);
          }}
        >
          {node.children && node.children.length > 0 && renderTree(node.children, level + 1)}
        </TreeNode>
      );
    });
  }, [collapsedTreeNodes, selectedNodeId, nodes, setCenter, initialEdges, setCollapsedNodes, fitView]);

  console.log('📊 Rendering with', nodes.length, 'visible nodes and', edges.length, 'visible edges');

  if (nodes.length === 0) {
    return (
      <div style={{ padding: 50, color: 'red', fontSize: 20 }}>
        ERROR: No nodes to display!
      </div>
    );
  }

  return (
    <div style={{ width: '100vw', height: '100vh', background: '#f9fafb', display: 'flex', flexDirection: 'column' }}>
      {/* 顶部面包屑导航栏 */}
      <div style={{
        minHeight: '48px',
        maxHeight: '120px',
        background: 'white',
        borderBottom: '1px solid #e5e7eb',
        display: 'flex',
        alignItems: 'flex-start',
        padding: '12px 24px',
        zIndex: 1000,
        boxShadow: '0 1px 3px rgba(0, 0, 0, 0.05)',
        flexShrink: 0,
        overflowY: 'auto',
      }}>
        <div style={{ width: '100%' }}>
          {selectedNodeId ? (
            <div style={{ fontSize: '12px', color: '#6b7280' }}>
              {/* 面包屑导航 - 显示所有路径 */}
              {(() => {
                const selectedNode = nodes.find(n => n.id === selectedNodeId);
                if (!selectedNode) return null;

                // 找到所有从根节点到当前节点的路径
                const findAllPaths = (targetId) => {
                  const paths = [];

                  const buildPath = (nodeId, currentPath) => {
                    const node = initialNodes.find(n => n.id === nodeId);
                    if (!node) return;

                    const newPath = [node, ...currentPath];

                    // 找到所有父节点
                    const parents = initialEdges.filter(e => e.target === nodeId).map(e => e.source);

                    if (parents.length === 0) {
                      // 到达根节点
                      paths.push(newPath);
                    } else {
                      // 递归处理所有父节点
                      parents.forEach(parentId => {
                        buildPath(parentId, newPath);
                      });
                    }
                  };

                  buildPath(targetId, []);
                  return paths;
                };

                const allPaths = findAllPaths(selectedNodeId);

                // 去重：将路径转换为字符串进行比较
                const uniquePaths = [];
                const pathStrings = new Set();
                allPaths.forEach(path => {
                  const pathString = path.map(n => n.id).join('->');
                  if (!pathStrings.has(pathString)) {
                    pathStrings.add(pathString);
                    uniquePaths.push(path);
                  }
                });

                return (
                  <div style={{ display: 'flex', flexDirection: 'column', gap: '6px' }}>
                    {uniquePaths.map((path, pathIndex) => (
                      <div key={pathIndex} style={{ display: 'flex', alignItems: 'center', gap: '6px', flexWrap: 'wrap' }}>
                        {pathIndex > 0 && <span style={{ color: '#d1d5db', marginRight: '4px' }}>或</span>}
                        {path.map((node, index) => {
                          // 获取节点的 score、strategy 和 isSelected
                          const nodeScore = node.data.score ? parseFloat(node.data.score) : 0;
                          const nodeStrategy = node.data.strategy || '';
                          const strategyColor = getStrategyColor(nodeStrategy);
                          const nodeIsSelected = node.type === 'note' ? node.data.matchLevel !== 'unsatisfied' : node.data.isSelected !== false;

                          return (
                          <React.Fragment key={node.id + '-' + index}>
                            <span
                              onClick={() => {
                                const nodeId = node.id;

                                // 找到所有祖先节点
                                const ancestorIds = [nodeId];
                                const findAncestors = (id) => {
                                  initialEdges.forEach(edge => {
                                    if (edge.target === id && !ancestorIds.includes(edge.source)) {
                                      ancestorIds.push(edge.source);
                                      findAncestors(edge.source);
                                    }
                                  });
                                };
                                findAncestors(nodeId);

                                // 如果节点或其祖先被隐藏，先恢复它们
                                setHiddenNodes(prev => {
                                  const newSet = new Set(prev);
                                  ancestorIds.forEach(id => newSet.delete(id));
                                  return newSet;
                                });

                                // 展开目录树中到达该节点的路径
                                // 需要找到所有包含该节点的树路径的 treeKey，并展开它们的父节点
                                setCollapsedTreeNodes(prev => {
                                  const newSet = new Set(prev);
                                  // 清空所有折叠状态，让目录树完全展开到选中节点
                                  // 这样可以确保选中节点在目录中可见
                                  return new Set();
                                });

                                setSelectedNodeId(nodeId);
                                setTimeout(() => {
                                  fitView({
                                    nodes: [{ id: nodeId }],
                                    duration: 800,
                                    padding: 0.3,
                                  });
                                }, 100);
                              }}
                              style={{
                                padding: '6px 8px',
                                borderRadius: '4px',
                                background: 'white',
                                border: index === path.length - 1 ? '2px solid #3b82f6' : '1px solid #d1d5db',
                                color: '#374151',
                                fontWeight: index === path.length - 1 ? '600' : '400',
                                width: '180px',
                                cursor: 'pointer',
                                transition: 'all 0.2s ease',
                                position: 'relative',
                                display: 'inline-flex',
                                flexDirection: 'column',
                                gap: '4px',
                              }}
                              onMouseEnter={(e) => {
                                e.currentTarget.style.opacity = '0.8';
                              }}
                              onMouseLeave={(e) => {
                                e.currentTarget.style.opacity = '1';
                              }}
                              title={\`\${node.data.title || node.id} (Score: \${nodeScore.toFixed(2)}, Strategy: \${nodeStrategy}, Selected: \${nodeIsSelected})\`}
                            >
                              {/* 上半部分：竖线 + 图标 + 文字 + 分数 */}
                              <div style={{
                                display: 'flex',
                                alignItems: 'center',
                                gap: '6px',
                              }}>
                                {/* 策略类型竖线 */}
                                <div style={{
                                  width: '3px',
                                  height: '16px',
                                  background: strategyColor,
                                  borderRadius: '2px',
                                  flexShrink: 0,
                                }} />

                                {/* 节点类型图标 */}
                                <span style={{
                                  fontSize: '11px',
                                  flexShrink: 0,
                                }}>
                                  {node.type === 'note' ? '📝' : '🔍'}
                                </span>

                                {/* 节点文字 */}
                                <span style={{
                                  flex: 1,
                                  fontSize: '12px',
                                  color: nodeIsSelected ? '#374151' : '#ef4444',
                                }}>
                                  {truncateMiddle(node.data.title || node.id, 18)}
                                </span>

                                {/* 分数显示 */}
                                <span style={{
                                  fontSize: '10px',
                                  color: '#6b7280',
                                  fontWeight: '500',
                                  flexShrink: 0,
                                }}>
                                  {nodeScore.toFixed(2)}
                                </span>
                              </div>

                              {/* 分数下划线 */}
                              <div style={{
                                width: (nodeScore * 100) + '%',
                                height: '2px',
                                background: getScoreColor(nodeScore),
                                borderRadius: '1px',
                                marginLeft: '9px',
                              }} />
                            </span>
                            {index < path.length - 1 && <span style={{ color: '#9ca3af' }}>›</span>}
                          </React.Fragment>
                        )})}
                      </div>
                    ))}
                  </div>
                );
              })()}
            </div>
          ) : (
            <div style={{ fontSize: '13px', color: '#9ca3af', textAlign: 'center' }}>
              选择一个节点查看路径
            </div>
          )}
        </div>
      </div>

      {/* 主内容区：目录 + 画布 */}
      <div style={{ display: 'flex', flex: 1, overflow: 'hidden' }}>
        {/* 左侧目录树 */}
        <div style={{
          width: '320px',
          background: 'white',
          borderRight: '1px solid #e5e7eb',
          display: 'flex',
          flexDirection: 'column',
          flexShrink: 0,
        }}>
          <div style={{
            padding: '12px 16px',
            borderBottom: '1px solid #e5e7eb',
            display: 'flex',
            justifyContent: 'space-between',
            alignItems: 'center',
          }}>
            <span style={{
              fontWeight: '600',
              fontSize: '14px',
              color: '#111827',
            }}>
              节点目录
            </span>
            <div style={{ display: 'flex', gap: '6px' }}>
              <button
                onClick={() => {
                  setCollapsedTreeNodes(new Set());
                }}
                style={{
                  fontSize: '11px',
                  padding: '4px 8px',
                  borderRadius: '4px',
                  border: '1px solid #d1d5db',
                  background: 'white',
                  color: '#6b7280',
                  cursor: 'pointer',
                  fontWeight: '500',
                }}
                title="展开全部节点"
              >
                全部展开
              </button>
              <button
                onClick={() => {
                  const getAllTreeKeys = (nodes) => {
                    const keys = new Set();
                    const traverse = (node) => {
                      if (node.children && node.children.length > 0) {
                        keys.add(node.treeKey);
                        node.children.forEach(traverse);
                      }
                    };
                    nodes.forEach(traverse);
                    return keys;
                  };
                  setCollapsedTreeNodes(getAllTreeKeys(treeRoots));
                }}
                style={{
                  fontSize: '11px',
                  padding: '4px 8px',
                  borderRadius: '4px',
                  border: '1px solid #d1d5db',
                  background: 'white',
                  color: '#6b7280',
                  cursor: 'pointer',
                  fontWeight: '500',
                }}
                title="折叠全部节点"
              >
                全部折叠
              </button>
            </div>
          </div>
          <div style={{
            flex: 1,
            overflowX: 'auto',
            overflowY: 'auto',
            padding: '8px',
          }}>
            <div style={{ minWidth: 'fit-content' }}>
              {renderTree(treeRoots)}
            </div>
          </div>
        </div>

        {/* 画布区域 */}
        <div style={{ flex: 1, position: 'relative' }}>

          {/* 右侧图例 */}
          <div style={{
            position: 'absolute',
            top: '20px',
            right: '20px',
            background: 'white',
            padding: '16px',
            borderRadius: '12px',
            boxShadow: '0 4px 12px rgba(0, 0, 0, 0.08)',
            zIndex: 1000,
            maxWidth: '260px',
            border: '1px solid #e5e7eb',
          }}>
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '12px' }}>
          <h3 style={{ fontSize: '14px', fontWeight: '600', color: '#111827', margin: 0 }}>图例</h3>
          <button
            onClick={() => setFocusMode(!focusMode)}
            style={{
              fontSize: '11px',
              padding: '4px 8px',
              borderRadius: '4px',
              border: '1px solid',
              borderColor: focusMode ? '#3b82f6' : '#d1d5db',
              background: focusMode ? '#3b82f6' : 'white',
              color: focusMode ? 'white' : '#6b7280',
              cursor: 'pointer',
              fontWeight: '500',
            }}
            title={focusMode ? '关闭聚焦模式' : '开启聚焦模式'}
          >
            {focusMode ? '🎯 聚焦' : '📊 全图'}
          </button>
        </div>

        <div style={{ fontSize: '12px' }}>
          {/* 画布节点展开/折叠控制 */}
          <div style={{ marginBottom: '12px', paddingBottom: '12px', borderBottom: '1px solid #f3f4f6' }}>
            <div style={{ fontSize: '12px', fontWeight: '500', marginBottom: '8px', color: '#374151' }}>节点控制</div>
            <div style={{ display: 'flex', gap: '6px' }}>
              <button
                onClick={() => {
                  setCollapsedNodes(new Set());
                }}
                style={{
                  fontSize: '11px',
                  padding: '4px 8px',
                  borderRadius: '4px',
                  border: '1px solid #d1d5db',
                  background: 'white',
                  color: '#6b7280',
                  cursor: 'pointer',
                  fontWeight: '500',
                  flex: 1,
                }}
                title="展开画布中所有节点的子节点"
              >
                全部展开
              </button>
              <button
                onClick={() => {
                  const allNodeIds = new Set(initialNodes.map(n => n.id));
                  setCollapsedNodes(allNodeIds);
                }}
                style={{
                  fontSize: '11px',
                  padding: '4px 8px',
                  borderRadius: '4px',
                  border: '1px solid #d1d5db',
                  background: 'white',
                  color: '#6b7280',
                  cursor: 'pointer',
                  fontWeight: '500',
                  flex: 1,
                }}
                title="折叠画布中所有节点的子节点"
              >
                全部折叠
              </button>
            </div>
          </div>

          <div style={{ paddingTop: '12px', borderTop: '1px solid #f3f4f6' }}>
            <div style={{ fontSize: '12px', fontWeight: '500', marginBottom: '8px', color: '#374151' }}>策略类型</div>
            <div style={{ display: 'flex', alignItems: 'center', margin: '6px 0' }}>
              <div style={{ width: '20px', height: '2px', marginRight: '8px', background: '#10b981', opacity: 0.7 }}></div>
              <span style={{ color: '#6b7280', fontSize: '11px' }}>初始分词</span>
            </div>
            <div style={{ display: 'flex', alignItems: 'center', margin: '6px 0' }}>
              <div style={{ width: '20px', height: '2px', marginRight: '8px', background: '#06b6d4', opacity: 0.7 }}></div>
              <span style={{ color: '#6b7280', fontSize: '11px' }}>调用sug</span>
            </div>
            <div style={{ display: 'flex', alignItems: 'center', margin: '6px 0' }}>
              <div style={{ width: '20px', height: '2px', marginRight: '8px', background: '#f59e0b', opacity: 0.7 }}></div>
              <span style={{ color: '#6b7280', fontSize: '11px' }}>同义改写</span>
            </div>
            <div style={{ display: 'flex', alignItems: 'center', margin: '6px 0' }}>
              <div style={{ width: '20px', height: '2px', marginRight: '8px', background: '#3b82f6', opacity: 0.7 }}></div>
              <span style={{ color: '#6b7280', fontSize: '11px' }}>加词</span>
            </div>
            <div style={{ display: 'flex', alignItems: 'center', margin: '6px 0' }}>
              <div style={{ width: '20px', height: '2px', marginRight: '8px', background: '#8b5cf6', opacity: 0.7 }}></div>
              <span style={{ color: '#6b7280', fontSize: '11px' }}>抽象改写</span>
            </div>
            <div style={{ display: 'flex', alignItems: 'center', margin: '6px 0' }}>
              <div style={{ width: '20px', height: '2px', marginRight: '8px', background: '#ec4899', opacity: 0.7 }}></div>
              <span style={{ color: '#6b7280', fontSize: '11px' }}>基于部分匹配改进</span>
            </div>
            <div style={{ display: 'flex', alignItems: 'center', margin: '6px 0' }}>
              <div style={{ width: '20px', height: '2px', marginRight: '8px', background: '#a855f7', opacity: 0.7 }}></div>
              <span style={{ color: '#6b7280', fontSize: '11px' }}>结果分支-抽象改写</span>
            </div>
            <div style={{ display: 'flex', alignItems: 'center', margin: '6px 0' }}>
              <div style={{ width: '20px', height: '2px', marginRight: '8px', background: '#fb923c', opacity: 0.7 }}></div>
              <span style={{ color: '#6b7280', fontSize: '11px' }}>结果分支-同义改写</span>
            </div>
          </div>

          <div style={{
            marginTop: '12px',
            paddingTop: '12px',
            borderTop: '1px solid #f3f4f6',
            fontSize: '11px',
            color: '#9ca3af',
            lineHeight: '1.5',
          }}>
            💡 点击节点左上角 × 隐藏节点
          </div>

          {/* 隐藏节点列表 - 在图例内部 */}
          {hiddenNodes.size > 0 && (
            <div style={{
              marginTop: '12px',
              paddingTop: '12px',
              borderTop: '1px solid #f3f4f6',
            }}>
              <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '8px' }}>
                <h4 style={{ fontSize: '12px', fontWeight: '600', color: '#111827' }}>已隐藏节点</h4>
                <button
                  onClick={() => setHiddenNodes(new Set())}
                  style={{
                    fontSize: '10px',
                    color: '#3b82f6',
                    background: 'none',
                    border: 'none',
                    cursor: 'pointer',
                    textDecoration: 'underline',
                  }}
                >
                  全部恢复
                </button>
              </div>
              <div style={{ fontSize: '12px', maxHeight: '200px', overflow: 'auto' }}>
                {Array.from(hiddenNodes).map(nodeId => {
                  const node = initialNodes.find(n => n.id === nodeId);
                  if (!node) return null;
                  return (
                    <div
                      key={nodeId}
                      style={{
                        display: 'flex',
                        justifyContent: 'space-between',
                        alignItems: 'center',
                        padding: '6px 8px',
                        margin: '4px 0',
                        background: '#f9fafb',
                        borderRadius: '6px',
                        fontSize: '11px',
                      }}
                    >
                      <span
                        style={{
                          flex: 1,
                          overflow: 'hidden',
                          textOverflow: 'ellipsis',
                          whiteSpace: 'nowrap',
                          color: '#374151',
                        }}
                        title={node.data.title || nodeId}
                      >
                        {node.data.title || nodeId}
                      </span>
                      <button
                        onClick={() => {
                          setHiddenNodes(prev => {
                            const newSet = new Set(prev);
                            newSet.delete(nodeId);
                            return newSet;
                          });
                        }}
                        style={{
                          marginLeft: '8px',
                          fontSize: '10px',
                          color: '#10b981',
                          background: 'none',
                          border: 'none',
                          cursor: 'pointer',
                          flexShrink: 0,
                        }}
                      >
                        恢复
                      </button>
                    </div>
                  );
                })}
              </div>
            </div>
          )}
          </div>
          </div>

          {/* React Flow 画布 */}
          <ReactFlow
            nodes={nodes}
            edges={edges}
            nodeTypes={nodeTypes}
            fitView
            fitViewOptions={{ padding: 0.2, duration: 500 }}
            minZoom={0.1}
            maxZoom={1.5}
            nodesDraggable={true}
            nodesConnectable={false}
            elementsSelectable={true}
            defaultEdgeOptions={{
              type: 'smoothstep',
            }}
            proOptions={{ hideAttribution: true }}
            onNodeClick={(event, clickedNode) => {
              setSelectedNodeId(clickedNode.id);
            }}
          >
            <Controls style={{ bottom: '20px', left: 'auto', right: '20px' }} />
            <Background variant="dots" gap={20} size={1} color="#e5e7eb" />
          </ReactFlow>
        </div>
      </div>
    </div>
  );
}

function App() {
  return (
    <ReactFlowProvider>
      <FlowContent />
    </ReactFlowProvider>
  );
}

const root = createRoot(document.getElementById('root'));
root.render(<App />);
`;

fs.writeFileSync(reactComponentPath, reactComponent);

// 使用 esbuild 打包
console.log('🎨 Building modern visualization...');

build({
  entryPoints: [reactComponentPath],
  bundle: true,
  outfile: path.join(__dirname, 'bundle_v2.js'),
  format: 'iife',
  loader: {
    '.css': 'css',
  },
  minify: false,
  sourcemap: 'inline',
  // 强制所有 React 引用指向同一个位置，避免多副本
  alias: {
    'react': path.join(__dirname, 'node_modules/react'),
    'react-dom': path.join(__dirname, 'node_modules/react-dom'),
    'react/jsx-runtime': path.join(__dirname, 'node_modules/react/jsx-runtime'),
    'react/jsx-dev-runtime': path.join(__dirname, 'node_modules/react/jsx-dev-runtime'),
  },
}).then(() => {
  // 读取打包后的 JS
  const bundleJs = fs.readFileSync(path.join(__dirname, 'bundle_v2.js'), 'utf-8');

  // 读取 CSS
  const cssPath = path.join(__dirname, 'node_modules/@xyflow/react/dist/style.css');
  const css = fs.readFileSync(cssPath, 'utf-8');

  // 生成最终 HTML
  const html = `<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>查询图可视化</title>
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap" rel="stylesheet">
    <script src="https://unpkg.com/dagre@0.8.5/dist/dagre.min.js"></script>
    <script>
      // 过滤特定的 React 警告
      const originalError = console.error;
      console.error = (...args) => {
        if (typeof args[0] === 'string' && args[0].includes('Each child in a list should have a unique "key" prop')) {
          return;
        }
        originalError.apply(console, args);
      };
    </script>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }
        body {
            font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
            overflow: hidden;
            -webkit-font-smoothing: antialiased;
            -moz-osx-font-smoothing: grayscale;
        }
        #root {
            width: 100vw;
            height: 100vh;
        }
        ${css}

        /* 自定义样式覆盖 */
        .react-flow__edge-path {
            stroke-linecap: round;
        }
        .react-flow__controls {
            box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
            border: 1px solid #e5e7eb;
            border-radius: 8px;
        }
        .react-flow__controls-button {
            border: none;
            border-bottom: 1px solid #e5e7eb;
        }
        .react-flow__controls-button:hover {
            background: #f9fafb;
        }
    </style>
</head>
<body>
    <div id="root"></div>
    <script>${bundleJs}</script>
</body>
</html>`;

  // 写入输出文件
  fs.writeFileSync(outputFile, html);

  // 清理临时文件
  fs.unlinkSync(reactComponentPath);
  fs.unlinkSync(path.join(__dirname, 'bundle_v2.js'));

  console.log('✅ Visualization generated: ' + outputFile);
  console.log('📊 Nodes: ' + Object.keys(data.nodes).length);
  console.log('🔗 Edges: ' + data.edges.length);
}).catch(error => {
  console.error('❌ Build error:', error);
  process.exit(1);
});
