import type { MenuProps } from 'antd';
import type { TFunction } from 'i18next';
import {
  PlusOutlined,
  MinusOutlined,
  EditOutlined,
  SwapOutlined,
  DownloadOutlined,
} from '@ant-design/icons';

// ─── 组件特有右键菜单操作注册表 ────────────────────────────────────────────────
//
// 每种组件类型可以注册自己特有的右键菜单操作（第 1 层）。
// 操作通过直接修改组件的 code 字符串来实现。

export interface ContextMenuAction {
  key: string;
  label: string | ((code: string) => string);
  icon?: React.ReactNode;
  /** 执行操作，返回修改后的 code */
  execute: (code: string) => string;
  /** 可选：是否禁用 */
  disabled?: (code: string) => boolean;
}

export type ContextMenuActionsFactory = (code: string) => MenuProps['items'];

/** 右键菜单 factory 的上下文，由 CanvasPage 提供 */
export interface MenuActionContext {
  /** 打开文本输入弹窗，用户确认后回调 onConfirm */
  showPrompt: (opts: {
    label: string;
    placeholder?: string;
    defaultValue?: string;
    onConfirm: (value: string) => void;
  }) => void;
}

// ─── NumCard 特有操作 ─────────────────────────────────────────────────────────

function numCardActions(
  code: string,
  updateCode: (newCode: string) => void,
  closeMenu: () => void,
  ctx?: MenuActionContext,
  t?: TFunction,
): MenuProps['items'] {
  const hasComparisons = /comparisons\s*=\s*\{/.test(code);
  const comparisonsHidden = /showComparisons\s*=\s*\{false\}/.test(code);
  const hasWithChartVariant = /variant\s*=\s*"withChart"/.test(code);
  const chartHidden = /showChart\s*=\s*\{false\}/.test(code);
  const isWithChart = hasWithChartVariant && !chartHidden;
  // 如果有 comparisons 数据且没隐藏，或者没数据，都算"当前显示"状态用于 label
  const comparisonsVisible = hasComparisons && !comparisonsHidden;

  return [
    ...chartTitleActions(code, updateCode, closeMenu, ctx, t)!,
    {
      key: 'toggle-comparisons',
      icon: comparisonsVisible ? <MinusOutlined /> : <PlusOutlined />,
      danger: comparisonsVisible ? true : undefined,
      label: comparisonsVisible
        ? (t?.('canvas.menu.removeComparisons') ?? '隐藏环比数据')
        : (t?.('canvas.menu.addComparisons') ?? '显示环比/对比数据'),
      onClick: () => {
        let newCode = code;
        if (comparisonsVisible) {
          // 隐藏：注入 showComparisons={false}，不删数据
          if (/showComparisons\s*=/.test(newCode)) {
            newCode = newCode.replace(
              /showComparisons\s*=\s*\{true\}/,
              'showComparisons={false}',
            );
          } else {
            if (/testId\s*=/.test(newCode)) {
              newCode = newCode.replace(
                /([ \t]*)(testId\s*=)/,
                `$1showComparisons={false}\n$1$2`,
              );
            } else {
              newCode = newCode.replace(/\/>/, `  showComparisons={false}\n/>`);
            }
          }
        } else if (hasComparisons && comparisonsHidden) {
          // 有数据但被隐藏了 → 显示：移除 showComparisons={false}
          newCode = newCode.replace(/\s*showComparisons\s*=\s*\{false\}/, '');
        } else {
          // 没有 comparisons 数据 → 注入默认模板
          const compTemplate = `comparisons={[\n    { label: "比昨日", value: "0%", isUp: true },\n    { label: "比上月", value: "0%", isUp: false },\n  ]}`;
          if (/testId\s*=/.test(newCode)) {
            newCode = newCode.replace(
              /([ \t]*)(testId\s*=)/,
              `$1${compTemplate}\n$1$2`,
            );
          } else {
            newCode = newCode.replace(/\/>/, `  ${compTemplate}\n/>`);
          }
        }
        updateCode(newCode);
        closeMenu();
      },
    },
    {
      key: 'toggle-chart',
      icon: isWithChart ? <MinusOutlined /> : <PlusOutlined />,
      danger: isWithChart ? true : undefined,
      label: isWithChart
        ? (t?.('canvas.menu.removeChart') ?? '隐藏右侧折线图')
        : (t?.('canvas.menu.addChart') ?? '显示右侧折线图'),
      onClick: () => {
        let newCode = code;
        if (isWithChart) {
          // 隐藏折线图：注入 showChart={false}，切 variant 回 default（恢复数字大小）
          if (/showChart\s*=/.test(newCode)) {
            newCode = newCode.replace(
              /showChart\s*=\s*\{true\}/,
              'showChart={false}',
            );
          } else {
            if (/testId\s*=/.test(newCode)) {
              newCode = newCode.replace(
                /([ \t]*)(testId\s*=)/,
                `$1showChart={false}\n$1$2`,
              );
            } else {
              newCode = newCode.replace(/\/>/, `  showChart={false}\n/>`);
            }
          }
          // 切 variant 回 default，避免数字保持 withChart 的小字体
          newCode = newCode.replace(
            /variant\s*=\s*"withChart"/,
            'variant="default"',
          );
        } else {
          // 显示折线图
          const chartHidden = /showChart\s*=\s*\{false\}/.test(newCode);
          if (chartHidden) {
            // 有数据但被隐藏了 → 移除 showChart={false}，切 variant 回 withChart
            newCode = newCode.replace(/\s*showChart\s*=\s*\{false\}/, '');
            if (/variant\s*=\s*"[^"]*"/.test(newCode)) {
              newCode = newCode.replace(
                /variant\s*=\s*"[^"]*"/,
                'variant="withChart"',
              );
            } else {
              newCode = newCode.replace(
                /(<NumCard)/,
                '$1\n  variant="withChart"',
              );
            }
          } else {
            // 从没有 chart → 切 variant + 注入默认数据
            if (/variant\s*=\s*"[^"]*"/.test(newCode)) {
              newCode = newCode.replace(
                /variant\s*=\s*"[^"]*"/,
                'variant="withChart"',
              );
            } else {
              newCode = newCode.replace(
                /(<NumCard)/,
                '$1\n  variant="withChart"',
              );
            }
            // 只有没有 chartData 时才注入默认模板
            if (!/chartData\s*=/.test(newCode)) {
              const chartTemplate = `chartData={[\n    { name: "W1", value: 0 },\n    { name: "W2", value: 0 },\n    { name: "W3", value: 0 },\n    { name: "W4", value: 0 },\n  ]}\n  chartTestId="canvas-numcard-chart"`;
              if (/testId\s*=/.test(newCode)) {
                newCode = newCode.replace(
                  /([ \t]*)(testId\s*=)/,
                  `$1${chartTemplate}\n$1$2`,
                );
              } else {
                newCode = newCode.replace(/\/>/, `  ${chartTemplate}\n/>`);
              }
            }
          }
        }
        updateCode(newCode);
        closeMenu();
      },
    },
    // 仅在折线图可见时显示坐标轴选项
    ...(isWithChart
      ? [
          {
            key: 'toggle-axis',
            icon: /showAxis\s*=\s*\{true\}/.test(code) ? (
              <MinusOutlined />
            ) : (
              <PlusOutlined />
            ),
            danger: /showAxis\s*=\s*\{true\}/.test(code) ? true : undefined,
            label: /showAxis\s*=\s*\{true\}/.test(code)
              ? (t?.('canvas.menu.hideAxis') ?? '隐藏坐标轴')
              : (t?.('canvas.menu.showAxis') ?? '显示坐标轴'),
            onClick: () => {
              let newCode = code;
              const hasAxis = /showAxis\s*=\s*\{true\}/.test(newCode);
              if (hasAxis) {
                newCode = newCode.replace(/\s*showAxis\s*=\s*\{true\}/, '');
              } else {
                if (/testId\s*=/.test(newCode)) {
                  newCode = newCode.replace(
                    /([ \t]*)(testId\s*=)/,
                    `$1showAxis={true}\n$1$2`,
                  );
                } else {
                  newCode = newCode.replace(/\/>/, `  showAxis={true}\n/>`);
                }
              }
              updateCode(newCode);
              closeMenu();
            },
          },
        ]
      : []),
  ];
}

// ─── Table 特有操作 ──────────────────────────────────────────────────────────

const PAGE_SIZE_OPTIONS = [10, 20, 50, 100];

function tableActions(
  code: string,
  updateCode: (newCode: string) => void,
  closeMenu: () => void,
  ctx?: MenuActionContext,
  t?: TFunction,
): MenuProps['items'] {
  const hasPagination = /pagination\s*=\s*\{\{/.test(code);

  const downloadItems = chartDownloadActions(code, updateCode, closeMenu, t)!;

  if (hasPagination) {
    // 已有分页：显示"修改分页数量"子菜单 + "移除分页"
    const currentPageSize = code.match(/pageSize\s*:\s*(\d+)/)?.[1] ?? '10';

    return [
      {
        key: 'change-page-size',
        icon: <PlusOutlined />,
        label: t?.('canvas.menu.changePageSize') ?? '修改分页数量',
        children: PAGE_SIZE_OPTIONS.map((size) => ({
          key: `page-size-${size}`,
          label: `${size} 条/页${String(size) === currentPageSize ? ' ✓' : ''}`,
          onClick: () => {
            const newCode = code.replace(
              /pageSize\s*:\s*\d+/,
              `pageSize: ${size}`,
            );
            updateCode(newCode);
            closeMenu();
          },
        })),
      },
      {
        key: 'remove-pagination',
        icon: <MinusOutlined />,
        label: t?.('canvas.menu.removePagination') ?? '移除分页',
        danger: true,
        onClick: () => {
          let newCode = code;
          newCode = newCode.replace(/\s*pagination\s*=\s*\{\{[\s\S]*?\}\}/, '');
          newCode = newCode.replace(/\s*entityName\s*=\s*"[^"]*"/, '');
          updateCode(newCode);
          closeMenu();
        },
      },
      ...downloadItems,
    ];
  }

  // 无分页：显示"添加分页"子菜单，选择 pageSize
  return [
    {
      key: 'add-pagination',
      icon: <PlusOutlined />,
      label: t?.('canvas.menu.addPagination') ?? '添加分页',
      children: PAGE_SIZE_OPTIONS.map((size) => ({
        key: `add-page-size-${size}`,
        label: `${size} 条/页`,
        onClick: () => {
          let newCode = code;
          const paginationTemplate = `pagination={{ current: 1, pageSize: ${size}, total: 0 }}`;
          if (/testId\s*=/.test(newCode)) {
            newCode = newCode.replace(
              /([ \t]*)(testId\s*=)/,
              `$1${paginationTemplate}\n$1$2`,
            );
          } else {
            newCode = newCode.replace(/\/>/, `  ${paginationTemplate}\n/>`);
          }
          updateCode(newCode);
          closeMenu();
        },
      })),
    },
    ...downloadItems,
  ];
}

// ─── 通用 Chart 操作：图例显隐 ─────────────────────────────────────────────────

function chartLegendActions(
  code: string,
  updateCode: (newCode: string) => void,
  closeMenu: () => void,
  t?: TFunction,
): MenuProps['items'] {
  const hasLegendShow = /legendShow\s*=\s*\{/.test(code);
  const legendIsHidden = /legendShow\s*=\s*\{false\}/.test(code);

  // 当前状态：没有显式设置 legendShow 或 legendShow={true} → 图例可见
  const isVisible = !hasLegendShow || !legendIsHidden;

  return [
    {
      key: 'toggle-legend',
      icon: isVisible ? <MinusOutlined /> : <PlusOutlined />,
      label: isVisible
        ? (t?.('canvas.menu.hideLegend') ?? '隐藏图例')
        : (t?.('canvas.menu.showLegend') ?? '显示图例'),
      danger: isVisible ? true : undefined,
      onClick: () => {
        let newCode = code;
        if (isVisible) {
          // 当前可见 → 隐藏
          if (hasLegendShow) {
            newCode = newCode.replace(
              /legendShow\s*=\s*\{true\}/,
              'legendShow={false}',
            );
          } else {
            // 注入 legendShow={false}
            if (/testId\s*=/.test(newCode)) {
              newCode = newCode.replace(
                /([ \t]*)(testId\s*=)/,
                `$1legendShow={false}\n$1$2`,
              );
            } else {
              newCode = newCode.replace(/\/>/, `  legendShow={false}\n/>`);
            }
          }
        } else {
          // 当前隐藏 → 显示：将 legendShow={false} 替换为 legendShow={true}
          newCode = newCode.replace(
            /legendShow\s*=\s*\{false\}/,
            'legendShow={true}',
          );
        }
        updateCode(newCode);
        closeMenu();
      },
    },
  ];
}

// ─── 通用 Chart 操作：修改标题 ─────────────────────────────────────────────────

function chartTitleActions(
  code: string,
  updateCode: (newCode: string) => void,
  closeMenu: () => void,
  ctx?: MenuActionContext,
  t?: TFunction,
): MenuProps['items'] {
  const hasTitle = /title\s*=\s*"/.test(code);
  const currentTitle = code.match(/title\s*=\s*"([^"]*)"/)?.[1] ?? '';

  return [
    {
      key: 'edit-title',
      icon: <EditOutlined />,
      label: hasTitle
        ? (t?.('canvas.menu.editTitle') ?? '修改标题')
        : (t?.('canvas.menu.addTitle') ?? '添加标题'),
      onClick: () => {
        closeMenu();
        ctx?.showPrompt({
          label: hasTitle
            ? (t?.('canvas.menu.editChartTitle') ?? '修改图表标题')
            : (t?.('canvas.menu.addChartTitle') ?? '添加图表标题'),
          placeholder:
            t?.('canvas.menu.titlePlaceholder') ?? '请输入标题（留空则移除）',
          defaultValue: currentTitle,
          onConfirm: (newTitle) => {
            let newCode = code;
            if (newTitle === '') {
              // 移除标题
              newCode = newCode.replace(/\s*title\s*=\s*"[^"]*"/, '');
            } else if (hasTitle) {
              // 修改标题
              newCode = newCode.replace(
                /title\s*=\s*"[^"]*"/,
                `title="${newTitle}"`,
              );
            } else {
              // 注入标题
              if (/testId\s*=/.test(newCode)) {
                newCode = newCode.replace(
                  /([ \t]*)(testId\s*=)/,
                  `$1title="${newTitle}"\n$1$2`,
                );
              } else {
                newCode = newCode.replace(/\/>/, `  title="${newTitle}"\n/>`);
              }
            }
            updateCode(newCode);
          },
        });
      },
    },
  ];
}

// ─── 通用 Chart 操作：显示/隐藏下载按钮 ───────────────────────────────────────

function chartDownloadActions(
  code: string,
  updateCode: (newCode: string) => void,
  closeMenu: () => void,
  t?: TFunction,
): MenuProps['items'] {
  const hasDownload = /showDownload\s*=\s*\{true\}/.test(code);

  return [
    {
      key: 'toggle-download',
      icon: <DownloadOutlined />,
      label: hasDownload
        ? (t?.('canvas.menu.hideDownload') ?? '隐藏下载按钮')
        : (t?.('canvas.menu.showDownload') ?? '显示下载按钮'),
      danger: hasDownload ? true : undefined,
      onClick: () => {
        let newCode = code;
        if (hasDownload) {
          newCode = newCode.replace(/\s*showDownload\s*=\s*\{true\}/, '');
        } else {
          if (/testId\s*=/.test(newCode)) {
            newCode = newCode.replace(
              /([ \t]*)(testId\s*=)/,
              `$1showDownload={true}\n$1$2`,
            );
          } else {
            newCode = newCode.replace(/\/>/, `  showDownload={true}\n/>`);
          }
        }
        updateCode(newCode);
        closeMenu();
      },
    },
  ];
}

// ─── BarChart 特有操作 ─────────────────────────────────────────────────────

function barChartActions(
  code: string,
  updateCode: (newCode: string) => void,
  closeMenu: () => void,
  ctx?: MenuActionContext,
  t?: TFunction,
): MenuProps['items'] {
  const isHorizontal = /direction\s*=\s*"horizontal"/.test(code);
  const gridIsHidden = /showGrid\s*=\s*\{false\}/.test(code);
  const labelIsHidden = /showLabel\s*=\s*\{false\}/.test(code);
  const isGroupBar = /variant\s*=\s*"groupBar"/.test(code);
  const isStacked = /stacked\s*=\s*\{true\}/.test(code);
  const isPercent = /percent\s*=\s*\{true\}/.test(code);

  return [
    // ── 多系列模式切换（仅 groupBar） ─────────────────────────────
    ...(isGroupBar
      ? [
          {
            key: 'toggle-stacked',
            icon: isStacked ? <MinusOutlined /> : <PlusOutlined />,
            label: isStacked
              ? (t?.('canvas.menu.disableStacked') ?? '关闭堆叠')
              : (t?.('canvas.menu.enableStacked') ?? '开启堆叠'),
            danger: isStacked ? true : undefined,
            onClick: () => {
              let newCode = code;
              if (isStacked) {
                // 关闭堆叠 → 同时移除 percent
                newCode = newCode.replace(/\s*stacked\s*=\s*\{true\}/, '');
                newCode = newCode.replace(/\s*percent\s*=\s*\{true\}/, '');
              } else {
                if (/testId\s*=/.test(newCode)) {
                  newCode = newCode.replace(
                    /([ \t]*)(testId\s*=)/,
                    `$1stacked={true}\n$1$2`,
                  );
                } else {
                  newCode = newCode.replace(/\/>/, `  stacked={true}\n/>`);
                }
              }
              updateCode(newCode);
              closeMenu();
            },
          },
          // percent 仅在 stacked 时显示
          ...(isStacked
            ? [
                {
                  key: 'toggle-percent',
                  icon: isPercent ? <MinusOutlined /> : <PlusOutlined />,
                  label: isPercent
                    ? (t?.('canvas.menu.disablePercent') ?? '关闭百分比')
                    : (t?.('canvas.menu.enablePercent') ?? '开启百分比'),
                  danger: isPercent ? true : undefined,
                  onClick: () => {
                    let newCode = code;
                    if (isPercent) {
                      newCode = newCode.replace(
                        /\s*percent\s*=\s*\{true\}/,
                        '',
                      );
                    } else {
                      if (/testId\s*=/.test(newCode)) {
                        newCode = newCode.replace(
                          /([ \t]*)(testId\s*=)/,
                          `$1percent={true}\n$1$2`,
                        );
                      } else {
                        newCode = newCode.replace(
                          /\/>/,
                          `  percent={true}\n/>`,
                        );
                      }
                    }
                    updateCode(newCode);
                    closeMenu();
                  },
                },
              ]
            : []),
        ]
      : []),
    {
      key: 'bar-direction',
      icon: <PlusOutlined />,
      label: t?.('canvas.menu.direction') ?? '方向',
      children: [
        {
          key: 'direction-vertical',
          label: `${t?.('canvas.menu.vertical') ?? '纵向'}${!isHorizontal ? ' ✓' : ''}`,
          onClick: () => {
            let newCode = code;
            // 移除 direction prop（vertical 是默认值，不需要显式写）
            newCode = newCode.replace(/\s*direction\s*=\s*"horizontal"/, '');
            updateCode(newCode);
            closeMenu();
          },
        },
        {
          key: 'direction-horizontal',
          label: `${t?.('canvas.menu.horizontal') ?? '横向'}${isHorizontal ? ' ✓' : ''}`,
          onClick: () => {
            let newCode = code;
            if (!isHorizontal) {
              // 注入 direction="horizontal" 到 testId 前
              if (/testId\s*=/.test(newCode)) {
                newCode = newCode.replace(
                  /([ \t]*)(testId\s*=)/,
                  `$1direction="horizontal"\n$1$2`,
                );
              } else {
                newCode = newCode.replace(
                  /\/>/,
                  `  direction="horizontal"\n/>`,
                );
              }
            }
            updateCode(newCode);
            closeMenu();
          },
        },
      ],
    },
    {
      key: 'toggle-grid',
      icon: gridIsHidden ? <PlusOutlined /> : <MinusOutlined />,
      label: gridIsHidden
        ? (t?.('canvas.menu.showGrid') ?? '显示网格')
        : (t?.('canvas.menu.hideGrid') ?? '隐藏网格'),
      danger: !gridIsHidden ? true : undefined,
      onClick: () => {
        let newCode = code;
        if (gridIsHidden) {
          newCode = newCode.replace(/\s*showGrid\s*=\s*\{false\}/, '');
        } else {
          if (/testId\s*=/.test(newCode)) {
            newCode = newCode.replace(
              /([ \t]*)(testId\s*=)/,
              `$1showGrid={false}\n$1$2`,
            );
          } else {
            newCode = newCode.replace(/\/>/, `  showGrid={false}\n/>`);
          }
        }
        updateCode(newCode);
        closeMenu();
      },
    },
    {
      key: 'toggle-label',
      icon: labelIsHidden ? <PlusOutlined /> : <MinusOutlined />,
      label: labelIsHidden
        ? (t?.('canvas.menu.showLabel') ?? '显示数据标签')
        : (t?.('canvas.menu.hideLabel') ?? '隐藏数据标签'),
      danger: !labelIsHidden ? true : undefined,
      onClick: () => {
        let newCode = code;
        if (labelIsHidden) {
          newCode = newCode.replace(/\s*showLabel\s*=\s*\{false\}/, '');
        } else {
          if (/testId\s*=/.test(newCode)) {
            newCode = newCode.replace(
              /([ \t]*)(testId\s*=)/,
              `$1showLabel={false}\n$1$2`,
            );
          } else {
            newCode = newCode.replace(/\/>/, `  showLabel={false}\n/>`);
          }
        }
        updateCode(newCode);
        closeMenu();
      },
    },
    ...chartLegendActions(code, updateCode, closeMenu, t)!,
    ...chartTitleActions(code, updateCode, closeMenu, ctx, t)!,
    ...chartDownloadActions(code, updateCode, closeMenu, t)!,
  ];
}

// ─── PieChart 特有操作 ─────────────────────────────────────────────────────

function pieChartActions(
  code: string,
  updateCode: (newCode: string) => void,
  closeMenu: () => void,
  ctx?: MenuActionContext,
  t?: TFunction,
): MenuProps['items'] {
  const currentVariant =
    code.match(/variant\s*=\s*"(pie|ring|sunburst)"/)?.[1] ?? 'pie';
  const isSunburst = currentVariant === 'sunburst';

  const items: MenuProps['items'] = [];

  // variant 切换（sunburst 不可切换）
  if (!isSunburst) {
    items.push({
      key: 'pie-variant',
      icon: <PlusOutlined />,
      label: t?.('canvas.menu.switchVariant') ?? '切换样式',
      children: [
        {
          key: 'variant-pie',
          label: `${t?.('canvas.menu.pie') ?? '饼图'}${currentVariant === 'pie' ? ' ✓' : ''}`,
          onClick: () => {
            const newCode = code.replace(
              /variant\s*=\s*"(pie|ring)"/,
              'variant="pie"',
            );
            updateCode(newCode);
            closeMenu();
          },
        },
        {
          key: 'variant-ring',
          label: `${t?.('canvas.menu.ring') ?? '环形图'}${currentVariant === 'ring' ? ' ✓' : ''}`,
          onClick: () => {
            const newCode = code.replace(
              /variant\s*=\s*"(pie|ring)"/,
              'variant="ring"',
            );
            updateCode(newCode);
            closeMenu();
          },
        },
      ],
    });

    // topN 显示项数（仅 pie/ring 生效）
    const topNMatch = code.match(/topN\s*=\s*\{(\d+)\}/);
    const currentTopN = topNMatch ? Number(topNMatch[1]) : 5; // 默认 5
    const TOP_N_OPTIONS = [3, 5, 8, 10, 0]; // 0 = 不限制

    items.push({
      key: 'pie-topn',
      icon: <PlusOutlined />,
      label: t?.('canvas.menu.topN') ?? '显示项数',
      children: TOP_N_OPTIONS.map((n) => ({
        key: `topn-${n}`,
        label: `${n === 0 ? '不限制' : `${n} 项`}${currentTopN === n ? ' ✓' : ''}`,
        onClick: () => {
          let newCode = code;
          if (topNMatch) {
            // 已有 topN → 替换
            newCode = newCode.replace(/topN\s*=\s*\{\d+\}/, `topN={${n}}`);
          } else {
            // 无 topN → 注入
            if (/testId\s*=/.test(newCode)) {
              newCode = newCode.replace(
                /([ \t]*)(testId\s*=)/,
                `$1topN={${n}}\n$1$2`,
              );
            } else {
              newCode = newCode.replace(/\/>/, `  topN={${n}}\n/>`);
            }
          }
          updateCode(newCode);
          closeMenu();
        },
      })),
    });
  }

  // 图例 + 标题（所有 chart 通用）
  items.push(...chartLegendActions(code, updateCode, closeMenu, t)!);
  items.push(...chartTitleActions(code, updateCode, closeMenu, ctx, t)!);
  items.push(...chartDownloadActions(code, updateCode, closeMenu, t)!);

  return items;
}

// ─── LineChart 特有操作 ─────────────────────────────────────────────────────

function lineChartActions(
  code: string,
  updateCode: (newCode: string) => void,
  closeMenu: () => void,
  ctx?: MenuActionContext,
  t?: TFunction,
): MenuProps['items'] {
  // ── showGrid toggle（默认 true，隐藏 = 注入 {false}）──
  const gridIsHidden = /showGrid\s*=\s*\{false\}/.test(code);

  // ── showDots toggle（默认 false，显示 = 注入 {true}）──
  const dotsIsShown = /showDots\s*=\s*\{true\}/.test(code);

  // ── showLabel toggle（默认 false，显示 = 注入 {true}，仅 showDots 时有意义）──
  const labelIsShown = /showLabel\s*=\s*\{true\}/.test(code);

  // ── stacked toggle（默认 false，开启 = 注入 {true}）──
  const isStacked = /stacked\s*=\s*\{true\}/.test(code);

  // ── area toggle（默认 false，开启 = 注入 {true}）──
  const isArea = /area\s*=\s*\{true\}/.test(code);

  // ── smooth toggle（默认 false，开启 = 注入 {true}）──
  const isSmooth =
    /smooth\s*=\s*\{true\}/.test(code) || /\bsmooth\b(?!\s*=)/.test(code);

  // ── showAxis toggle（默认 true，隐藏 = 注入 {false}）──
  const axisIsHidden = /showAxis\s*=\s*\{false\}/.test(code);

  return [
    ...chartLegendActions(code, updateCode, closeMenu, t)!,
    {
      key: 'toggle-grid',
      icon: gridIsHidden ? <PlusOutlined /> : <MinusOutlined />,
      label: gridIsHidden
        ? (t?.('canvas.menu.showGrid') ?? '显示网格')
        : (t?.('canvas.menu.hideGrid') ?? '隐藏网格'),
      danger: !gridIsHidden ? true : undefined,
      onClick: () => {
        let newCode = code;
        if (gridIsHidden) {
          // 当前隐藏 → 显示：移除 showGrid={false}
          newCode = newCode.replace(/\s*showGrid\s*=\s*\{false\}/, '');
        } else {
          // 当前显示 → 隐藏：注入 showGrid={false}
          if (/testId\s*=/.test(newCode)) {
            newCode = newCode.replace(
              /([ \t]*)(testId\s*=)/,
              `$1showGrid={false}\n$1$2`,
            );
          } else {
            newCode = newCode.replace(/\/>/, `  showGrid={false}\n/>`);
          }
        }
        updateCode(newCode);
        closeMenu();
      },
    },
    {
      key: 'toggle-axis',
      icon: axisIsHidden ? <PlusOutlined /> : <MinusOutlined />,
      label: axisIsHidden
        ? (t?.('canvas.menu.showAxis') ?? '显示坐标轴')
        : (t?.('canvas.menu.hideAxis') ?? '隐藏坐标轴'),
      danger: !axisIsHidden ? true : undefined,
      onClick: () => {
        let newCode = code;
        if (axisIsHidden) {
          // 当前隐藏 → 显示：移除 showAxis={false}
          newCode = newCode.replace(/\s*showAxis\s*=\s*\{false\}/, '');
        } else {
          // 当前显示 → 隐藏：注入 showAxis={false}
          if (/testId\s*=/.test(newCode)) {
            newCode = newCode.replace(
              /([ \t]*)(testId\s*=)/,
              `$1showAxis={false}\n$1$2`,
            );
          } else {
            newCode = newCode.replace(/\/>/, `  showAxis={false}\n/>`);
          }
        }
        updateCode(newCode);
        closeMenu();
      },
    },
    {
      key: 'toggle-dots',
      icon: dotsIsShown ? <MinusOutlined /> : <PlusOutlined />,
      label: dotsIsShown
        ? (t?.('canvas.menu.hideDots') ?? '隐藏坐标点')
        : (t?.('canvas.menu.showDots') ?? '显示坐标点'),
      danger: dotsIsShown ? true : undefined,
      onClick: () => {
        let newCode = code;
        if (dotsIsShown) {
          // 当前显示 → 隐藏：移除 showDots={true}
          newCode = newCode.replace(/\s*showDots\s*=\s*\{true\}/, '');
        } else {
          // 当前隐藏 → 显示：注入 showDots={true}
          if (/testId\s*=/.test(newCode)) {
            newCode = newCode.replace(
              /([ \t]*)(testId\s*=)/,
              `$1showDots={true}\n$1$2`,
            );
          } else {
            newCode = newCode.replace(/\/>/, `  showDots={true}\n/>`);
          }
        }
        updateCode(newCode);
        closeMenu();
      },
    },
    // showLabel 仅在 showDots 开启时显示
    ...(dotsIsShown
      ? [
          {
            key: 'toggle-label',
            icon: labelIsShown ? <MinusOutlined /> : <PlusOutlined />,
            label: labelIsShown
              ? (t?.('canvas.menu.hideDotsLabel') ?? '隐藏坐标点数据')
              : (t?.('canvas.menu.showDotsLabel') ?? '显示坐标点数据'),
            danger: labelIsShown ? true : undefined,
            onClick: () => {
              let newCode = code;
              if (labelIsShown) {
                // 隐藏：移除 showLabel={true}
                newCode = newCode.replace(/\s*showLabel\s*=\s*\{true\}/, '');
              } else {
                // 显示：注入 showLabel={true}
                if (/testId\s*=/.test(newCode)) {
                  newCode = newCode.replace(
                    /([ \t]*)(testId\s*=)/,
                    `$1showLabel={true}\n$1$2`,
                  );
                } else {
                  newCode = newCode.replace(/\/>/, `  showLabel={true}\n/>`);
                }
              }
              updateCode(newCode);
              closeMenu();
            },
          },
        ]
      : []),
    {
      key: 'toggle-stacked',
      icon: isStacked ? <MinusOutlined /> : <PlusOutlined />,
      label: isStacked
        ? (t?.('canvas.menu.disableStacked') ?? '关闭堆叠')
        : (t?.('canvas.menu.enableStacked') ?? '开启堆叠'),
      danger: isStacked ? true : undefined,
      onClick: () => {
        let newCode = code;
        if (isStacked) {
          // 关闭：移除 stacked={true}
          newCode = newCode.replace(/\s*stacked\s*=\s*\{true\}/, '');
        } else {
          // 开启：注入 stacked={true}
          if (/testId\s*=/.test(newCode)) {
            newCode = newCode.replace(
              /([ \t]*)(testId\s*=)/,
              `$1stacked={true}\n$1$2`,
            );
          } else {
            newCode = newCode.replace(/\/>/, `  stacked={true}\n/>`);
          }
        }
        updateCode(newCode);
        closeMenu();
      },
    },
    {
      key: 'toggle-area',
      icon: isArea ? <MinusOutlined /> : <PlusOutlined />,
      label: isArea
        ? (t?.('canvas.menu.disableArea') ?? '关闭面积')
        : (t?.('canvas.menu.enableArea') ?? '开启面积'),
      danger: isArea ? true : undefined,
      onClick: () => {
        let newCode = code;
        if (isArea) {
          // 关闭：移除 area={true}
          newCode = newCode.replace(/\s*area\s*=\s*\{true\}/, '');
        } else {
          // 开启：注入 area={true}
          if (/testId\s*=/.test(newCode)) {
            newCode = newCode.replace(
              /([ \t]*)(testId\s*=)/,
              `$1area={true}\n$1$2`,
            );
          } else {
            newCode = newCode.replace(/\/>/, `  area={true}\n/>`);
          }
        }
        updateCode(newCode);
        closeMenu();
      },
    },
    {
      key: 'toggle-smooth',
      icon: isSmooth ? <MinusOutlined /> : <PlusOutlined />,
      label: isSmooth
        ? (t?.('canvas.menu.disableSmooth') ?? '关闭拟合曲线')
        : (t?.('canvas.menu.enableSmooth') ?? '开启拟合曲线'),
      danger: isSmooth ? true : undefined,
      onClick: () => {
        let newCode = code;
        if (isSmooth) {
          // 关闭：移除 smooth={true} 或 smooth（布尔简写）
          newCode = newCode.replace(/\s*smooth\s*=\s*\{true\}/, '');
          newCode = newCode.replace(/\s*smooth\b(?!\s*=)/, '');
        } else {
          // 开启：注入 smooth={true}
          if (/testId\s*=/.test(newCode)) {
            newCode = newCode.replace(
              /([ \t]*)(testId\s*=)/,
              `$1smooth={true}\n$1$2`,
            );
          } else {
            newCode = newCode.replace(/\/>/, `  smooth={true}\n/>`);
          }
        }
        updateCode(newCode);
        closeMenu();
      },
    },
    ...chartTitleActions(code, updateCode, closeMenu, ctx, t)!,
    ...chartDownloadActions(code, updateCode, closeMenu, t)!,
  ];
}

// ─── RadarChart 特有操作 ───────────────────────────────────────────────────

function radarChartActions(
  code: string,
  updateCode: (newCode: string) => void,
  closeMenu: () => void,
  ctx?: MenuActionContext,
  t?: TFunction,
): MenuProps['items'] {
  return [
    ...chartLegendActions(code, updateCode, closeMenu, t)!,
    ...chartTitleActions(code, updateCode, closeMenu, ctx, t)!,
    ...chartDownloadActions(code, updateCode, closeMenu, t)!,
  ];
}

// ─── TreemapChart 特有操作 ─────────────────────────────────────────────────

function treemapChartActions(
  code: string,
  updateCode: (newCode: string) => void,
  closeMenu: () => void,
  ctx?: MenuActionContext,
  t?: TFunction,
): MenuProps['items'] {
  return [
    ...chartLegendActions(code, updateCode, closeMenu, t)!,
    ...chartTitleActions(code, updateCode, closeMenu, ctx, t)!,
    ...chartDownloadActions(code, updateCode, closeMenu, t)!,
  ];
}

// ─── MultiChart 特有操作 ──────────────────────────────────────────────────────

const MULTI_CHART_TYPES = [
  { key: 'bar', label: '柱状图', labelEn: 'Bar Chart' },
  { key: 'line', label: '折线图', labelEn: 'Line Chart' },
  { key: 'pie', label: '饼图', labelEn: 'Pie Chart' },
  { key: 'radar', label: '雷达图', labelEn: 'Radar Chart' },
  { key: 'treemap', label: '矩形树图', labelEn: 'Treemap' },
  { key: 'worldmap', label: '世界地图', labelEn: 'World Map' },
  { key: 'table', label: '表格', labelEn: 'Table' },
];

/** 从 code 中解析当前 defaultVariant */
function parseDefaultVariant(code: string): string {
  const match = code.match(/defaultVariant\s*=\s*"([^"]*)"/);
  return match?.[1] ?? 'bar';
}

/** 从 code 中解析 charts 列表 */
function parseChartsList(code: string): string[] {
  const match = code.match(/charts=\{(\[.*?\])\}/s);
  if (!match) return ['bar', 'line', 'pie'];
  try {
    return JSON.parse(match[1].replace(/'/g, '"'));
  } catch {
    return ['bar', 'line', 'pie'];
  }
}

/** 从 code 中解析 chartConfig JSON */
function parseChartConfig(code: string): Record<string, Record<string, any>> {
  const match = code.match(/chartConfig=\{([\s\S]*?)\}\s*(?=\w+=|\/?>)/);
  if (!match) return {};
  try {
    // chartConfig={{"bar":{"showGrid":false}}}
    return JSON.parse(match[1]);
  } catch {
    return {};
  }
}

/** 将 chartConfig 写回 code 字符串 */
function writeChartConfig(
  code: string,
  config: Record<string, Record<string, any>>,
): string {
  const configStr = `chartConfig={${JSON.stringify(config)}}`;
  if (/chartConfig=\{[\s\S]*?\}\s*(?=\w+=|\/?>)/.test(code)) {
    return code.replace(
      /chartConfig=\{[\s\S]*?\}\s*(?=\w+=|\/?>)/,
      configStr + '\n  ',
    );
  }
  // 注入到 testId 之前
  if (/testId\s*=/.test(code)) {
    return code.replace(/([ \t]*)(testId\s*=)/, `$1${configStr}\n$1$2`);
  }
  return code.replace(/\/>/, `  ${configStr}\n/>`);
}

/** 各子图表的隐式默认 props（右键菜单 regex 所依赖的） */
const CHART_IMPLICIT_DEFAULTS: Record<string, Record<string, any>> = {
  pie: { variant: 'pie' },
  bar: { variant: 'groupBar' },
  line: {},
  radar: {},
  treemap: {},
  worldmap: {},
  table: {},
};

function multiChartActions(
  code: string,
  updateCode: (newCode: string) => void,
  closeMenu: () => void,
  ctx?: MenuActionContext,
  t?: TFunction,
): MenuProps['items'] {
  const activeChart = parseDefaultVariant(code);
  const chartsList = parseChartsList(code);
  const chartConfig = parseChartConfig(code);
  const activeConfig = chartConfig[activeChart] ?? {};

  // ── 切换到 (二级菜单) ──
  const switchItems: MenuProps['items'] = chartsList.map((key) => {
    const meta = MULTI_CHART_TYPES.find((c) => c.key === key);
    const label = meta?.label ?? key;
    return {
      key: `switch-${key}`,
      label: `${label}${key === activeChart ? ' ✓' : ''}`,
      onClick: () => {
        let newCode = code;
        if (/defaultVariant\s*=\s*"[^"]*"/.test(newCode)) {
          newCode = newCode.replace(
            /defaultVariant\s*=\s*"[^"]*"/,
            `defaultVariant="${key}"`,
          );
        } else {
          if (/testId\s*=/.test(newCode)) {
            newCode = newCode.replace(
              /([ \t]*)(testId\s*=)/,
              `$1defaultVariant="${key}"\n$1$2`,
            );
          } else {
            newCode = newCode.replace(/\/>/, `  defaultVariant="${key}"\n/>`);
          }
        }
        updateCode(newCode);
        closeMenu();
      },
    };
  });

  // ── 当前图表的配置项 ──
  // 构建一个"虚拟 code"传给 chart actions，让它们能正确读取/修改状态
  // chart actions 修改虚拟 code 后，我们提取差异写入 chartConfig
  const chartActionsMap: Record<string, ComponentActionsFactory | undefined> = {
    bar: barChartActions,
    line: lineChartActions,
    pie: pieChartActions,
    radar: radarChartActions,
    treemap: treemapChartActions,
    table: tableActions,
  };

  const currentChartFactory = chartActionsMap[activeChart];
  let chartSpecificItems: MenuProps['items'] = [];

  if (currentChartFactory) {
    // 构建虚拟 code：合并隐式默认 + 用户显式配置，转为 JSX props
    const implicitDefaults = CHART_IMPLICIT_DEFAULTS[activeChart] ?? {};
    const effectiveConfig = { ...implicitDefaults, ...activeConfig };
    let virtualCode = `<Component\n`;
    for (const [k, v] of Object.entries(effectiveConfig)) {
      if (typeof v === 'boolean') {
        virtualCode += `  ${k}={${v}}\n`;
      } else if (typeof v === 'string') {
        virtualCode += `  ${k}="${v}"\n`;
      }
    }
    virtualCode += `  testId="virtual"\n/>`;

    const virtualUpdateCode = (newVirtualCode: string) => {
      // 从 newVirtualCode 提取所有 props
      const newConfig: Record<string, any> = {};
      // 解析 boolean props: xxx={true} / xxx={false}
      const boolMatches = newVirtualCode.matchAll(
        /(\w+)\s*=\s*\{(true|false)\}/g,
      );
      for (const m of boolMatches) {
        if (m[1] !== 'testId') newConfig[m[1]] = m[2] === 'true';
      }
      // 解析 string props: xxx="yyy"
      const strMatches = newVirtualCode.matchAll(/(\w+)\s*=\s*"([^"]*)"/g);
      for (const m of strMatches) {
        if (m[1] !== 'testId') newConfig[m[1]] = m[2];
      }

      // 更新 chartConfig
      const updatedConfig = { ...chartConfig, [activeChart]: newConfig };
      // 清理空对象
      if (Object.keys(updatedConfig[activeChart]).length === 0) {
        delete updatedConfig[activeChart];
      }
      const newCode = writeChartConfig(code, updatedConfig);
      updateCode(newCode);
    };

    chartSpecificItems = currentChartFactory(
      virtualCode,
      virtualUpdateCode,
      closeMenu,
      ctx,
      t,
    );
  }

  return [
    {
      key: 'switch-chart',
      icon: <SwapOutlined />,
      label: t?.('canvas.menu.switchTo') ?? '切换到',
      children: switchItems,
    },
    ...(chartSpecificItems && chartSpecificItems.length > 0
      ? [
          { type: 'divider' as const, key: 'div-chart-config' },
          ...chartSpecificItems,
        ]
      : []),
  ];
}

// ─── 注册表 ───────────────────────────────────────────────────────────────────

type ComponentActionsFactory = (
  code: string,
  updateCode: (newCode: string) => void,
  closeMenu: () => void,
  ctx?: MenuActionContext,
  t?: TFunction,
) => MenuProps['items'];

export const CANVAS_CONTEXT_MENU_REGISTRY: Record<
  string,
  ComponentActionsFactory
> = {
  NumCard: numCardActions,
  Table: tableActions,
  BarChart: barChartActions,
  PieChart: pieChartActions,
  LineChart: lineChartActions,
  RadarChart: radarChartActions,
  TreemapChart: treemapChartActions,
  MultiChart: multiChartActions,
};
