import React, {
  useState,
  useCallback,
  useMemo,
  useRef,
  useEffect,
} from 'react';
import { GridLayout, useContainerWidth } from 'react-grid-layout';
import type { LayoutItem } from 'react-grid-layout';
import { Dropdown, Modal, Checkbox, Input, Button, Space } from 'antd';
import type { MenuProps } from 'antd';
import {
  PlusOutlined,
  DeleteOutlined,
  EditOutlined,
  CopyOutlined,
  SettingOutlined,
  PlusCircleOutlined,
  DatabaseOutlined,
  MinusCircleOutlined,
} from '@ant-design/icons';
import CanvasCell from './CanvasCell';
import CodeFloatWindow from './CodeFloatWindow';
import CanvasToolbar from './CanvasToolbar';
import { CANVAS_COMPONENTS, CANVAS_DEFAULTS } from './canvasDefaults';
import { CANVAS_CONFIG_REGISTRY } from './canvasConfigRegistry';
import { useAgent } from '@/components/agentPanel/AgentContext';
import { generatePrompt } from '@/components/agentPanel/promptGenerator';
import { CANVAS_CONTEXT_MENU_REGISTRY } from './canvasContextMenuRegistry';
import type { MenuActionContext } from './canvasContextMenuRegistry';
import type { CanvasConfigModalProps } from './canvasConfigRegistry';
import NumCardDataSourceModal from './components/NumCardDataSourceModal';
import TableDataSourceModal from './components/TableDataSourceModal';
import BarChartDataSourceModal from './components/BarChartDataSourceModal';
import LineChartDataSourceModal from './components/LineChartDataSourceModal';
import RadarChartDataSourceModal from './components/RadarChartDataSourceModal';
import MultiChartDataSourceModal from './components/MultiChartDataSourceModal';
import PromptModal from './components/PromptModal';
import { createSectionCompactor } from './sectionCompactor';
import type { CanvasItem as CanvasItemType } from './types';
import { useTranslation } from 'react-i18next';
import 'react-grid-layout/css/styles.css';
import 'react-resizable/css/styles.css';

// ─── ID 生成 ─────────────────────────────────────────────────────────────────

let _id = 0;
const genId = () => `canvas-${Date.now()}-${++_id}`;

// ─── Constants ───────────────────────────────────────────────────────────────

const COLS = 48;
const ROW_HEIGHT = 10;
const MARGIN_Y = 8;

/** 组件库类型 → 画布实际 componentType 映射 */
const COMPONENT_TYPE_MAP: Record<string, string> = {
  BarChart: 'MultiChart',
  LineChart: 'MultiChart',
  PieChart: 'MultiChart',
};

// ─── Helpers ─────────────────────────────────────────────────────────────────

/** Compute the pixel height a component should fill given a grid row count. */
function gridRowsToPx(h: number): number {
  return h * ROW_HEIGHT + (h - 1) * MARGIN_Y;
}

function syncCodeHeight(code: string, h: number): string {
  const px = Math.max(50, gridRowsToPx(h));

  // Table 组件用 height:100% 自适应外层容器，不需要注入固定高度
  if (/^<Table[\s\n]/.test(code.trim())) {
    return code;
  }

  // withChart variant 不需要固定高度（自适应外层容器）
  if (/variant\s*=\s*"withChart"/.test(code)) {
    let cleaned = code;
    // 移除可能残留的 contentHeight prop
    cleaned = cleaned.replace(/\s*contentHeight\s*=\s*\{[^}]*\}/, '');
    // 移除 style={{ height: X }} （固定高度会阻止自适应）
    cleaned = cleaned.replace(
      /\s*style\s*=\s*\{\{\s*height\s*:\s*\d+\s*\}\}/,
      '',
    );
    return cleaned;
  }

  // contentHeight={X} — NumLineCard inner chart area (subtract Card padding 32px)
  if (/contentHeight\s*=\s*\{[^}]*\}/.test(code)) {
    const inner = Math.max(40, px - 32);
    return code.replace(
      /contentHeight\s*=\s*\{[^}]*\}/g,
      `contentHeight={${inner}}`,
    );
  }
  if (/height\s*=\s*\{[^}]*\}/.test(code)) {
    return code.replace(/height\s*=\s*\{[^}]*\}/g, `height={${px}}`);
  }
  if (/style\s*=\s*\{\{[^}]*height\s*:/.test(code)) {
    return code.replace(/(height\s*:\s*)\d+/g, `$1${px}`);
  }
  if (/\/>\s*$/.test(code)) {
    return code.replace(/\/>\s*$/, `  style={{ height: ${px} }}\n/>`);
  }
  return code;
}

// ─── Props ───────────────────────────────────────────────────────────────────

interface CanvasPageProps {
  items: CanvasItemType[];
  layout: LayoutItem[];
  isDirty: boolean;
  canvasName: string;
  isPublished: boolean;
  onItemsChange: (items: CanvasItemType[]) => void;
  onLayoutChange: (layout: LayoutItem[]) => void;
  onBothChange: (items: CanvasItemType[], layout: LayoutItem[]) => void;
  onSave: () => void;
  onBack: () => void;
  onPublish: () => void;
  onUnpublish: () => void;
}

// ─── CanvasPage ────────────────────────────────────────────────────────────────

const CanvasPage: React.FC<CanvasPageProps> = ({
  items,
  layout,
  isDirty,
  canvasName,
  isPublished,
  onItemsChange,
  onLayoutChange,
  onBothChange,
  onSave,
  onBack,
  onPublish,
  onUnpublish,
}) => {
  const { t } = useTranslation();

  const [selectedId, setSelectedId] = useState<string | null>(null);
  const [isPreview, setIsPreview] = useState(false);
  const [editingId, setEditingId] = useState<string | null>(null);
  const [isDragging, setIsDragging] = useState(false);
  const [configModal, setConfigModal] = useState<{
    id: string;
    componentType: string;
  } | null>(null);

  const [dataSourceModal, setDataSourceModal] = useState<{
    id: string;
    componentType: string;
    code: string;
  } | null>(null);

  // ── PromptModal state（通用文本输入弹窗）──
  const [promptModal, setPromptModal] = useState<{
    label: string;
    placeholder?: string;
    defaultValue?: string;
    onConfirm: (value: string) => void;
  } | null>(null);

  const menuActionContext: MenuActionContext = useMemo(
    () => ({
      showPrompt: (opts) => setPromptModal(opts),
    }),
    [],
  );

  const agent = useAgent();

  // 触发 agent 修改数据来源
  const triggerDataSourceAgent = useCallback(
    (
      id: string,
      componentType: string,
      code: string,
      userPrompt?: string,
      skill?: string,
    ) => {
      const prompt = generatePrompt({
        skill: skill ?? 'canvas-sql-query',
        pageInfo: agent?.pageInfo ?? {
          resourceName: '',
          pageType: 'unknown',
          path: '',
          sourceFilePath: null,
        },
        userPrompt: userPrompt || '修改数据来源',
        inspectedElement: {
          type: 'canvas-component' as any,
          resource: 'canvas',
          x: 0,
          y: 0,
          meta: { componentType, canvasItemId: id, code },
        },
      });
      agent?.sendPrompt(prompt);
    },
    [agent],
  );

  const { width: containerWidth, containerRef, mounted } = useContainerWidth();

  const liveRef = useRef({
    items,
    layout,
    onItemsChange,
    onLayoutChange,
    onBothChange,
    selectedId,
    editingId,
  });
  liveRef.current = {
    items,
    layout,
    onItemsChange,
    onLayoutChange,
    onBothChange,
    selectedId,
    editingId,
  };

  // 自定义 compactor：Section 与普通组件之间允许重叠
  const sectionCompactor = useMemo(
    () => createSectionCompactor(() => liveRef.current.items),
    [],
  );

  // 添加组件
  const addComponent = useCallback((type: string) => {
    const { items, layout, onBothChange } = liveRef.current;
    const def = CANVAS_DEFAULTS[type];
    if (!def) return;
    const id = genId();
    const maxY = layout.reduce((m, l) => Math.max(m, l.y + l.h), 0);
    const h = def.layout.h;
    // 映射实际 componentType（BarChart/LineChart/PieChart → MultiChart）
    const actualType = COMPONENT_TYPE_MAP[type] ?? type;
    onBothChange(
      [
        ...items,
        { id, componentType: actualType, code: syncCodeHeight(def.code, h) },
      ],
      [
        ...layout,
        {
          i: id,
          x: 0,
          y: maxY,
          w: def.layout.w,
          h,
          minW: def.layout.minW,
          minH: def.layout.minH ?? 2,
        },
      ],
    );
  }, []);

  // 删除组件
  const removeComponent = useCallback((id: string) => {
    const { items, layout, onBothChange } = liveRef.current;
    onBothChange(
      items.filter((item) => item.id !== id),
      layout.filter((l) => l.i !== id),
    );
    setSelectedId((prev) => (prev === id ? null : prev));
    setEditingId((prev) => (prev === id ? null : prev));
  }, []);

  // 复制组件
  const duplicateComponent = useCallback((id: string) => {
    const { items, layout, onBothChange } = liveRef.current;
    const item = items.find((i) => i.id === id);
    const itemLayout = layout.find((l) => l.i === id);
    if (!item || !itemLayout) return;
    const newId = genId();
    const maxY = layout.reduce((m, l) => Math.max(m, l.y + l.h), 0);
    onBothChange(
      [...items, { ...item, id: newId }],
      [...layout, { ...itemLayout, i: newId, y: maxY }],
    );
  }, []);

  // 更新组件代码
  const updateCode = useCallback((id: string, code: string) => {
    const { items, onItemsChange } = liveRef.current;
    onItemsChange(
      items.map((item) => (item.id === id ? { ...item, code } : item)),
    );
  }, []);

  // ── canvas:patch HMR 监听 — agent 通过 POST /canvas-patch 触发画布实时更新 ──
  useEffect(() => {
    if (!import.meta.hot) return;
    const handler = ({ itemId, code }: { itemId: string; code: string }) => {
      updateCode(itemId, code);
    };
    import.meta.hot.on('canvas:patch', handler);
    return () => {
      import.meta.hot?.off('canvas:patch', handler);
    };
  }, [updateCode]);

  // ── MultiChart 工具栏切换图表类型时，同步更新代码中的 defaultVariant ──
  useEffect(() => {
    const handler = (e: Event) => {
      const { variant } = (e as CustomEvent).detail;
      if (!variant) return;
      // 从事件源找到对应的 canvas item
      const target = e.target as HTMLElement;
      const testId = target?.getAttribute('data-testid');
      if (!testId) return;
      const { items } = liveRef.current;
      const item = items.find((i) => i.code?.includes(`testId="${testId}"`));
      if (!item) return;
      // 更新 defaultVariant
      let newCode = item.code;
      if (/defaultVariant\s*=\s*"[^"]*"/.test(newCode)) {
        newCode = newCode.replace(
          /defaultVariant\s*=\s*"[^"]*"/,
          `defaultVariant="${variant}"`,
        );
      } else if (/testId\s*=/.test(newCode)) {
        newCode = newCode.replace(
          /([ \t]*)(testId\s*=)/,
          `$1defaultVariant="${variant}"\n$1$2`,
        );
      }
      if (newCode !== item.code) updateCode(item.id, newCode);
    };
    document.addEventListener('multichart-variant-change', handler);
    return () =>
      document.removeEventListener('multichart-variant-change', handler);
  }, [updateCode]);

  const handleDragStop = useCallback((_layout: readonly LayoutItem[]) => {
    setIsDragging(false);
    if (_layout.some((l) => l.i === '__dropping-elem__')) return;
    const { layout, onLayoutChange } = liveRef.current;
    const hasChanged = _layout.some((l) => {
      const existing = layout.find((e) => e.i === l.i);
      return (
        !existing ||
        l.x !== existing.x ||
        l.y !== existing.y ||
        l.w !== existing.w ||
        l.h !== existing.h
      );
    });
    if (!hasChanged) return;
    const merged = _layout.map((l) => {
      const existing = layout.find((e) => e.i === l.i);
      return existing?.minW !== undefined || existing?.minH !== undefined
        ? { ...l, minW: existing?.minW, minH: existing?.minH }
        : l;
    });
    onLayoutChange(merged);
  }, []);

  const handleResizeStop = useCallback(
    (_layout: readonly LayoutItem[], _oldItem: any, newItem: any) => {
      const { layout, items, onItemsChange, onLayoutChange } = liveRef.current;

      if (
        _layout.length > 0 &&
        !_layout.some((l) => l.i === '__dropping-elem__')
      ) {
        const merged = _layout.map((l) => {
          const existing = layout.find((e) => e.i === l.i);
          return existing?.minW !== undefined || existing?.minH !== undefined
            ? { ...l, minW: existing?.minW, minH: existing?.minH }
            : l;
        });
        onLayoutChange(merged);
      }

      if (!newItem?.i) return;
      const { i: itemId, h } = newItem;
      onItemsChange(
        items.map((item) => {
          if (item.id !== itemId) return item;
          return { ...item, code: syncCodeHeight(item.code, h) };
        }),
      );
    },
    [],
  );

  // ── 外部拖拽放入 ──

  const handleDrop = useCallback(
    (
      _layout: readonly LayoutItem[],
      item: LayoutItem | undefined,
      e: Event,
    ) => {
      const type = (e as DragEvent).dataTransfer?.getData('componentType');
      if (!type) return;
      const def = CANVAS_DEFAULTS[type];
      if (!def) return;
      const { items, onBothChange } = liveRef.current;
      const id = genId();
      const h = def.layout.h;
      const cleanLayout = _layout.filter((l) => l.i !== '__dropping-elem__');

      // 优先使用 variant 代码（来自 Popover 拖拽），fallback 到 def.code
      const rawVariantCode = (e as DragEvent).dataTransfer?.getData(
        'variantCode',
      );
      const baseCode = rawVariantCode
        ? rawVariantCode.replace(
            /testId="demo-([^"]+)"/g,
            `testId="canvas-$1-${Date.now()}"`,
          )
        : def.code;

      // 映射实际 componentType（BarChart/LineChart/PieChart → MultiChart）
      const actualType = COMPONENT_TYPE_MAP[type] ?? type;

      onBothChange(
        [
          ...items,
          { id, componentType: actualType, code: syncCodeHeight(baseCode, h) },
        ],
        [
          ...cleanLayout,
          {
            i: id,
            x: item?.x ?? 0,
            y: item?.y ?? 0,
            w: def.layout.w,
            h,
            minW: def.layout.minW,
            minH: def.layout.minH ?? 2,
          },
        ],
      );
    },
    [],
  );

  // ── 右键菜单 ──

  const [itemMenu, setItemMenu] = useState<{
    x: number;
    y: number;
    id: string;
  } | null>(null);

  const handleItemContextMenu = useCallback(
    (e: React.MouseEvent, id: string) => {
      setItemMenu({ x: e.clientX, y: e.clientY, id });
    },
    [],
  );

  const itemMenuItems: MenuProps['items'] = itemMenu
    ? (() => {
        const item = items.find((i) => i.id === itemMenu.id);
        const componentType = item?.componentType ?? '';
        const hasConfig = componentType in CANVAS_CONFIG_REGISTRY;

        // ── 第 1 层：组件特有操作 ──
        const specificActionsFactory =
          CANVAS_CONTEXT_MENU_REGISTRY[componentType];
        const specificActions = specificActionsFactory
          ? specificActionsFactory(
              item?.code ?? '',
              (newCode) => {
                updateCode(itemMenu.id, newCode);
              },
              () => setItemMenu(null),
              menuActionContext,
              t,
            )
          : [];

        // ── 第 2 层：通用操作（检查元素） ──
        const commonActions: MenuProps['items'] = [
          ...(hasConfig
            ? [
                {
                  key: 'config',
                  icon: <SettingOutlined />,
                  label:
                    componentType === 'MultiChart'
                      ? (t('canvas.configCharts') ?? '配置显示图表与顺序')
                      : t('canvas.config'),
                  onClick: () => {
                    setConfigModal({ id: itemMenu.id, componentType });
                    setItemMenu(null);
                  },
                },
              ]
            : []),
          {
            key: 'data-source',
            icon: <DatabaseOutlined />,
            label: t('canvas.dataSource.label'),
            onClick: () => {
              if (
                componentType === 'NumCard' ||
                componentType === 'Table' ||
                componentType === 'BarChart' ||
                componentType === 'LineChart' ||
                componentType === 'RadarChart' ||
                componentType === 'MultiChart'
              ) {
                setDataSourceModal({
                  id: itemMenu.id,
                  componentType,
                  code: item?.code ?? '',
                });
              } else {
                menuActionContext.showPrompt({
                  label: t('canvas.dataSource.label'),
                  placeholder: t('canvas.dataSource.placeholder'),
                  defaultValue: '',
                  onConfirm: (value) => {
                    if (!value.trim()) return;
                    const prompt = generatePrompt({
                      skill: 'canvas-sql-query',
                      pageInfo: agent?.pageInfo ?? {
                        resourceName: '',
                        pageType: 'unknown',
                        path: '',
                        sourceFilePath: null,
                      },
                      userPrompt: `为此 ${componentType} 组件用 SQL 查询真实数据并填充：${value.trim()}`,
                      inspectedElement: {
                        type: 'canvas-component' as any,
                        resource: 'canvas',
                        x: 0,
                        y: 0,
                        meta: {
                          componentType,
                          canvasItemId: itemMenu.id,
                          code: item?.code ?? '',
                        },
                      },
                    });
                    agent?.sendPrompt(prompt);
                  },
                });
              }
              setItemMenu(null);
            },
          },
          {
            key: 'custom-agent',
            icon: <EditOutlined />,
            label: t('canvas.custom'),
            onClick: () => {
              setItemMenu(null);
              menuActionContext.showPrompt({
                label: t('canvas.customAction'),
                placeholder: t('canvas.customPlaceholder'),
                defaultValue: '',
                onConfirm: (value) => {
                  if (!value.trim()) return;
                  const prompt = generatePrompt({
                    skill: null,
                    pageInfo: agent?.pageInfo ?? {
                      resourceName: '',
                      pageType: 'unknown',
                      path: '',
                      sourceFilePath: null,
                    },
                    userPrompt: value.trim(),
                    inspectedElement: {
                      type: 'canvas-component' as any,
                      resource: 'canvas',
                      x: 0,
                      y: 0,
                      meta: {
                        componentType,
                        canvasItemId: itemMenu.id,
                        code: item?.code ?? '',
                      },
                    },
                  });
                  agent?.sendPrompt(prompt);
                },
              });
            },
          },
        ];

        // ── 第 3 层：代码级操作 ──
        const codeActions: MenuProps['items'] = [
          {
            key: 'edit',
            icon: <EditOutlined />,
            label: t('canvas.editCode'),
            onClick: () => {
              setEditingId(itemMenu.id);
              setItemMenu(null);
            },
          },
          {
            key: 'copy',
            icon: <CopyOutlined />,
            label: t('canvas.duplicate'),
            onClick: () => {
              duplicateComponent(itemMenu.id);
              setItemMenu(null);
            },
          },
          {
            key: 'delete',
            icon: <DeleteOutlined />,
            label: t('canvas.delete'),
            danger: true,
            onClick: () => {
              removeComponent(itemMenu.id);
              setItemMenu(null);
            },
          },
        ];

        // ── 组装三层 ──
        const result: MenuProps['items'] = [];
        if (specificActions && specificActions.length > 0) {
          result.push(...specificActions);
          result.push({ type: 'divider' as const });
        }
        result.push(...commonActions);
        result.push({ type: 'divider' as const });
        result.push(...codeActions);

        return result;
      })()
    : [];

  const handleCanvasClick = useCallback(() => {
    setSelectedId(null);
    setItemMenu(null);
  }, []);

  const editingItem = editingId ? items.find((i) => i.id === editingId) : null;
  const editingDef = editingItem
    ? CANVAS_DEFAULTS[editingItem.componentType]
    : null;

  const dropConfig = useMemo(
    () => ({
      enabled: true,
      defaultItem: { w: 12, h: 12 },
      onDragOver: (e: DragEvent) => {
        const typeKey = Array.from(
          (e as unknown as React.DragEvent).dataTransfer?.types ??
            e.dataTransfer?.types ??
            [],
        ).find((t: string) => t.startsWith('x-component-type/'));
        if (!typeKey) return;
        const componentType = CANVAS_COMPONENTS.find(
          (t) => `x-component-type/${t.toLowerCase()}` === typeKey,
        );
        if (!componentType) return;
        const def = CANVAS_DEFAULTS[componentType];
        if (!def) return;
        return { w: def.layout.w, h: def.layout.h };
      },
    }),
    [],
  );

  return (
    <div
      style={{
        display: 'flex',
        flexDirection: 'column',
        ...(isPreview ? {} : { height: 'calc(100vh - 64px - 64px)' }),
      }}
    >
      {/* ── 顶部工具栏（固定在画布外面，不参与滚动） ── */}
      {!isPreview && (
        <CanvasToolbar
          isDirty={isDirty}
          canvasName={canvasName}
          isPublished={isPublished}
          onSave={onSave}
          onBack={onBack}
          onPublish={onPublish}
          onUnpublish={onUnpublish}
          onPreview={() => setIsPreview(true)}
        />
      )}

      {/* ── 画布 ── */}
      <div
        ref={containerRef as React.RefObject<HTMLDivElement>}
        onClick={handleCanvasClick}
        style={{
          flex: 1,
          overflow: isPreview ? 'visible' : 'auto',
          position: 'relative',
          background: isDragging
            ? `
              radial-gradient(circle, #1677ff 1.5px, transparent 1.5px) 0 0 / 90px 90px,
              radial-gradient(circle, #1677ff 0.8px, transparent 0.8px) 0 0 / 18px 18px,
              #f0f2f5
            `
            : '#f0f2f5',
          transition: 'background 0.15s',
        }}
      >
        {/* 空画布提示 */}
        {items.length === 0 && (
          <div
            style={{
              position: 'absolute',
              inset: 0,
              display: 'flex',
              flexDirection: 'column',
              alignItems: 'center',
              justifyContent: 'center',
              gap: 12,
              userSelect: 'none',
              pointerEvents: 'none',
            }}
          >
            <PlusCircleOutlined style={{ fontSize: 48, color: '#d9d9d9' }} />
            <span style={{ fontSize: 16, color: '#8c8c8c' }}>
              {t('canvas.emptyHint')}
            </span>
            <span style={{ fontSize: 13, color: '#bfbfbf' }}>
              或右键画布添加组件
            </span>
          </div>
        )}

        {/* 网格布局 */}
        {mounted && (
          <div style={{ maxWidth: 1600, margin: '0 auto', width: '100%' }}>
            <GridLayout
              className="layout"
              layout={layout}
              width={Math.min(containerWidth, 1600)}
              style={{
                minHeight: Math.max(
                  // 根据 layout 内容底部动态延伸 + 额外空间
                  layout.reduce(
                    (max, item) =>
                      Math.max(
                        max,
                        (item.y + item.h) * (ROW_HEIGHT + MARGIN_Y),
                      ),
                    0,
                  ) + (isDragging ? 600 : 300),
                  // 至少占满一屏
                  window.innerHeight - 200,
                ),
              }}
              gridConfig={{
                cols: COLS,
                rowHeight: ROW_HEIGHT,
                margin: [MARGIN_Y, MARGIN_Y] as const,
              }}
              dragConfig={{
                enabled: !isPreview,
                handle: '.canvas-item-drag',
              }}
              resizeConfig={{
                enabled: !isPreview,
              }}
              dropConfig={isPreview ? undefined : dropConfig}
              onDrop={isPreview ? undefined : handleDrop}
              compactor={sectionCompactor}
              onResizeStop={handleResizeStop}
              onDragStart={() => setIsDragging(true)}
              onDragStop={handleDragStop}
            >
              {items.map((item) => (
                <CanvasCell
                  key={item.id}
                  className="canvas-item-drag"
                  style={{ zIndex: item.componentType === 'Section' ? 0 : 1 }}
                  componentType={item.componentType}
                  isPreview={isPreview}
                  data-grid-id={item.id}
                  data-agent-type="canvas-component"
                  data-agent-resource="canvas"
                  data-agent-meta={JSON.stringify({
                    componentType: item.componentType,
                    canvasItemId: item.id,
                    code: item.code,
                  })}
                  code={item.code}
                  selected={selectedId === item.id}
                  onClick={() => setSelectedId(item.id)}
                  onDoubleClick={() => setEditingId(item.id)}
                  onContextMenu={(e: React.MouseEvent) =>
                    handleItemContextMenu(e, item.id)
                  }
                />
              ))}
            </GridLayout>
          </div>
        )}

        {/* 预览模式退出按钮 */}
        {isPreview && (
          <Button
            onClick={() => setIsPreview(false)}
            style={{
              position: 'fixed',
              top: 68,
              left: '58%',
              transform: 'translateX(-50%)',
              zIndex: 1000,
              borderRadius: 8,
              background: '#fff',
              border: '1px solid #e0e0e0',
              boxShadow: '0 2px 8px rgba(0,0,0,0.08)',
              fontWeight: 500,
              color: '#555',
            }}
          >
            {t('canvas.exitPreview')}
          </Button>
        )}

        {/* 组件右键菜单 */}
        {itemMenu && (
          <Dropdown
            menu={{ items: itemMenuItems }}
            open
            trigger={['click']}
            onOpenChange={(open) => {
              if (!open) setItemMenu(null);
            }}
          >
            <div
              style={{
                position: 'fixed',
                left: itemMenu.x,
                top: itemMenu.y,
                width: 1,
                height: 1,
              }}
            />
          </Dropdown>
        )}

        {/* 组件配置 Modal */}
        {configModal &&
          (() => {
            const ConfigModal = CANVAS_CONFIG_REGISTRY[
              configModal.componentType
            ] as React.FC<CanvasConfigModalProps> | undefined;
            const item = items.find((i) => i.id === configModal.id);
            if (!ConfigModal || !item) return null;
            return (
              <ConfigModal
                code={item.code}
                onConfirm={(newCode) => {
                  updateCode(configModal.id, newCode);
                  setConfigModal(null);
                }}
                onCancel={() => setConfigModal(null)}
              />
            );
          })()}

        {/* 浮动代码窗口 */}
        {!isPreview && editingItem && editingDef && (
          <CodeFloatWindow
            componentType={editingItem.componentType}
            imports={editingDef.imports}
            code={editingItem.code}
            props={editingDef.props}
            variants={editingDef.variants}
            onChange={(code) => updateCode(editingItem.id, code)}
            onClose={() => setEditingId(null)}
          />
        )}

        {/* NumCard 修改数据来源 Modal */}
        <NumCardDataSourceModal
          open={
            !!dataSourceModal && dataSourceModal.componentType === 'NumCard'
          }
          code={dataSourceModal?.code ?? ''}
          onConfirm={({
            wantChart,
            chartRange,
            wantComparisons,
            comparisonLabels,
            dataDescription,
            code: origCode,
          }) => {
            if (!dataSourceModal) return;
            let newCode = origCode;

            // 处理折线图
            const curHasChart = /variant\s*=\s*"withChart"/.test(newCode);
            if (wantChart && !curHasChart) {
              // 添加 withChart
              if (/variant\s*=\s*"[^"]*"/.test(newCode)) {
                newCode = newCode.replace(
                  /variant\s*=\s*"[^"]*"/,
                  'variant="withChart"',
                );
              } else {
                newCode = newCode.replace(
                  /(<NumCard)/,
                  '$1\n  variant="withChart"',
                );
              }
              if (!/chartData\s*=/.test(newCode)) {
                const tpl = `chartData={[]}\n  chartTestId="canvas-numcard-chart"`;
                newCode = newCode.replace(
                  /([ \t]*)(testId\s*=)/,
                  `$1${tpl}\n$1$2`,
                );
              }
            } else if (!wantChart && curHasChart) {
              // 移除 withChart
              newCode = newCode.replace(
                /variant\s*=\s*"withChart"/,
                'variant="default"',
              );
              newCode = newCode.replace(
                /\s*chartData\s*=\s*\{[\s\S]*?\]\s*\}/,
                '',
              );
              newCode = newCode.replace(/\s*chartTestId\s*=\s*"[^"]*"/, '');
            }

            // 处理比较数据
            const curHasComp = /comparisons\s*=\s*\{\[/.test(newCode);
            if (wantComparisons && comparisonLabels.length > 0) {
              const compStr = comparisonLabels
                .map((l) => `{ label: "${l}", value: "0%", isUp: true }`)
                .join(',\n    ');
              const compProp = `comparisons={[\n    ${compStr},\n  ]}`;
              if (curHasComp) {
                newCode = newCode.replace(
                  /comparisons\s*=\s*\{\[[\s\S]*?\]\}/,
                  compProp,
                );
              } else {
                if (/testId\s*=/.test(newCode)) {
                  newCode = newCode.replace(
                    /([ \t]*)(testId\s*=)/,
                    `$1${compProp}\n$1$2`,
                  );
                } else {
                  newCode = newCode.replace(/\/>/, `  ${compProp}\n/>`);
                }
              }
            } else if (!wantComparisons && curHasComp) {
              newCode = newCode.replace(
                /\s*comparisons\s*=\s*\{\[[\s\S]*?\]\}/,
                '',
              );
            }

            updateCode(dataSourceModal.id, newCode);
            setDataSourceModal(null);

            // 构造描述发给 agent
            const parts: string[] = [];
            if (dataDescription) parts.push(dataDescription);
            if (wantChart)
              parts.push(`需要右侧折线图数据，显示范围：${chartRange}`);
            if (wantComparisons && comparisonLabels.length > 0) {
              parts.push(`需要比较数据：${comparisonLabels.join('、')}`);
            }
            const userPrompt = parts.join('。');
            triggerDataSourceAgent(
              dataSourceModal.id,
              dataSourceModal.componentType,
              newCode,
              userPrompt || undefined,
              'canvas-sql-query',
            );
          }}
          onCancel={() => setDataSourceModal(null)}
        />

        {/* Table 修改数据来源 Modal */}
        <TableDataSourceModal
          open={!!dataSourceModal && dataSourceModal.componentType === 'Table'}
          code={dataSourceModal?.code ?? ''}
          onConfirm={({
            columns,
            wantPagination,
            pageSize,
            dataDescription,
            code: origCode,
          }) => {
            if (!dataSourceModal) return;
            let newCode = origCode;

            // 根据用户选择更新 columns
            const colsStr = columns
              .map(
                (c) => `    { title: "${c}", dataIndex: "${c}", key: "${c}" }`,
              )
              .join(',\n');
            if (/columns\s*=\s*\{\[/.test(newCode)) {
              newCode = newCode.replace(
                /columns\s*=\s*\{\[[\s\S]*?\]\}/,
                `columns={[\n${colsStr},\n  ]}`,
              );
            }

            // 处理分页
            const curHasPagination = /pagination\s*=\s*\{\{/.test(newCode);
            if (wantPagination && !curHasPagination) {
              const paginationTpl = `pagination={{ current: 1, pageSize: ${pageSize}, total: 0 }}`;
              if (/testId\s*=/.test(newCode)) {
                newCode = newCode.replace(
                  /([ \t]*)(testId\s*=)/,
                  `$1${paginationTpl}\n$1$2`,
                );
              } else {
                newCode = newCode.replace(/\/>/, `  ${paginationTpl}\n/>`);
              }
            } else if (wantPagination && curHasPagination) {
              // 更新已有的 pageSize
              newCode = newCode.replace(
                /pageSize\s*:\s*\d+/,
                `pageSize: ${pageSize}`,
              );
            } else if (!wantPagination && curHasPagination) {
              newCode = newCode.replace(
                /\s*pagination\s*=\s*\{\{[\s\S]*?\}\}/,
                '',
              );
              newCode = newCode.replace(/\s*entityName\s*=\s*"[^"]*"/, '');
            }

            updateCode(dataSourceModal.id, newCode);
            setDataSourceModal(null);

            // 构造描述发给 agent
            const parts: string[] = [];
            if (dataDescription) parts.push(dataDescription);
            parts.push(`表格列：${columns.join('、')}`);
            if (wantPagination) parts.push(`需要分页，每页 ${pageSize} 条`);
            const userPrompt = parts.join('。');
            triggerDataSourceAgent(
              dataSourceModal.id,
              dataSourceModal.componentType,
              newCode,
              userPrompt,
              'canvas-sql-query',
            );
          }}
          onCancel={() => setDataSourceModal(null)}
        />

        {/* BarChart 修改数据来源 Modal */}
        <BarChartDataSourceModal
          open={
            !!dataSourceModal && dataSourceModal.componentType === 'BarChart'
          }
          code={dataSourceModal?.code ?? ''}
          onConfirm={(result) => {
            if (!dataSourceModal) return;
            let newCode = result.code;

            // 修改 direction
            if (result.direction === 'horizontal') {
              if (!/direction\s*=/.test(newCode)) {
                if (/testId\s*=/.test(newCode)) {
                  newCode = newCode.replace(
                    /([ \t]*)(testId\s*=)/,
                    `$1direction="horizontal"\n$1$2`,
                  );
                } else {
                  newCode = newCode.replace(
                    /\/>/,
                    `  direction="horizontal"\n/>`,
                  );
                }
              } else {
                newCode = newCode.replace(
                  /direction\s*=\s*"[^"]*"/,
                  'direction="horizontal"',
                );
              }
            } else {
              // vertical 是默认值，移除 direction prop
              newCode = newCode.replace(/\s*direction\s*=\s*"[^"]*"/, '');
            }

            // 修改 showGrid
            if (!result.showGrid) {
              if (!/showGrid\s*=/.test(newCode)) {
                if (/testId\s*=/.test(newCode)) {
                  newCode = newCode.replace(
                    /([ \t]*)(testId\s*=)/,
                    `$1showGrid={false}\n$1$2`,
                  );
                } else {
                  newCode = newCode.replace(/\/>/, `  showGrid={false}\n/>`);
                }
              }
            } else {
              newCode = newCode.replace(/\s*showGrid\s*=\s*\{false\}/, '');
            }

            // 修改 showLabel
            if (!result.showLabel) {
              if (!/showLabel\s*=/.test(newCode)) {
                if (/testId\s*=/.test(newCode)) {
                  newCode = newCode.replace(
                    /([ \t]*)(testId\s*=)/,
                    `$1showLabel={false}\n$1$2`,
                  );
                } else {
                  newCode = newCode.replace(/\/>/, `  showLabel={false}\n/>`);
                }
              }
            } else {
              newCode = newCode.replace(/\s*showLabel\s*=\s*\{false\}/, '');
            }

            // 更新画布
            updateCode(dataSourceModal.id, newCode);
            setDataSourceModal(null);

            // 构造 prompt 并触发 agent
            const dirLabel =
              result.direction === 'horizontal' ? '横向' : '纵向';
            const parts: string[] = [];
            if (result.dataDescription) parts.push(result.dataDescription);
            parts.push(`方向: ${dirLabel}`);
            if (!result.showGrid) parts.push('隐藏网格线');
            if (!result.showLabel) parts.push('隐藏数据标签');
            const userPrompt = parts.join('。');
            triggerDataSourceAgent(
              dataSourceModal.id,
              'BarChart',
              newCode,
              userPrompt,
              'canvas-sql-query',
            );
          }}
          onCancel={() => setDataSourceModal(null)}
        />

        <LineChartDataSourceModal
          open={
            !!dataSourceModal && dataSourceModal.componentType === 'LineChart'
          }
          code={dataSourceModal?.code ?? ''}
          onConfirm={(result) => {
            if (!dataSourceModal) return;
            let newCode = result.code;

            // ── legendShow ──
            if (!result.legendShow) {
              if (!/legendShow\s*=\s*\{/.test(newCode)) {
                if (/testId\s*=/.test(newCode)) {
                  newCode = newCode.replace(
                    /([ \t]*)(testId\s*=)/,
                    `$1legendShow={false}\n$1$2`,
                  );
                } else {
                  newCode = newCode.replace(/\/>/, `  legendShow={false}\n/>`);
                }
              } else {
                newCode = newCode.replace(
                  /legendShow\s*=\s*\{true\}/,
                  'legendShow={false}',
                );
              }
            } else {
              newCode = newCode.replace(/\s*legendShow\s*=\s*\{false\}/, '');
            }

            // ── showGrid（默认 true，false 时注入）──
            if (!result.showGrid) {
              if (!/showGrid\s*=/.test(newCode)) {
                if (/testId\s*=/.test(newCode)) {
                  newCode = newCode.replace(
                    /([ \t]*)(testId\s*=)/,
                    `$1showGrid={false}\n$1$2`,
                  );
                } else {
                  newCode = newCode.replace(/\/>/, `  showGrid={false}\n/>`);
                }
              }
            } else {
              newCode = newCode.replace(/\s*showGrid\s*=\s*\{false\}/, '');
            }

            // ── showAxis（默认 true，false 时注入）──
            if (!result.showAxis) {
              if (!/showAxis\s*=/.test(newCode)) {
                if (/testId\s*=/.test(newCode)) {
                  newCode = newCode.replace(
                    /([ \t]*)(testId\s*=)/,
                    `$1showAxis={false}\n$1$2`,
                  );
                } else {
                  newCode = newCode.replace(/\/>/, `  showAxis={false}\n/>`);
                }
              }
            } else {
              newCode = newCode.replace(/\s*showAxis\s*=\s*\{false\}/, '');
            }

            // ── showDots（默认 false，true 时注入）──
            if (result.showDots) {
              if (!/showDots\s*=/.test(newCode)) {
                if (/testId\s*=/.test(newCode)) {
                  newCode = newCode.replace(
                    /([ \t]*)(testId\s*=)/,
                    `$1showDots={true}\n$1$2`,
                  );
                } else {
                  newCode = newCode.replace(/\/>/, `  showDots={true}\n/>`);
                }
              }
            } else {
              newCode = newCode.replace(/\s*showDots\s*=\s*\{true\}/, '');
            }

            // ── showLabel（默认 false，true 时注入，仅 showDots 时有意义）──
            if (result.showDots && result.showLabel) {
              if (!/showLabel\s*=/.test(newCode)) {
                if (/testId\s*=/.test(newCode)) {
                  newCode = newCode.replace(
                    /([ \t]*)(testId\s*=)/,
                    `$1showLabel={true}\n$1$2`,
                  );
                } else {
                  newCode = newCode.replace(/\/>/, `  showLabel={true}\n/>`);
                }
              }
            } else {
              newCode = newCode.replace(/\s*showLabel\s*=\s*\{true\}/, '');
            }

            // ── stacked（默认 false，true 时注入）──
            if (result.stacked) {
              if (!/stacked\s*=/.test(newCode)) {
                if (/testId\s*=/.test(newCode)) {
                  newCode = newCode.replace(
                    /([ \t]*)(testId\s*=)/,
                    `$1stacked={true}\n$1$2`,
                  );
                } else {
                  newCode = newCode.replace(/\/>/, `  stacked={true}\n/>`);
                }
              }
            } else {
              newCode = newCode.replace(/\s*stacked\s*=\s*\{true\}/, '');
            }

            // ── area（默认 false，true 时注入）──
            if (result.area) {
              if (!/area\s*=/.test(newCode)) {
                if (/testId\s*=/.test(newCode)) {
                  newCode = newCode.replace(
                    /([ \t]*)(testId\s*=)/,
                    `$1area={true}\n$1$2`,
                  );
                } else {
                  newCode = newCode.replace(/\/>/, `  area={true}\n/>`);
                }
              }
            } else {
              newCode = newCode.replace(/\s*area\s*=\s*\{true\}/, '');
            }

            // ── smooth（默认 false，true 时注入）──
            if (result.smooth) {
              if (!/smooth\s*=/.test(newCode)) {
                if (/testId\s*=/.test(newCode)) {
                  newCode = newCode.replace(
                    /([ \t]*)(testId\s*=)/,
                    `$1smooth={true}\n$1$2`,
                  );
                } else {
                  newCode = newCode.replace(/\/>/, `  smooth={true}\n/>`);
                }
              }
            } else {
              newCode = newCode.replace(/\s*smooth\s*=\s*\{true\}/, '');
              newCode = newCode.replace(/\s*smooth\b(?!\s*=)/, '');
            }

            // 更新画布
            updateCode(dataSourceModal.id, newCode);
            setDataSourceModal(null);

            // 构造 prompt 并触发 agent
            const parts: string[] = [];
            if (result.dataDescription) parts.push(result.dataDescription);
            const flags: string[] = [];
            if (result.stacked) flags.push('堆叠');
            if (result.area) flags.push('面积填充');
            if (result.smooth) flags.push('拟合曲线');
            if (!result.showGrid) flags.push('无网格线');
            if (!result.showAxis) flags.push('无坐标轴');
            if (!result.legendShow) flags.push('隐藏图例');
            if (result.showDots) flags.push('显示坐标点');
            if (result.showDots && result.showLabel)
              flags.push('显示坐标点数据');
            if (flags.length) parts.push(`样式: ${flags.join(', ')}`);
            const userPrompt = parts.join('。');
            triggerDataSourceAgent(
              dataSourceModal.id,
              'LineChart',
              newCode,
              userPrompt || undefined,
              'canvas-sql-query',
            );
          }}
          onCancel={() => setDataSourceModal(null)}
        />

        {/* RadarChart 修改数据来源 Modal */}
        <RadarChartDataSourceModal
          open={
            !!dataSourceModal && dataSourceModal.componentType === 'RadarChart'
          }
          code={dataSourceModal?.code ?? ''}
          onConfirm={({ dimensionCount, dataDescription }) => {
            if (!dataSourceModal) return;
            setDataSourceModal(null);

            // 不修改代码，仅构建 prompt 发给 agent
            const parts: string[] = [];
            parts.push(`雷达图需要${dimensionCount}个维度`);
            if (dataDescription) parts.push(dataDescription);
            const userPrompt = parts.join('，');
            triggerDataSourceAgent(
              dataSourceModal.id,
              dataSourceModal.componentType,
              dataSourceModal.code,
              userPrompt,
              'canvas-sql-query',
            );
          }}
          onCancel={() => setDataSourceModal(null)}
        />

        {/* MultiChart 修改数据来源 Modal */}
        <MultiChartDataSourceModal
          open={
            !!dataSourceModal && dataSourceModal.componentType === 'MultiChart'
          }
          code={dataSourceModal?.code ?? ''}
          onConfirm={({
            dataDescription,
            charts,
            chartConfigs,
            code: origCode,
          }) => {
            if (!dataSourceModal) return;
            let newCode = origCode;

            // 更新 charts prop
            const chartsStr = `charts={${JSON.stringify(charts)}}`;
            if (/charts=\{.*?\}/s.test(newCode)) {
              newCode = newCode.replace(/charts=\{.*?\}/s, chartsStr);
            } else if (/testId\s*=/.test(newCode)) {
              newCode = newCode.replace(
                /([ \t]*)(testId\s*=)/,
                `$1${chartsStr}\n$1$2`,
              );
            }

            // 更新 chartConfig prop
            const configObj: Record<string, Record<string, any>> = {};
            for (const [chartKey, config] of Object.entries(chartConfigs)) {
              const nonDefaults: Record<string, any> = {};
              for (const [k, v] of Object.entries(config)) {
                // 只存非默认值的配置
                if (
                  k === 'showGrid' ||
                  k === 'showLabel' ||
                  k === 'legendShow'
                ) {
                  if (!v) nonDefaults[k] = false;
                } else {
                  if (v) nonDefaults[k] = true;
                }
              }
              if (Object.keys(nonDefaults).length > 0) {
                configObj[chartKey] = nonDefaults;
              }
            }
            if (Object.keys(configObj).length > 0) {
              const configStr = `chartConfig={${JSON.stringify(configObj)}}`;
              if (/chartConfig=\{[\s\S]*?\}\s*(?=\w+=|\/?>)/.test(newCode)) {
                newCode = newCode.replace(
                  /chartConfig=\{[\s\S]*?\}\s*(?=\w+=|\/?>)/,
                  configStr + '\n  ',
                );
              } else if (/testId\s*=/.test(newCode)) {
                newCode = newCode.replace(
                  /([ \t]*)(testId\s*=)/,
                  `$1${configStr}\n$1$2`,
                );
              }
            }

            updateCode(dataSourceModal.id, newCode);
            setDataSourceModal(null);

            // 构造 prompt 发给 agent
            const parts: string[] = [];
            if (dataDescription) parts.push(dataDescription);
            parts.push(`复合图表包含：${charts.join('、')}`);
            // 附加各图表配置描述（与单独图表 DataSource modal 输出一致）
            for (const [chartKey, config] of Object.entries(chartConfigs)) {
              const flags: string[] = [];
              for (const [k, v] of Object.entries(config)) {
                if (v === false || v === '' || v === undefined || v === null)
                  continue;
                // select/slider 类型：输出具体值
                if (typeof v === 'string') {
                  if (k === 'variant' && v === 'groupBar') flags.push('多系列');
                  else if (k === 'variant' && v === 'bar') flags.push('单系列');
                  else if (k === 'variant' && v === 'ring')
                    flags.push('环形图');
                  else if (k === 'direction' && v === 'horizontal')
                    flags.push('横向');
                  else if (k === 'direction' && v === 'vertical')
                    flags.push('纵向');
                } else if (typeof v === 'number') {
                  if (k === 'dimensionCount') flags.push(`${v}个维度`);
                } else if (v === true) {
                  // boolean 类型：输出中文描述
                  if (k === 'stacked') flags.push('堆叠');
                  else if (k === 'percent') flags.push('百分比');
                  else if (k === 'area') flags.push('面积填充');
                  else if (k === 'smooth') flags.push('拟合曲线');
                  else if (k === 'showDots') flags.push('显示坐标点');
                  else if (k === 'showLabel') flags.push('显示坐标点数据');
                  // 默认开启的项目只在关闭时才有意义，开启时不输出
                }
              }
              // 默认开启的项目关闭时输出
              if (config.showGrid === false) flags.push('隐藏网格线');
              if (config.showAxis === false) flags.push('隐藏坐标轴');
              if (config.legendShow === false) flags.push('隐藏图例');
              if (config.showLabel === false) flags.push('隐藏数据标签');
              if (flags.length > 0) {
                parts.push(`${chartKey}: ${flags.join(', ')}`);
              }
            }
            parts.push(
              '请返回 MultiChartData 格式：{ xData: string[], series: [{ name, data }] }',
            );
            const userPrompt = parts.join('。');
            triggerDataSourceAgent(
              dataSourceModal.id,
              dataSourceModal.componentType,
              newCode,
              userPrompt,
              'canvas-sql-query',
            );
          }}
          onCancel={() => setDataSourceModal(null)}
        />

        {/* ── 通用文本输入弹窗（如修改标题） ── */}
        <PromptModal
          open={!!promptModal}
          label={promptModal?.label ?? ''}
          placeholder={promptModal?.placeholder}
          defaultValue={promptModal?.defaultValue ?? ''}
          onConfirm={(value) => {
            promptModal?.onConfirm(value);
            setPromptModal(null);
          }}
          onCancel={() => setPromptModal(null)}
        />
      </div>
    </div>
  );
};

export default CanvasPage;
