"use client";

import { useState, useCallback, useEffect, useMemo, useRef } from "react";
import { Database, Cog } from "lucide-react";
import { cn } from "@/lib/utils";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { Button } from "@/components/ui/button";
import {
  ScreenDefinition,
  ComponentData,
  LayoutData,
  GroupState,
  TableInfo,
  Position,
  ColumnInfo,
  GridSettings,
  ScreenResolution,
  SCREEN_RESOLUTIONS,
} from "@/types/screen";
import { generateComponentId } from "@/lib/utils/generateId";
import {
  getComponentIdFromWebType,
  createV2ConfigFromColumn,
  getV2ConfigFromWebType,
} from "@/lib/utils/webTypeMapping";
import {
  createGroupComponent,
  calculateBoundingBox,
  calculateRelativePositions,
  restoreAbsolutePositions,
} from "@/lib/utils/groupingUtils";
import {
  adjustGridColumnsFromSize,
  updateSizeFromGridColumns,
  calculateWidthFromColumns,
  snapSizeToGrid,
  snapToGrid,
} from "@/lib/utils/gridUtils";
import {
  alignComponents,
  distributeComponents,
  matchComponentSize,
  toggleAllLabels,
  nudgeComponents,
  AlignMode,
  DistributeDirection,
  MatchSizeMode,
} from "@/lib/utils/alignmentUtils";
import { KeyboardShortcutsModal } from "./modals/KeyboardShortcutsModal";

// 10px 단위 스냅 함수
const snapTo10px = (value: number): number => {
  return Math.round(value / 10) * 10;
};

const snapPositionTo10px = (position: Position): Position => {
  return {
    x: snapTo10px(position.x),
    y: snapTo10px(position.y),
    z: position.z,
  };
};

const snapSizeTo10px = (size: { width: number; height: number }): { width: number; height: number } => {
  return {
    width: snapTo10px(size.width),
    height: snapTo10px(size.height),
  };
};

// calculateGridInfo 더미 함수 (하위 호환성을 위해 유지)
const calculateGridInfo = (width: number, height: number, settings: any) => {
  return {
    columnWidth: 10,
    totalWidth: width,
    totalHeight: height,
    columns: settings.columns || 12,
    gap: settings.gap || 0,
    padding: settings.padding || 0,
  };
};
import { GroupingToolbar } from "./GroupingToolbar";
import { screenApi, tableTypeApi } from "@/lib/api/screen";
import { tableManagementApi } from "@/lib/api/tableManagement";
import { ExternalRestApiConnectionAPI } from "@/lib/api/externalRestApiConnection";
import { toast } from "sonner";
import { MenuAssignmentModal } from "./MenuAssignmentModal";
import { FileAttachmentDetailModal } from "./FileAttachmentDetailModal";
import { initializeComponents } from "@/lib/registry/components";
import { ScreenFileAPI } from "@/lib/api/screenFile";
import { safeMigrateLayout, needsMigration } from "@/lib/utils/widthToColumnSpan";
import { convertV2ToLegacy, convertLegacyToV2, isValidV2Layout } from "@/lib/utils/layoutV2Converter";

// V2 API 사용 플래그 (true: V2, false: 기존)
const USE_V2_API = true;

import StyleEditor from "./StyleEditor";
import { RealtimePreview } from "./RealtimePreviewDynamic";
import FloatingPanel from "./FloatingPanel";
import { DynamicComponentRenderer } from "@/lib/registry/DynamicComponentRenderer";
import { MultilangSettingsModal } from "./modals/MultilangSettingsModal";
import DesignerToolbar from "./DesignerToolbar";
import TablesPanel from "./panels/TablesPanel";
import { TemplatesPanel, TemplateComponent } from "./panels/TemplatesPanel";
import { ComponentsPanel } from "./panels/ComponentsPanel";
import PropertiesPanel from "./panels/PropertiesPanel";
import DetailSettingsPanel from "./panels/DetailSettingsPanel";
import ResolutionPanel from "./panels/ResolutionPanel";
import { usePanelState, PanelConfig } from "@/hooks/usePanelState";
import { FlowButtonGroup } from "./widgets/FlowButtonGroup";
import { FlowVisibilityConfig } from "@/types/control-management";
import {
  areAllButtons,
  generateGroupId,
  groupButtons,
  ungroupButtons,
  findAllButtonGroups,
} from "@/lib/utils/flowButtonGroupUtils";
import { FlowButtonGroupDialog } from "./dialogs/FlowButtonGroupDialog";
import { ScreenPreviewProvider } from "@/contexts/ScreenPreviewContext";
import { TableOptionsProvider } from "@/contexts/TableOptionsContext";

// 새로운 통합 UI 컴포넌트
import { SlimToolbar } from "./toolbar/SlimToolbar";
import { V2PropertiesPanel } from "./panels/V2PropertiesPanel";

// 컴포넌트 초기화 (새 시스템)
import "@/lib/registry/components";
// 성능 최적화 도구 초기화 (필요시 사용)
import "@/lib/registry/utils/performanceOptimizer";

interface ScreenDesignerProps {
  selectedScreen: ScreenDefinition | null;
  onBackToList: () => void;
  onScreenUpdate?: (updatedScreen: Partial<ScreenDefinition>) => void;
  // POP 모드 지원
  isPop?: boolean;
  defaultDevicePreview?: "mobile" | "tablet";
}

import { useLayerOptional, LayerProvider, createDefaultLayer } from "@/contexts/LayerContext";
import { LayerManagerPanel } from "./LayerManagerPanel";
import { LayerType, LayerDefinition } from "@/types/screen-management";

// 패널 설정 업데이트
const panelConfigs: PanelConfig[] = [
  {
    id: "v2",
    title: "패널",
    defaultPosition: "left",
    defaultWidth: 240,
    defaultHeight: 700,
    shortcutKey: "p",
  },
  {
    id: "layer",
    title: "레이어",
    defaultPosition: "right",
    defaultWidth: 240,
    defaultHeight: 500,
    shortcutKey: "l",
  },
];

export default function ScreenDesigner({ 
  selectedScreen, 
  onBackToList, 
  onScreenUpdate,
  isPop = false,
  defaultDevicePreview = "tablet"
}: ScreenDesignerProps) {
  // POP 모드 여부에 따른 API 분기
  const USE_POP_API = isPop;
  const [layout, setLayout] = useState<LayoutData>({
    components: [],
    gridSettings: {
      columns: 12,
      gap: 16,
      padding: 0,
      snapToGrid: true,
      showGrid: false, // 기본값 false로 변경
      gridColor: "#d1d5db",
      gridOpacity: 0.5,
    },
  });
  const [isSaving, setIsSaving] = useState(false);
  const [isGeneratingMultilang, setIsGeneratingMultilang] = useState(false);
  const [showMultilangSettingsModal, setShowMultilangSettingsModal] = useState(false);

  // 🆕 화면에 할당된 메뉴 OBJID
  const [menuObjid, setMenuObjid] = useState<number | undefined>(undefined);

  // 메뉴 할당 모달 상태
  const [showMenuAssignmentModal, setShowMenuAssignmentModal] = useState(false);

  // 단축키 도움말 모달 상태
  const [showShortcutsModal, setShowShortcutsModal] = useState(false);

  // 파일첨부 상세 모달 상태
  const [showFileAttachmentModal, setShowFileAttachmentModal] = useState(false);
  const [selectedFileComponent, setSelectedFileComponent] = useState<ComponentData | null>(null);

  // 해상도 설정 상태
  const [screenResolution, setScreenResolution] = useState<ScreenResolution>(
    SCREEN_RESOLUTIONS[0], // 기본값: Full HD
  );

  // 🆕 패널 상태 관리 (usePanelState 훅)
  const { panelStates, togglePanel, openPanel, closePanel, closeAllPanels, updatePanelPosition, updatePanelSize } =
    usePanelState(panelConfigs);

  const [selectedComponent, setSelectedComponent] = useState<ComponentData | null>(null);

  // 🆕 탭 내부 컴포넌트 선택 상태 (중첩 구조 지원)
  const [selectedTabComponentInfo, setSelectedTabComponentInfo] = useState<{
    tabsComponentId: string; // 탭 컴포넌트 ID
    tabId: string; // 탭 ID
    componentId: string; // 탭 내부 컴포넌트 ID
    component: any; // 탭 내부 컴포넌트 데이터
    // 🆕 중첩 구조용: 부모 분할 패널 정보
    parentSplitPanelId?: string | null;
    parentPanelSide?: "left" | "right" | null;
  } | null>(null);

  // 🆕 분할 패널 내부 컴포넌트 선택 상태
  const [selectedPanelComponentInfo, setSelectedPanelComponentInfo] = useState<{
    splitPanelId: string; // 분할 패널 컴포넌트 ID
    panelSide: "left" | "right"; // 좌측/우측 패널
    componentId: string; // 패널 내부 컴포넌트 ID
    component: any; // 패널 내부 컴포넌트 데이터
  } | null>(null);

  // 컴포넌트 선택 시 통합 패널 자동 열기
  const handleComponentSelect = useCallback(
    (component: ComponentData | null) => {
      setSelectedComponent(component);
      // 일반 컴포넌트 선택 시 탭 내부 컴포넌트/분할 패널 내부 컴포넌트 선택 해제
      if (component) {
        setSelectedTabComponentInfo(null);
        setSelectedPanelComponentInfo(null);
      }

      // 컴포넌트가 선택되면 통합 패널 자동 열기
      if (component) {
        openPanel("v2");
      }
    },
    [openPanel],
  );

  // 🆕 탭 내부 컴포넌트 선택 핸들러 (중첩 구조 지원)
  const handleSelectTabComponent = useCallback(
    (
      tabsComponentId: string,
      tabId: string,
      compId: string,
      comp: any,
      // 🆕 중첩 구조용: 부모 분할 패널 정보 (선택적)
      parentSplitPanelId?: string | null,
      parentPanelSide?: "left" | "right" | null,
    ) => {
      if (!compId) {
        // 탭 영역 빈 공간 클릭 시 선택 해제
        setSelectedTabComponentInfo(null);
        return;
      }

      setSelectedTabComponentInfo({
        tabsComponentId,
        tabId,
        componentId: compId,
        component: comp,
        parentSplitPanelId: parentSplitPanelId || null,
        parentPanelSide: parentPanelSide || null,
      });
      // 탭 내부 컴포넌트 선택 시 일반 컴포넌트/분할 패널 내부 컴포넌트 선택 해제
      setSelectedComponent(null);
      setSelectedPanelComponentInfo(null);
      openPanel("v2");
    },
    [openPanel],
  );

  // 🆕 분할 패널 내부 컴포넌트 선택 핸들러
  const handleSelectPanelComponent = useCallback(
    (splitPanelId: string, panelSide: "left" | "right", compId: string, comp: any) => {
      // 🐛 디버깅: 전달받은 comp 확인
      console.log("🐛 [handleSelectPanelComponent] comp:", {
        compId,
        componentType: comp?.componentType,
        selectedTable: comp?.componentConfig?.selectedTable,
        fieldMapping: comp?.componentConfig?.fieldMapping,
        fieldMappingKeys: comp?.componentConfig?.fieldMapping ? Object.keys(comp.componentConfig.fieldMapping) : [],
      });

      if (!compId) {
        // 패널 영역 빈 공간 클릭 시 선택 해제
        setSelectedPanelComponentInfo(null);
        return;
      }

      setSelectedPanelComponentInfo({
        splitPanelId,
        panelSide,
        componentId: compId,
        component: comp,
      });
      // 분할 패널 내부 컴포넌트 선택 시 일반 컴포넌트/탭 내부 컴포넌트 선택 해제
      setSelectedComponent(null);
      setSelectedTabComponentInfo(null);
      openPanel("v2");
    },
    [openPanel],
  );

  // 🆕 중첩된 탭 컴포넌트 선택 이벤트 리스너 (분할 패널 안의 탭 안의 컴포넌트)
  useEffect(() => {
    const handleNestedTabComponentSelect = (event: CustomEvent) => {
      const { tabsComponentId, tabId, componentId, component, parentSplitPanelId, parentPanelSide } = event.detail;

      if (!componentId) {
        setSelectedTabComponentInfo(null);
        return;
      }

      console.log("🎯 중첩된 탭 컴포넌트 선택:", event.detail);

      setSelectedTabComponentInfo({
        tabsComponentId,
        tabId,
        componentId,
        component,
        parentSplitPanelId,
        parentPanelSide,
      });
      setSelectedComponent(null);
      setSelectedPanelComponentInfo(null);
      openPanel("v2");
    };

    window.addEventListener("nested-tab-component-select", handleNestedTabComponentSelect as EventListener);

    return () => {
      window.removeEventListener("nested-tab-component-select", handleNestedTabComponentSelect as EventListener);
    };
  }, [openPanel]);

  // 클립보드 상태
  const [clipboard, setClipboard] = useState<ComponentData[]>([]);

  // 실행취소/다시실행을 위한 히스토리 상태
  const [history, setHistory] = useState<LayoutData[]>([]);
  const [historyIndex, setHistoryIndex] = useState(-1);

  // 그룹 상태
  const [groupState, setGroupState] = useState<GroupState>({
    selectedComponents: [],
    isGrouping: false,
  });

  // 드래그 상태
  const [dragState, setDragState] = useState({
    isDragging: false,
    draggedComponent: null as ComponentData | null,
    draggedComponents: [] as ComponentData[], // 다중 드래그를 위한 컴포넌트 배열
    originalPosition: { x: 0, y: 0, z: 1 },
    currentPosition: { x: 0, y: 0, z: 1 },
    grabOffset: { x: 0, y: 0 },
    justFinishedDrag: false, // 드래그 종료 직후 클릭 방지용
  });

  // Pan 모드 상태 (스페이스바 + 드래그)
  const [isPanMode, setIsPanMode] = useState(false);
  const [panState, setPanState] = useState({
    isPanning: false,
    startX: 0,
    startY: 0,
    outerScrollLeft: 0,
    outerScrollTop: 0,
    innerScrollLeft: 0,
    innerScrollTop: 0,
  });
  const canvasContainerRef = useRef<HTMLDivElement>(null);

  // Zoom 상태
  const [zoomLevel, setZoomLevel] = useState(1); // 1 = 100%
  const MIN_ZOOM = 0.1; // 10%
  const MAX_ZOOM = 3; // 300%
  const zoomRafRef = useRef<number | null>(null); // 줌 RAF throttle용

  // 전역 파일 상태 변경 시 강제 리렌더링을 위한 상태
  const [forceRenderTrigger, setForceRenderTrigger] = useState(0);

  // 파일 컴포넌트 데이터 복원 함수 (실제 DB에서 조회)
  const restoreFileComponentsData = useCallback(
    async (components: ComponentData[]) => {
      if (!selectedScreen?.screenId) return;

      // console.log("🔄 파일 컴포넌트 데이터 복원 시작:", components.length);

      try {
        // 실제 DB에서 화면의 모든 파일 정보 조회
        const fileResponse = await ScreenFileAPI.getScreenComponentFiles(selectedScreen.screenId);

        if (!fileResponse.success) {
          // console.warn("⚠️ 파일 정보 조회 실패:", fileResponse);
          return;
        }

        const { componentFiles } = fileResponse;

        if (typeof window !== "undefined") {
          // 전역 파일 상태 초기화
          const globalFileState: { [key: string]: any[] } = {};
          let restoredCount = 0;

          // DB에서 조회한 파일 정보를 전역 상태로 복원
          Object.keys(componentFiles).forEach((componentId) => {
            const files = componentFiles[componentId];
            if (files && files.length > 0) {
              globalFileState[componentId] = files;
              restoredCount++;

              // localStorage에도 백업
              const backupKey = `fileComponent_${componentId}_files`;
              localStorage.setItem(backupKey, JSON.stringify(files));

              console.log("📁 DB에서 파일 컴포넌트 데이터 복원:", {
                componentId: componentId,
                fileCount: files.length,
                files: files.map((f) => ({ objid: f.objid, name: f.realFileName })),
              });
            }
          });

          // 전역 상태 업데이트
          (window as any).globalFileState = globalFileState;

          // 모든 파일 컴포넌트에 복원 완료 이벤트 발생
          Object.keys(globalFileState).forEach((componentId) => {
            const files = globalFileState[componentId];
            const syncEvent = new CustomEvent("globalFileStateChanged", {
              detail: {
                componentId: componentId,
                files: files,
                fileCount: files.length,
                timestamp: Date.now(),
                isRestore: true,
              },
            });
            window.dispatchEvent(syncEvent);
          });

          if (restoredCount > 0) {
            toast.success(
              `${restoredCount}개 파일 컴포넌트 데이터가 DB에서 복원되었습니다. (총 ${fileResponse.totalFiles}개 파일)`,
            );
          }
        }
      } catch (error) {
        // console.error("❌ 파일 컴포넌트 데이터 복원 실패:", error);
        toast.error("파일 데이터 복원 중 오류가 발생했습니다.");
      }
    },
    [selectedScreen?.screenId],
  );

  // 드래그 선택 상태
  const [selectionDrag, setSelectionDrag] = useState({
    isSelecting: false,
    startPoint: { x: 0, y: 0, z: 1 },
    currentPoint: { x: 0, y: 0, z: 1 },
    wasSelecting: false, // 방금 전에 드래그 선택이 진행 중이었는지 추적
  });

  // 테이블 데이터
  const [tables, setTables] = useState<TableInfo[]>([]);
  const [searchTerm, setSearchTerm] = useState("");

  // 🆕 검색어로 필터링된 테이블 목록
  const filteredTables = useMemo(() => {
    if (!searchTerm.trim()) return tables;
    const term = searchTerm.toLowerCase();
    return tables.filter(
      (table) =>
        table.tableName.toLowerCase().includes(term) ||
        table.columns?.some((col) => col.columnName.toLowerCase().includes(term)),
    );
  }, [tables, searchTerm]);

  // 그룹 생성 다이얼로그
  const [showGroupCreateDialog, setShowGroupCreateDialog] = useState(false);

  const canvasRef = useRef<HTMLDivElement>(null);

  // 10px 격자 라인 생성 (시각적 가이드용)
  const gridLines = useMemo(() => {
    if (!layout.gridSettings?.showGrid) return [];

    const width = screenResolution.width;
    const height = screenResolution.height;
    const lines: Array<{ type: "vertical" | "horizontal"; position: number }> = [];

    // 10px 단위로 격자 라인 생성
    for (let x = 0; x <= width; x += 10) {
      lines.push({ type: "vertical", position: x });
    }
    for (let y = 0; y <= height; y += 10) {
      lines.push({ type: "horizontal", position: y });
    }

    return lines;
  }, [layout.gridSettings?.showGrid, screenResolution.width, screenResolution.height]);

  // 🆕 현재 편집 중인 레이어 ID (DB의 layer_id, 1 = 기본 레이어)
  const [activeLayerId, setActiveLayerIdLocal] = useState<number>(1);
  const activeLayerIdRef = useRef<number>(1);
  const setActiveLayerIdWithRef = useCallback((id: number) => {
    setActiveLayerIdLocal(id);
    activeLayerIdRef.current = id;
  }, []);

  // 🆕 좌측 패널 탭 상태 관리
  const [leftPanelTab, setLeftPanelTab] = useState<string>("components");

  // 🆕 조건부 영역(Zone) 목록 (DB screen_conditional_zones 기반)
  const [zones, setZones] = useState<import("@/types/screen-management").ConditionalZone[]>([]);

  // 🆕 조건부 영역 드래그 상태 (캔버스에서 드래그로 영역 설정)
  const [regionDrag, setRegionDrag] = useState<{
    isDrawing: boolean; // 새 영역 그리기 모드
    isDragging: boolean; // 기존 영역 이동 모드
    isResizing: boolean; // 기존 영역 리사이즈 모드
    targetLayerId: string | null; // 대상 Zone ID (문자열)
    startX: number;
    startY: number;
    currentX: number;
    currentY: number;
    resizeHandle: string | null; // 리사이즈 핸들 위치
    originalRegion: { x: number; y: number; width: number; height: number } | null;
  }>({
    isDrawing: false,
    isDragging: false,
    isResizing: false,
    targetLayerId: null,
    startX: 0,
    startY: 0,
    currentX: 0,
    currentY: 0,
    resizeHandle: null,
    originalRegion: null,
  });

  // 현재 활성 레이어의 Zone 정보 (캔버스 크기 결정용)
  const [activeLayerZone, setActiveLayerZone] = useState<import("@/types/screen-management").ConditionalZone | null>(null);

  // 다른 레이어의 컴포넌트 메타 정보 캐시 (데이터 전달 타겟 선택용)
  const [otherLayerComponents, setOtherLayerComponents] = useState<ComponentData[]>([]);

  // 🆕 activeLayerId 변경 시 해당 레이어의 Zone 찾기
  useEffect(() => {
    if (activeLayerId <= 1 || !selectedScreen?.screenId) {
      setActiveLayerZone(null);
      return;
    }
    // 레이어의 condition_config에서 zone_id를 가져와서 zones에서 찾기
    const findZone = async () => {
      try {
        const layerData = await screenApi.getLayerLayout(selectedScreen.screenId, activeLayerId);
        const zoneId = layerData?.conditionConfig?.zone_id;
        if (zoneId) {
          const zone = zones.find(z => z.zone_id === zoneId);
          setActiveLayerZone(zone || null);
        } else {
          setActiveLayerZone(null);
        }
      } catch {
        setActiveLayerZone(null);
      }
    };
    findZone();
  }, [activeLayerId, selectedScreen?.screenId, zones]);

  // 다른 레이어의 컴포넌트 메타 정보 로드 (데이터 전달 타겟 선택용)
  useEffect(() => {
    if (!selectedScreen?.screenId) return;
    const loadOtherLayerComponents = async () => {
      try {
        const allLayers = await screenApi.getScreenLayers(selectedScreen.screenId);
        const currentLayerId = activeLayerIdRef.current || 1;
        const otherLayers = allLayers.filter((l: any) => l.layer_id !== currentLayerId && l.layer_id > 0);

        const components: ComponentData[] = [];
        for (const layerInfo of otherLayers) {
          try {
            const layerData = await screenApi.getLayerLayout(selectedScreen.screenId, layerInfo.layer_id);
            const rawComps = layerData?.components;
            if (rawComps && Array.isArray(rawComps)) {
              for (const comp of rawComps) {
                components.push({
                  ...comp,
                  _layerName: layerInfo.layer_name || `레이어 ${layerInfo.layer_id}`,
                  _layerId: String(layerInfo.layer_id),
                } as any);
              }
            }
          } catch {
            // 개별 레이어 로드 실패 무시
          }
        }
        setOtherLayerComponents(components);
      } catch {
        setOtherLayerComponents([]);
      }
    };
    loadOtherLayerComponents();
  }, [selectedScreen?.screenId, activeLayerId]);

  // 캔버스에 렌더링할 컴포넌트 (DB 기반 레이어: 각 레이어별로 별도 로드되므로 전체 표시)
  const visibleComponents = useMemo(() => {
    return layout.components;
  }, [layout.components]);

  // 이미 배치된 컬럼 목록 계산
  const placedColumns = useMemo(() => {
    const placed = new Set<string>();
    // 🔧 화면의 메인 테이블명을 fallback으로 사용
    const screenTableName = selectedScreen?.tableName;

    const collectColumns = (components: ComponentData[]) => {
      components.forEach((comp) => {
        const anyComp = comp as any;

        // 🔧 tableName과 columnName을 여러 위치에서 찾기 (최상위, componentConfig, 또는 화면 테이블명)
        const tableName = anyComp.tableName || anyComp.componentConfig?.tableName || screenTableName;
        const columnName = anyComp.columnName || anyComp.componentConfig?.columnName;

        // widget 타입 또는 component 타입에서 columnName 확인 (tableName은 화면 테이블명으로 fallback)
        if ((comp.type === "widget" || comp.type === "component") && tableName && columnName) {
          const key = `${tableName}.${columnName}`;
          placed.add(key);
        }

        // 자식 컴포넌트도 확인 (재귀)
        if (comp.children && comp.children.length > 0) {
          collectColumns(comp.children);
        }
      });
    };

    collectColumns(layout.components);
    return placed;
  }, [layout.components, selectedScreen?.tableName]);

  // 히스토리에 저장
  const saveToHistory = useCallback(
    (newLayout: LayoutData) => {
      setHistory((prev) => {
        const newHistory = prev.slice(0, historyIndex + 1);
        newHistory.push(newLayout);
        return newHistory.slice(-50); // 최대 50개까지만 저장
      });
      setHistoryIndex((prev) => Math.min(prev + 1, 49));
    },
    [historyIndex],
  );

  // 🆕 탭 내부 컴포넌트 설정 업데이트 핸들러 (중첩 구조 지원)
  const handleUpdateTabComponentConfig = useCallback(
    (path: string, value: any) => {
      if (!selectedTabComponentInfo) return;

      const { tabsComponentId, tabId, componentId, parentSplitPanelId, parentPanelSide } = selectedTabComponentInfo;

      // 탭 컴포넌트 업데이트 함수 (재사용)
      const updateTabsComponent = (tabsComponent: any) => {
        const currentConfig = tabsComponent.componentConfig || {};
        const tabs = currentConfig.tabs || [];

        const updatedTabs = tabs.map((tab: any) => {
          if (tab.id === tabId) {
            return {
              ...tab,
              components: (tab.components || []).map((comp: any) => {
                if (comp.id === componentId) {
                  if (path.startsWith("componentConfig.")) {
                    const configPath = path.replace("componentConfig.", "");
                    return {
                      ...comp,
                      componentConfig: { ...comp.componentConfig, [configPath]: value },
                    };
                  } else if (path.startsWith("style.")) {
                    const stylePath = path.replace("style.", "");
                    return { ...comp, style: { ...comp.style, [stylePath]: value } };
                  } else if (path.startsWith("size.")) {
                    const sizePath = path.replace("size.", "");
                    return { ...comp, size: { ...comp.size, [sizePath]: value } };
                  } else {
                    return { ...comp, [path]: value };
                  }
                }
                return comp;
              }),
            };
          }
          return tab;
        });

        return { ...tabsComponent, componentConfig: { ...currentConfig, tabs: updatedTabs } };
      };

      setLayout((prevLayout) => {
        let newLayout;
        let updatedTabs;

        if (parentSplitPanelId && parentPanelSide) {
          // 🆕 중첩 구조: 분할 패널 안의 탭 업데이트
          newLayout = {
            ...prevLayout,
            components: prevLayout.components.map((c) => {
              if (c.id === parentSplitPanelId) {
                const splitConfig = (c as any).componentConfig || {};
                const panelKey = parentPanelSide === "left" ? "leftPanel" : "rightPanel";
                const panelConfig = splitConfig[panelKey] || {};
                const panelComponents = panelConfig.components || [];

                const tabsComponent = panelComponents.find((pc: any) => pc.id === tabsComponentId);
                if (!tabsComponent) return c;

                const updatedTabsComponent = updateTabsComponent(tabsComponent);
                updatedTabs = updatedTabsComponent.componentConfig.tabs;

                return {
                  ...c,
                  componentConfig: {
                    ...splitConfig,
                    [panelKey]: {
                      ...panelConfig,
                      components: panelComponents.map((pc: any) =>
                        pc.id === tabsComponentId ? updatedTabsComponent : pc,
                      ),
                    },
                  },
                };
              }
              return c;
            }),
          };
        } else {
          // 일반 구조: 최상위 탭 업데이트
          const tabsComponent = prevLayout.components.find((c) => c.id === tabsComponentId);
          if (!tabsComponent) return prevLayout;

          const updatedTabsComponent = updateTabsComponent(tabsComponent);
          updatedTabs = updatedTabsComponent.componentConfig.tabs;

          newLayout = {
            ...prevLayout,
            components: prevLayout.components.map((c) => (c.id === tabsComponentId ? updatedTabsComponent : c)),
          };
        }

        // 선택된 컴포넌트 정보도 업데이트
        if (updatedTabs) {
          const updatedComp = updatedTabs
            .find((t: any) => t.id === tabId)
            ?.components?.find((c: any) => c.id === componentId);
          if (updatedComp) {
            setSelectedTabComponentInfo((prev) => (prev ? { ...prev, component: updatedComp } : null));
          }
        }

        return newLayout;
      });
    },
    [selectedTabComponentInfo],
  );

  // 실행취소
  const undo = useCallback(() => {
    setHistoryIndex((prevIndex) => {
      if (prevIndex > 0) {
        const newIndex = prevIndex - 1;
        setHistory((prevHistory) => {
          if (prevHistory[newIndex]) {
            setLayout(prevHistory[newIndex]);
          }
          return prevHistory;
        });
        return newIndex;
      }
      return prevIndex;
    });
  }, []);

  // 다시실행
  const redo = useCallback(() => {
    setHistoryIndex((prevIndex) => {
      let newIndex = prevIndex;
      setHistory((prevHistory) => {
        if (prevIndex < prevHistory.length - 1) {
          newIndex = prevIndex + 1;
          if (prevHistory[newIndex]) {
            setLayout(prevHistory[newIndex]);
          }
        }
        return prevHistory;
      });
      return newIndex;
    });
  }, []);

  // 컴포넌트 속성 업데이트
  const updateComponentProperty = useCallback(
    (componentId: string, path: string, value: any) => {
      // 🔥 함수형 업데이트로 변경하여 최신 layout 사용
      setLayout((prevLayout) => {
        const targetComponent = prevLayout.components.find((comp) => comp.id === componentId);
        const isLayoutComponent = targetComponent?.type === "layout";

        // 🆕 그룹 설정 변경 시 같은 그룹의 모든 버튼에 일괄 적용
        const isGroupSetting = path === "webTypeConfig.flowVisibilityConfig.groupAlign";

        let affectedComponents: string[] = [componentId]; // 기본적으로 현재 컴포넌트만

        if (isGroupSetting && targetComponent) {
          const flowConfig = (targetComponent as any).webTypeConfig?.flowVisibilityConfig;
          const currentGroupId = flowConfig?.groupId;

          if (currentGroupId) {
            // 같은 그룹의 모든 버튼 찾기
            affectedComponents = prevLayout.components
              .filter((comp) => {
                const compConfig = (comp as any).webTypeConfig?.flowVisibilityConfig;
                return compConfig?.groupId === currentGroupId && compConfig?.enabled;
              })
              .map((comp) => comp.id);

            console.log("🔄 그룹 설정 일괄 적용:", {
              groupId: currentGroupId,
              setting: path.split(".").pop(),
              value,
              affectedButtons: affectedComponents,
            });
          }
        }

        // 레이아웃 컴포넌트의 위치가 변경되는 경우 존에 속한 컴포넌트들도 함께 이동
        const positionDelta = { x: 0, y: 0 };
        if (isLayoutComponent && (path === "position.x" || path === "position.y" || path === "position")) {
          const oldPosition = targetComponent.position;
          let newPosition = { ...oldPosition };

          if (path === "position.x") {
            newPosition.x = value;
            positionDelta.x = value - oldPosition.x;
          } else if (path === "position.y") {
            newPosition.y = value;
            positionDelta.y = value - oldPosition.y;
          } else if (path === "position") {
            newPosition = value;
            positionDelta.x = value.x - oldPosition.x;
            positionDelta.y = value.y - oldPosition.y;
          }

          console.log("📐 레이아웃 이동 감지:", {
            layoutId: componentId,
            oldPosition,
            newPosition,
            positionDelta,
          });
        }

        const pathParts = path.split(".");
        const updatedComponents = prevLayout.components.map((comp) => {
          // 🆕 그룹 설정이면 같은 그룹의 모든 버튼에 적용
          const shouldUpdate = isGroupSetting ? affectedComponents.includes(comp.id) : comp.id === componentId;

          if (!shouldUpdate) {
            // 레이아웃 이동 시 존에 속한 컴포넌트들도 함께 이동
            if (isLayoutComponent && (positionDelta.x !== 0 || positionDelta.y !== 0)) {
              // 이 레이아웃의 존에 속한 컴포넌트인지 확인
              const isInLayoutZone = comp.parentId === componentId && comp.zoneId;
              if (isInLayoutZone) {
                console.log("🔄 존 컴포넌트 함께 이동:", {
                  componentId: comp.id,
                  zoneId: comp.zoneId,
                  oldPosition: comp.position,
                  delta: positionDelta,
                });

                return {
                  ...comp,
                  position: {
                    ...comp.position,
                    x: comp.position.x + positionDelta.x,
                    y: comp.position.y + positionDelta.y,
                  },
                };
              }
            }
            return comp;
          }

          // 중첩 경로를 고려한 안전한 복사
          const newComp = { ...comp };

          // 경로를 따라 내려가면서 각 레벨을 새 객체로 복사
          let current: any = newComp;
          for (let i = 0; i < pathParts.length - 1; i++) {
            const key = pathParts[i];

            // 다음 레벨이 없거나 객체가 아니면 새 객체 생성
            if (!current[key] || typeof current[key] !== "object" || Array.isArray(current[key])) {
              current[key] = {};
            } else {
              // 기존 객체를 복사하여 불변성 유지
              current[key] = { ...current[key] };
            }
            current = current[key];
          }

          // 최종 값 설정
          const finalKey = pathParts[pathParts.length - 1];
          current[finalKey] = value;

          // 🔧 style 관련 업데이트 디버그 로그
          if (path.includes("style") || path.includes("labelDisplay")) {
            console.log("🎨 style 업데이트 제대로 렌더링된거다 내가바꿈:", {
              componentId: comp.id,
              path,
              value,
              updatedStyle: newComp.style,
              pathIncludesLabelDisplay: path.includes("labelDisplay"),
            });
          }

          // 🆕 labelDisplay 변경 시 강제 리렌더링 트리거 (조건문 밖으로 이동)
          if (path === "style.labelDisplay") {
            console.log("⏰⏰⏰ labelDisplay 변경 감지! forceRenderTrigger 실행 예정");
          }

          // 🆕 size 변경 시 style도 함께 업데이트 (파란 테두리와 실제 크기 동기화)
          if (path === "size.width" || path === "size.height" || path === "size") {
            // 🔧 style 객체를 새로 복사하여 불변성 유지
            newComp.style = { ...(newComp.style || {}) };

            if (path === "size.width") {
              newComp.style.width = `${value}px`;
            } else if (path === "size.height") {
              newComp.style.height = `${value}px`;
            } else if (path === "size") {
              // size 객체 전체가 변경된 경우
              if (value.width !== undefined) {
                newComp.style.width = `${value.width}px`;
              }
              if (value.height !== undefined) {
                newComp.style.height = `${value.height}px`;
              }
            }

            console.log("🔄 size 변경 → style 동기화:", {
              componentId: newComp.id,
              path,
              value,
              updatedStyle: newComp.style,
            });
          }

          // gridColumns 변경 시 크기 자동 업데이트 제거 (격자 시스템 제거됨)
          // if (path === "gridColumns" && prevLayout.gridSettings) {
          //   const updatedSize = updateSizeFromGridColumns(newComp, prevLayout.gridSettings as GridUtilSettings);
          //   newComp.size = updatedSize;
          // }

          // 크기 변경 시 격자 스냅 적용 제거 (직접 입력 시 불필요)
          // 드래그/리사이즈 시에는 별도 로직에서 처리됨
          // if (
          //   (path === "size.width" || path === "size.height") &&
          //   prevLayout.gridSettings?.snapToGrid &&
          //   newComp.type !== "group"
          // ) {
          //   const currentGridInfo = calculateGridInfo(screenResolution.width, screenResolution.height, {
          //     columns: prevLayout.gridSettings.columns,
          //     gap: prevLayout.gridSettings.gap,
          //     padding: prevLayout.gridSettings.padding,
          //     snapToGrid: prevLayout.gridSettings.snapToGrid || false,
          //   });
          //   const snappedSize = snapSizeToGrid(
          //     newComp.size,
          //     currentGridInfo,
          //     prevLayout.gridSettings as GridUtilSettings,
          //   );
          //   newComp.size = snappedSize;
          //
          //   const adjustedColumns = adjustGridColumnsFromSize(
          //     newComp,
          //     currentGridInfo,
          //     prevLayout.gridSettings as GridUtilSettings,
          //   );
          //   if (newComp.gridColumns !== adjustedColumns) {
          //     newComp.gridColumns = adjustedColumns;
          //   }
          // }

          // gridColumns 변경 시 크기를 격자에 맞게 자동 조정 제거 (격자 시스템 제거됨)
          // if (path === "gridColumns" && prevLayout.gridSettings?.snapToGrid && newComp.type !== "group") {
          //   const currentGridInfo = calculateGridInfo(screenResolution.width, screenResolution.height, {
          //     columns: prevLayout.gridSettings.columns,
          //     gap: prevLayout.gridSettings.gap,
          //     padding: prevLayout.gridSettings.padding,
          //     snapToGrid: prevLayout.gridSettings.snapToGrid || false,
          //   });
          //
          //   const newWidth = calculateWidthFromColumns(
          //     newComp.gridColumns,
          //     currentGridInfo,
          //     prevLayout.gridSettings as GridUtilSettings,
          //   );
          //   newComp.size = {
          //     ...newComp.size,
          //     width: newWidth,
          //   };
          // }

          // 위치 변경 시 격자 스냅 적용 (그룹 내부 컴포넌트 포함)
          if (
            (path === "position.x" || path === "position.y" || path === "position") &&
            layout.gridSettings?.snapToGrid
          ) {
            // 현재 해상도에 맞는 격자 정보 계산
            const currentGridInfo = calculateGridInfo(screenResolution.width, screenResolution.height, {
              columns: layout.gridSettings.columns,
              gap: layout.gridSettings.gap,
              padding: layout.gridSettings.padding,
              snapToGrid: layout.gridSettings.snapToGrid || false,
            });

            // 그룹 내부 컴포넌트인 경우 패딩을 고려한 격자 스냅 적용
            if (newComp.parentId && currentGridInfo) {
              const { columnWidth } = currentGridInfo;
              const { gap } = layout.gridSettings;

              // 그룹 내부 패딩 고려한 격자 정렬
              const padding = 16;
              const effectiveX = newComp.position.x - padding;
              const columnIndex = Math.round(effectiveX / (columnWidth + (gap || 16)));
              const snappedX = padding + columnIndex * (columnWidth + (gap || 16));

              // Y 좌표는 10px 단위로 스냅
              const effectiveY = newComp.position.y - padding;
              const rowIndex = Math.round(effectiveY / 10);
              const snappedY = padding + rowIndex * 10;

              // 크기도 외부 격자와 동일하게 스냅
              const fullColumnWidth = columnWidth + (gap || 16); // 외부 격자와 동일한 크기
              const widthInColumns = Math.max(1, Math.round(newComp.size.width / fullColumnWidth));
              const snappedWidth = widthInColumns * fullColumnWidth - (gap || 16); // gap 제거하여 실제 컴포넌트 크기
              // 높이는 사용자가 입력한 값 그대로 사용 (스냅 제거)
              const snappedHeight = Math.max(10, newComp.size.height);

              newComp.position = {
                x: Math.max(padding, snappedX), // 패딩만큼 최소 여백 확보
                y: Math.max(padding, snappedY),
                z: newComp.position.z || 1,
              };

              newComp.size = {
                width: snappedWidth,
                height: snappedHeight,
              };
            } else if (newComp.type !== "group") {
              // 그룹이 아닌 일반 컴포넌트만 격자 스냅 적용
              const snappedPosition = snapPositionTo10px(
                newComp.position,
                currentGridInfo,
                layout.gridSettings as GridUtilSettings,
              );
              newComp.position = snappedPosition;
            }
          }

          return newComp;
        });

        // 🔥 새로운 layout 생성
        const newLayout = { ...prevLayout, components: updatedComponents };

        saveToHistory(newLayout);

        // selectedComponent가 업데이트된 컴포넌트와 같다면 selectedComponent도 업데이트
        setSelectedComponent((prevSelected) => {
          if (prevSelected && prevSelected.id === componentId) {
            const updatedSelectedComponent = updatedComponents.find((c) => c.id === componentId);
            if (updatedSelectedComponent) {
              // 🔧 완전히 새로운 객체를 만들어서 React가 변경을 감지하도록 함
              const newSelectedComponent = JSON.parse(JSON.stringify(updatedSelectedComponent));
              return newSelectedComponent;
            }
          }
          return prevSelected;
        });

        // webTypeConfig 업데이트 후 레이아웃 상태 확인
        if (path === "webTypeConfig") {
          const updatedComponent = newLayout.components.find((c) => c.id === componentId);
          console.log("🔄 레이아웃 업데이트 후 컴포넌트 상태:", {
            componentId,
            updatedComponent: updatedComponent
              ? {
                  id: updatedComponent.id,
                  type: updatedComponent.type,
                  webTypeConfig: updatedComponent.type === "widget" ? (updatedComponent as any).webTypeConfig : null,
                }
              : null,
            layoutComponentsCount: newLayout.components.length,
            timestamp: new Date().toISOString(),
          });
        }

        return newLayout;
      });
    },
    [saveToHistory],
  );

  // 컴포넌트 시스템 초기화
  useEffect(() => {
    const initComponents = async () => {
      try {
        // console.log("🚀 컴포넌트 시스템 초기화 시작...");
        await initializeComponents();
        // console.log("✅ 컴포넌트 시스템 초기화 완료");
      } catch (error) {
        // console.error("❌ 컴포넌트 시스템 초기화 실패:", error);
      }
    };

    initComponents();
  }, []);

  // 화면 선택 시 파일 복원
  useEffect(() => {
    if (selectedScreen?.screenId) {
      restoreScreenFiles();
    }
  }, [selectedScreen?.screenId]);

  // 화면의 모든 파일 컴포넌트 파일 복원
  const restoreScreenFiles = useCallback(async () => {
    if (!selectedScreen?.screenId) return;

    try {
      // console.log("🔄 화면 파일 복원 시작:", selectedScreen.screenId);

      // 해당 화면의 모든 파일 조회
      const response = await ScreenFileAPI.getScreenComponentFiles(selectedScreen.screenId);

      if (response.success && response.componentFiles) {
        // console.log("📁 복원할 파일 데이터:", response.componentFiles);

        // 각 컴포넌트별로 파일 복원 (전역 상태와 localStorage 우선 적용)
        Object.entries(response.componentFiles).forEach(([componentId, serverFiles]) => {
          if (Array.isArray(serverFiles) && serverFiles.length > 0) {
            // 🎯 전역 상태와 localStorage에서 현재 파일 상태 확인
            const globalFileState = typeof window !== "undefined" ? (window as any).globalFileState || {} : {};
            const currentGlobalFiles = globalFileState[componentId] || [];

            let currentLocalStorageFiles: any[] = [];
            if (typeof window !== "undefined") {
              try {
                const storedFiles = localStorage.getItem(`fileComponent_${componentId}_files`);
                if (storedFiles) {
                  currentLocalStorageFiles = JSON.parse(storedFiles);
                }
              } catch (e) {
                // console.warn("localStorage 파일 파싱 실패:", e);
              }
            }

            // 🎯 우선순위: 전역 상태 > localStorage > 서버 데이터
            let finalFiles = serverFiles;
            if (currentGlobalFiles.length > 0) {
              finalFiles = currentGlobalFiles;
              // console.log(`📂 컴포넌트 ${componentId} 전역 상태 우선 적용:`, finalFiles.length, "개");
            } else if (currentLocalStorageFiles.length > 0) {
              finalFiles = currentLocalStorageFiles;
              // console.log(`📂 컴포넌트 ${componentId} localStorage 우선 적용:`, finalFiles.length, "개");
            } else {
              // console.log(`📂 컴포넌트 ${componentId} 서버 데이터 적용:`, finalFiles.length, "개");
            }

            // 전역 상태에 파일 저장
            globalFileState[componentId] = finalFiles;
            if (typeof window !== "undefined") {
              (window as any).globalFileState = globalFileState;
            }

            // localStorage에도 백업
            if (typeof window !== "undefined") {
              localStorage.setItem(`fileComponent_${componentId}_files`, JSON.stringify(finalFiles));
            }
          }
        });

        // 레이아웃의 컴포넌트들에 파일 정보 적용 (전역 상태 우선)
        setLayout((prevLayout) => {
          const updatedComponents = prevLayout.components.map((comp) => {
            // 🎯 전역 상태에서 최신 파일 정보 가져오기
            const globalFileState = typeof window !== "undefined" ? (window as any).globalFileState || {} : {};
            const finalFiles = globalFileState[comp.id] || [];

            if (finalFiles.length > 0) {
              return {
                ...comp,
                uploadedFiles: finalFiles,
                lastFileUpdate: Date.now(),
              };
            }
            return comp;
          });

          return {
            ...prevLayout,
            components: updatedComponents,
          };
        });

        // console.log("✅ 화면 파일 복원 완료");
      }
    } catch (error) {
      // console.error("❌ 화면 파일 복원 오류:", error);
    }
  }, [selectedScreen?.screenId]);

  // 전역 파일 상태 변경 이벤트 리스너
  useEffect(() => {
    const handleGlobalFileStateChange = (event: CustomEvent) => {
      // console.log("🔄 ScreenDesigner: 전역 파일 상태 변경 감지", event.detail);
      setForceRenderTrigger((prev) => prev + 1);
    };

    if (typeof window !== "undefined") {
      window.addEventListener("globalFileStateChanged", handleGlobalFileStateChange as EventListener);

      return () => {
        window.removeEventListener("globalFileStateChanged", handleGlobalFileStateChange as EventListener);
      };
    }
  }, []);

  // 화면의 기본 테이블/REST API 정보 로드
  useEffect(() => {
    const loadScreenDataSource = async () => {
      console.log("🔍 [ScreenDesigner] 데이터 소스 로드 시작:", {
        screenId: selectedScreen?.screenId,
        screenName: selectedScreen?.screenName,
        dataSourceType: selectedScreen?.dataSourceType,
        tableName: selectedScreen?.tableName,
        restApiConnectionId: selectedScreen?.restApiConnectionId,
        restApiEndpoint: selectedScreen?.restApiEndpoint,
        restApiJsonPath: selectedScreen?.restApiJsonPath,
        // 전체 selectedScreen 객체도 출력
        fullScreen: selectedScreen,
      });

      // REST API 데이터 소스인 경우
      // 1. dataSourceType이 "restapi"인 경우
      // 2. tableName이 restapi_ 또는 _restapi_로 시작하는 경우
      // 3. restApiConnectionId가 있는 경우
      const isRestApi =
        selectedScreen?.dataSourceType === "restapi" ||
        selectedScreen?.tableName?.startsWith("restapi_") ||
        selectedScreen?.tableName?.startsWith("_restapi_") ||
        !!selectedScreen?.restApiConnectionId;

      console.log("🔍 [ScreenDesigner] REST API 여부:", { isRestApi });

      if (isRestApi && (selectedScreen?.restApiConnectionId || selectedScreen?.tableName)) {
        try {
          // 연결 ID 추출 (restApiConnectionId가 없으면 tableName에서 추출)
          let connectionId = selectedScreen?.restApiConnectionId;
          if (!connectionId && selectedScreen?.tableName) {
            const match = selectedScreen.tableName.match(/restapi_(\d+)/);
            connectionId = match ? parseInt(match[1]) : undefined;
          }

          if (!connectionId) {
            throw new Error("REST API 연결 ID를 찾을 수 없습니다.");
          }

          console.log("🌐 [ScreenDesigner] REST API 데이터 로드:", { connectionId });

          const restApiData = await ExternalRestApiConnectionAPI.fetchData(
            connectionId,
            selectedScreen?.restApiEndpoint,
            selectedScreen?.restApiJsonPath || "response", // 기본값을 response로 변경
          );

          // REST API 응답에서 컬럼 정보 생성
          const columns: ColumnInfo[] = restApiData.columns.map((col) => ({
            tableName: `restapi_${connectionId}`,
            columnName: col.columnName,
            columnLabel: col.columnLabel,
            dataType: col.dataType === "string" ? "varchar" : col.dataType === "number" ? "numeric" : col.dataType,
            webType: col.dataType === "number" ? "number" : "text",
            input_type: "text",
            widgetType: col.dataType === "number" ? "number" : "text",
            isNullable: "YES",
            required: false,
          }));

          const tableInfo: TableInfo = {
            tableName: `restapi_${connectionId}`,
            tableLabel: restApiData.connectionInfo.connectionName || "REST API 데이터",
            columns,
          };

          console.log("✅ [ScreenDesigner] REST API 컬럼 로드 완료:", {
            tableName: tableInfo.tableName,
            tableLabel: tableInfo.tableLabel,
            columnsCount: columns.length,
            columns: columns.map((c) => c.columnName),
          });

          setTables([tableInfo]);
          console.log("REST API 데이터 소스 로드 완료:", {
            connectionName: restApiData.connectionInfo.connectionName,
            columnsCount: columns.length,
            rowsCount: restApiData.total,
          });
        } catch (error) {
          console.error("REST API 데이터 소스 로드 실패:", error);
          toast.error("REST API 데이터를 불러오는데 실패했습니다.");
          setTables([]);
        }
        return;
      }

      // 데이터베이스 데이터 소스인 경우 (기존 로직)
      const tableName = selectedScreen?.tableName;
      if (!tableName) {
        setTables([]);
        return;
      }

      try {
        // 테이블 라벨 조회
        const tableListResponse = await tableManagementApi.getTableList();
        const currentTable =
          tableListResponse.success && tableListResponse.data
            ? tableListResponse.data.find((t) => t.tableName === tableName)
            : null;
        const tableLabel = currentTable?.displayName || tableName;

        // 현재 화면의 테이블 컬럼 정보 조회 (캐시 버스팅으로 최신 데이터 가져오기)
        const columnsResponse = await tableTypeApi.getColumns(tableName, true);

        const columns: ColumnInfo[] = (columnsResponse || []).map((col: any) => {
          // widgetType 결정: inputType(entity 등) > webType > widget_type
          const inputType = col.inputType || col.input_type;
          const widgetType = inputType || col.widgetType || col.widget_type || col.webType || col.web_type;

          // detailSettings 파싱 (문자열이면 JSON 파싱)
          let detailSettings = col.detailSettings || col.detail_settings;
          if (typeof detailSettings === "string") {
            // JSON 형식인 경우에만 파싱 시도 (중괄호로 시작하는 경우)
            if (detailSettings.trim().startsWith("{")) {
              try {
                detailSettings = JSON.parse(detailSettings);
              } catch (e) {
                console.warn("detailSettings 파싱 실패:", e);
                detailSettings = {};
              }
            } else {
              // JSON이 아닌 일반 문자열인 경우 빈 객체로 처리
              detailSettings = {};
            }
          }

          // 엔티티 타입 디버깅
          if (inputType === "entity" || widgetType === "entity") {
            console.log("🔍 엔티티 컬럼 감지:", {
              columnName: col.columnName || col.column_name,
              inputType,
              widgetType,
              detailSettings,
              referenceTable: detailSettings?.referenceTable || col.referenceTable || col.reference_table,
            });
          }

          return {
            tableName: col.tableName || tableName,
            columnName: col.columnName || col.column_name,
            columnLabel: col.displayName || col.columnLabel || col.column_label || col.columnName || col.column_name,
            dataType: col.dataType || col.data_type || col.dbType,
            webType: col.webType || col.web_type,
            input_type: inputType,
            inputType: inputType,
            widgetType,
            isNullable: col.isNullable || col.is_nullable,
            required: col.required !== undefined ? col.required : col.isNullable === "NO" || col.is_nullable === "NO",
            columnDefault: col.columnDefault || col.column_default,
            characterMaximumLength: col.characterMaximumLength || col.character_maximum_length,
            codeCategory: col.codeCategory || col.code_category,
            codeValue: col.codeValue || col.code_value,
            // 엔티티 타입용 참조 테이블 정보 (detailSettings에서 추출)
            referenceTable: detailSettings?.referenceTable || col.referenceTable || col.reference_table,
            referenceColumn: detailSettings?.referenceColumn || col.referenceColumn || col.reference_column,
            displayColumn: detailSettings?.displayColumn || col.displayColumn || col.display_column,
            // detailSettings 전체 보존 (V2 컴포넌트용)
            detailSettings,
          };
        });

        const tableInfo: TableInfo = {
          tableName,
          tableLabel,
          columns,
        };

        setTables([tableInfo]); // 현재 화면의 테이블만 저장 (원래대로)
      } catch (error) {
        console.error("화면 테이블 정보 로드 실패:", error);
        setTables([]);
      }
    };

    loadScreenDataSource();
  }, [
    selectedScreen?.tableName,
    selectedScreen?.screenName,
    selectedScreen?.dataSourceType,
    selectedScreen?.restApiConnectionId,
    selectedScreen?.restApiEndpoint,
    selectedScreen?.restApiJsonPath,
  ]);

  // 테이블 선택 핸들러 - 사이드바에서 테이블 선택 시 호출
  const handleTableSelect = useCallback(
    async (tableName: string) => {
      console.log("📊 테이블 선택:", tableName);

      try {
        // 테이블 라벨 조회
        const tableListResponse = await tableManagementApi.getTableList();
        const currentTable =
          tableListResponse.success && tableListResponse.data
            ? tableListResponse.data.find((t: any) => (t.tableName || t.table_name) === tableName)
            : null;
        const tableLabel = currentTable?.displayName || currentTable?.table_label || tableName;

        // 테이블 컬럼 정보 조회
        const columnsResponse = await tableTypeApi.getColumns(tableName, true);

        const columns: ColumnInfo[] = (columnsResponse || []).map((col: any) => {
          const inputType = col.inputType || col.input_type;
          const widgetType = inputType || col.widgetType || col.widget_type || col.webType || col.web_type;

          let detailSettings = col.detailSettings || col.detail_settings;
          if (typeof detailSettings === "string") {
            try {
              detailSettings = JSON.parse(detailSettings);
            } catch (e) {
              detailSettings = {};
            }
          }

          return {
            tableName: col.tableName || tableName,
            columnName: col.columnName || col.column_name,
            columnLabel: col.displayName || col.columnLabel || col.column_label || col.columnName || col.column_name,
            dataType: col.dataType || col.data_type || col.dbType,
            webType: col.webType || col.web_type,
            input_type: inputType,
            inputType: inputType,
            widgetType,
            isNullable: col.isNullable || col.is_nullable,
            required: col.required !== undefined ? col.required : col.isNullable === "NO" || col.is_nullable === "NO",
            columnDefault: col.columnDefault || col.column_default,
            characterMaximumLength: col.characterMaximumLength || col.character_maximum_length,
            codeCategory: col.codeCategory || col.code_category,
            codeValue: col.codeValue || col.code_value,
            referenceTable: detailSettings?.referenceTable || col.referenceTable || col.reference_table,
            referenceColumn: detailSettings?.referenceColumn || col.referenceColumn || col.reference_column,
            displayColumn: detailSettings?.displayColumn || col.displayColumn || col.display_column,
            detailSettings,
          };
        });

        const tableInfo: TableInfo = {
          tableName,
          tableLabel,
          columns,
        };

        setTables([tableInfo]);
        toast.success(`테이블 "${tableLabel}" 선택됨`);

        // 기존 테이블과 다른 테이블 선택 시, 기존 컴포넌트 중 다른 테이블 컬럼은 제거
        if (tables.length > 0 && tables[0].tableName !== tableName) {
          setLayout((prev) => {
            const newComponents = prev.components.filter((comp) => {
              // 테이블 컬럼 기반 컴포넌트인지 확인
              if (comp.tableName && comp.tableName !== tableName) {
                console.log("🗑️ 다른 테이블 컴포넌트 제거:", comp.tableName, comp.columnName);
                return false;
              }
              return true;
            });

            if (newComponents.length < prev.components.length) {
              toast.info(
                `이전 테이블(${tables[0].tableName})의 컴포넌트가 ${prev.components.length - newComponents.length}개 제거되었습니다.`,
              );
            }

            return {
              ...prev,
              components: newComponents,
            };
          });
        }
      } catch (error) {
        console.error("테이블 정보 로드 실패:", error);
        toast.error("테이블 정보를 불러오는데 실패했습니다.");
      }
    },
    [tables],
  );

  // 화면 레이아웃 로드
  useEffect(() => {
    if (selectedScreen?.screenId) {
      // 현재 화면 ID를 전역 변수로 설정 (파일 업로드 시 사용)
      if (typeof window !== "undefined") {
        (window as any).__CURRENT_SCREEN_ID__ = selectedScreen.screenId;
      }

      const loadLayout = async () => {
        try {
          // 🆕 화면에 할당된 메뉴 조회
          const menuInfo = await screenApi.getScreenMenu(selectedScreen.screenId);
          if (menuInfo) {
            setMenuObjid(menuInfo.menuObjid);
            console.log("🔗 화면에 할당된 메뉴:", menuInfo);
          } else {
            console.warn("⚠️ 화면에 할당된 메뉴가 없습니다");
          }

          // V2/POP API 사용 여부에 따라 분기
          let response: any;
          if (USE_POP_API) {
            // POP 모드: screen_layouts_pop 테이블 사용
            const popResponse = await screenApi.getLayoutPop(selectedScreen.screenId);
            response = popResponse ? convertV2ToLegacy(popResponse) : null;
            console.log("📱 POP 레이아웃 로드:", popResponse?.components?.length || 0, "개 컴포넌트");
          } else if (USE_V2_API) {
            // 데스크톱 V2 모드: screen_layouts_v2 테이블 사용
            const v2Response = await screenApi.getLayoutV2(selectedScreen.screenId);

            // 🐛 디버깅: API 응답에서 fieldMapping.id 확인
            const splitPanelInV2 = v2Response?.components?.find((c: any) => c.url?.includes("v2-split-panel-layout"));
            const finishedTimelineInV2 = splitPanelInV2?.overrides?.rightPanel?.components?.find(
              (c: any) => c.id === "finished_timeline",
            );
            console.log("🐛 [API 응답 RAW] finished_timeline:", JSON.stringify(finishedTimelineInV2, null, 2));
            console.log("🐛 [API 응답] finished_timeline fieldMapping:", {
              fieldMapping: JSON.stringify(finishedTimelineInV2?.componentConfig?.fieldMapping),
              fieldMappingKeys: finishedTimelineInV2?.componentConfig?.fieldMapping
                ? Object.keys(finishedTimelineInV2?.componentConfig?.fieldMapping)
                : [],
              hasId: !!finishedTimelineInV2?.componentConfig?.fieldMapping?.id,
              idValue: finishedTimelineInV2?.componentConfig?.fieldMapping?.id,
            });

            response = v2Response ? convertV2ToLegacy(v2Response) : null;
          } else {
            response = await screenApi.getLayout(selectedScreen.screenId);
          }

          if (response) {
            // 🔄 마이그레이션 필요 여부 확인 (V2는 스킵)
            let layoutToUse = response;

            if (!USE_V2_API && needsMigration(response)) {
              const canvasWidth = response.screenResolution?.width || 1920;
              layoutToUse = safeMigrateLayout(response, canvasWidth);
            }

            // 🔄 webTypeConfig를 autoGeneration으로 변환
            const { convertLayoutComponents } = await import("@/lib/utils/webTypeConfigConverter");
            const convertedComponents = convertLayoutComponents(layoutToUse.components);

            // 기본 격자 설정 보장 (격자 표시와 스냅 기본 활성화)
            const layoutWithDefaultGrid = {
              ...layoutToUse,
              components: convertedComponents, // 변환된 컴포넌트 사용
              gridSettings: {
                columns: layoutToUse.gridSettings?.columns || 12, // DB 값 우선, 없으면 기본값 12
                gap: layoutToUse.gridSettings?.gap ?? 16, // DB 값 우선, 없으면 기본값 16
                padding: 0, // padding은 항상 0으로 강제
                snapToGrid: layoutToUse.gridSettings?.snapToGrid ?? true, // DB 값 우선
                showGrid: layoutToUse.gridSettings?.showGrid ?? false, // DB 값 우선
                gridColor: layoutToUse.gridSettings?.gridColor || "#d1d5db",
                gridOpacity: layoutToUse.gridSettings?.gridOpacity ?? 0.5,
              },
            };

            // 저장된 해상도 정보가 있으면 적용, 없으면 기본값 사용
            if (layoutToUse.screenResolution) {
              setScreenResolution(layoutToUse.screenResolution);
              // console.log("💾 저장된 해상도 불러옴:", layoutToUse.screenResolution);
            } else {
              // 기본 해상도 (Full HD)
              const defaultResolution =
                SCREEN_RESOLUTIONS.find((r) => r.name === "Full HD (1920×1080)") || SCREEN_RESOLUTIONS[0];
              setScreenResolution(defaultResolution);
              // console.log("🔧 기본 해상도 적용:", defaultResolution);
            }

            // 🔍 디버깅: 로드된 버튼 컴포넌트의 action 확인
            const buttonComponents = layoutWithDefaultGrid.components.filter((c: any) =>
              c.componentType?.startsWith("button"),
            );
            console.log(
              "🔍 [로드] 버튼 컴포넌트 action 확인:",
              buttonComponents.map((c: any) => ({
                id: c.id,
                type: c.componentType,
                actionType: c.componentConfig?.action?.type,
                fullAction: c.componentConfig?.action,
              })),
            );

            setLayout(layoutWithDefaultGrid);
            setHistory([layoutWithDefaultGrid]);
            setHistoryIndex(0);

            // 파일 컴포넌트 데이터 복원 (비동기)
            restoreFileComponentsData(layoutWithDefaultGrid.components);

            // 🆕 조건부 영역(Zone) 로드
            try {
              const loadedZones = await screenApi.getScreenZones(selectedScreen.screenId);
              setZones(loadedZones);
            } catch { /* Zone 로드 실패 무시 */ }
          }
        } catch (error) {
          // console.error("레이아웃 로드 실패:", error);
          toast.error("화면 레이아웃을 불러오는데 실패했습니다.");
        }
      };
      loadLayout();
    }
  }, [selectedScreen?.screenId]);

  // 스페이스바 키 이벤트 처리 (Pan 모드) + 전역 마우스 이벤트
  useEffect(() => {
    const handleKeyDown = (e: KeyboardEvent) => {
      // 입력 필드에서는 스페이스바 무시 (activeElement로 정확하게 체크)
      const activeElement = document.activeElement;
      if (
        activeElement instanceof HTMLInputElement ||
        activeElement instanceof HTMLTextAreaElement ||
        activeElement?.getAttribute("contenteditable") === "true" ||
        activeElement?.getAttribute("role") === "textbox"
      ) {
        return;
      }

      // e.target도 함께 체크 (이중 방어)
      if (e.target instanceof HTMLInputElement || e.target instanceof HTMLTextAreaElement) {
        return;
      }

      if (e.code === "Space") {
        e.preventDefault(); // 스페이스바 기본 스크롤 동작 차단
        if (!isPanMode) {
          setIsPanMode(true);
          // body에 커서 스타일 추가
          document.body.style.cursor = "grab";
        }
      }
    };

    const handleKeyUp = (e: KeyboardEvent) => {
      // 입력 필드에서는 스페이스바 무시
      const activeElement = document.activeElement;
      if (
        activeElement instanceof HTMLInputElement ||
        activeElement instanceof HTMLTextAreaElement ||
        activeElement?.getAttribute("contenteditable") === "true" ||
        activeElement?.getAttribute("role") === "textbox"
      ) {
        return;
      }

      if (e.code === "Space") {
        e.preventDefault(); // 스페이스바 기본 스크롤 동작 차단
        setIsPanMode(false);
        setPanState((prev) => ({ ...prev, isPanning: false }));
        // body 커서 스타일 복원
        document.body.style.cursor = "default";
      }
    };

    const handleMouseDown = (e: MouseEvent) => {
      if (isPanMode) {
        e.preventDefault();
        // 외부와 내부 스크롤 컨테이너 모두 저장
        setPanState({
          isPanning: true,
          startX: e.pageX,
          startY: e.pageY,
          outerScrollLeft: canvasContainerRef.current?.scrollLeft || 0,
          outerScrollTop: canvasContainerRef.current?.scrollTop || 0,
          innerScrollLeft: canvasRef.current?.scrollLeft || 0,
          innerScrollTop: canvasRef.current?.scrollTop || 0,
        });
        // 드래그 중 커서 변경
        document.body.style.cursor = "grabbing";
      }
    };

    const handleMouseMove = (e: MouseEvent) => {
      if (isPanMode && panState.isPanning) {
        e.preventDefault();
        const dx = e.pageX - panState.startX;
        const dy = e.pageY - panState.startY;

        // 외부 컨테이너 스크롤
        if (canvasContainerRef.current) {
          canvasContainerRef.current.scrollLeft = panState.outerScrollLeft - dx;
          canvasContainerRef.current.scrollTop = panState.outerScrollTop - dy;
        }

        // 내부 캔버스 스크롤
        if (canvasRef.current) {
          canvasRef.current.scrollLeft = panState.innerScrollLeft - dx;
          canvasRef.current.scrollTop = panState.innerScrollTop - dy;
        }
      }
    };

    const handleMouseUp = () => {
      if (isPanMode) {
        setPanState((prev) => ({ ...prev, isPanning: false }));
        // 드래그 종료 시 커서 복원
        document.body.style.cursor = "grab";
      }
    };

    window.addEventListener("keydown", handleKeyDown);
    window.addEventListener("keyup", handleKeyUp);
    window.addEventListener("mousedown", handleMouseDown);
    window.addEventListener("mousemove", handleMouseMove);
    window.addEventListener("mouseup", handleMouseUp);

    return () => {
      window.removeEventListener("keydown", handleKeyDown);
      window.removeEventListener("keyup", handleKeyUp);
      window.removeEventListener("mousedown", handleMouseDown);
      window.removeEventListener("mousemove", handleMouseMove);
      window.removeEventListener("mouseup", handleMouseUp);
    };
  }, [
    isPanMode,
    panState.isPanning,
    panState.startX,
    panState.startY,
    panState.outerScrollLeft,
    panState.outerScrollTop,
    panState.innerScrollLeft,
    panState.innerScrollTop,
  ]);

  // 마우스 휠로 줌 제어 (RAF throttle 적용으로 깜빡임 방지)
  useEffect(() => {
    const handleWheel = (e: WheelEvent) => {
      // 캔버스 컨테이너 내에서만 동작
      if (canvasContainerRef.current && canvasContainerRef.current.contains(e.target as Node)) {
        // Shift 키를 누르지 않은 경우에만 줌 (Shift + 휠은 수평 스크롤용)
        if (!e.shiftKey && !e.ctrlKey && !e.metaKey) {
          // 기본 스크롤 동작 방지
          e.preventDefault();

          const delta = e.deltaY;
          const zoomFactor = 0.001; // 줌 속도 조절

          // RAF throttle: 프레임당 한 번만 상태 업데이트
          if (zoomRafRef.current !== null) {
            cancelAnimationFrame(zoomRafRef.current);
          }
          zoomRafRef.current = requestAnimationFrame(() => {
            setZoomLevel((prevZoom) => {
              const newZoom = prevZoom - delta * zoomFactor;
              return Math.max(MIN_ZOOM, Math.min(MAX_ZOOM, newZoom));
            });
            zoomRafRef.current = null;
          });
        }
      }
    };

    // passive: false로 설정하여 preventDefault() 가능하게 함
    canvasContainerRef.current?.addEventListener("wheel", handleWheel, { passive: false });

    const containerRef = canvasContainerRef.current;
    return () => {
      containerRef?.removeEventListener("wheel", handleWheel);
      if (zoomRafRef.current !== null) {
        cancelAnimationFrame(zoomRafRef.current);
      }
    };
  }, [MIN_ZOOM, MAX_ZOOM]);

  // 격자 설정 업데이트 (컴포넌트 자동 조정 제거됨)
  const updateGridSettings = useCallback(
    (newGridSettings: GridSettings) => {
      const newLayout = { ...layout, gridSettings: newGridSettings };
      // 🆕 격자 설정 변경 시 컴포넌트 크기/위치 자동 조정 로직 제거됨
      // 사용자가 명시적으로 "격자 재조정" 버튼을 클릭해야만 조정됨
      setLayout(newLayout);
      saveToHistory(newLayout);
    },
    [layout, saveToHistory],
  );

  // 해상도 변경 핸들러 (컴포넌트 크기/위치 유지)
  const handleResolutionChange = useCallback(
    (newResolution: ScreenResolution) => {
      const oldWidth = screenResolution.width;
      const oldHeight = screenResolution.height;
      const newWidth = newResolution.width;
      const newHeight = newResolution.height;

      console.log("📱 해상도 변경:", {
        from: `${oldWidth}x${oldHeight}`,
        to: `${newWidth}x${newHeight}`,
        componentsCount: layout.components.length,
      });

      setScreenResolution(newResolution);

      // 해상도만 변경하고 컴포넌트 크기/위치는 그대로 유지
      const updatedLayout = {
        ...layout,
        screenResolution: newResolution,
      };

      setLayout(updatedLayout);
      saveToHistory(updatedLayout);

      toast.success("해상도가 변경되었습니다.", {
        description: `${oldWidth}×${oldHeight} → ${newWidth}×${newHeight}`,
      });

      console.log("✅ 해상도 변경 완료 (컴포넌트 크기/위치 유지)");
    },
    [layout, saveToHistory, screenResolution],
  );

  // 강제 격자 재조정 핸들러 (해상도 변경 후 수동 격자 맞춤용)
  const handleForceGridUpdate = useCallback(() => {
    if (!layout.gridSettings?.snapToGrid || layout.components.length === 0) {
      // console.log("격자 재조정 생략: 스냅 비활성화 또는 컴포넌트 없음");
      return;
    }

    console.log("🔄 격자 강제 재조정 시작:", {
      componentsCount: layout.components.length,
      resolution: `${screenResolution.width}x${screenResolution.height}`,
      gridSettings: layout.gridSettings,
    });

    // 현재 해상도로 격자 정보 계산
    const currentGridInfo = calculateGridInfo(screenResolution.width, screenResolution.height, {
      columns: layout.gridSettings.columns,
      gap: layout.gridSettings.gap,
      padding: layout.gridSettings.padding,
      snapToGrid: layout.gridSettings.snapToGrid || false,
    });

    const gridUtilSettings = {
      columns: layout.gridSettings.columns,
      gap: layout.gridSettings.gap,
      padding: layout.gridSettings.padding,
      snapToGrid: true,
    };

    const adjustedComponents = layout.components.map((comp) => {
      const snappedPosition = snapToGrid(comp.position, currentGridInfo, gridUtilSettings);
      const snappedSize = snapSizeToGrid(comp.size, currentGridInfo, gridUtilSettings);

      // gridColumns가 없거나 범위를 벗어나면 자동 조정
      let adjustedGridColumns = comp.gridColumns;
      if (!adjustedGridColumns || adjustedGridColumns < 1 || adjustedGridColumns > layout.gridSettings!.columns) {
        adjustedGridColumns = adjustGridColumnsFromSize({ size: snappedSize }, currentGridInfo, gridUtilSettings);
      }

      return {
        ...comp,
        position: snappedPosition,
        size: snappedSize,
        gridColumns: adjustedGridColumns,
      };
    });

    const newLayout = { ...layout, components: adjustedComponents };
    setLayout(newLayout);
    saveToHistory(newLayout);

    console.log("✅ 격자 강제 재조정 완료:", {
      adjustedComponents: adjustedComponents.length,
      gridInfo: {
        columnWidth: currentGridInfo.columnWidth.toFixed(2),
        totalWidth: currentGridInfo.totalWidth,
        columns: layout.gridSettings.columns,
      },
    });

    toast.success(`${adjustedComponents.length}개 컴포넌트가 격자에 맞게 재정렬되었습니다.`);
  }, [layout, screenResolution, saveToHistory]);

  // === 정렬/배분/동일크기/라벨토글/Nudge 핸들러 ===

  // 컴포넌트 정렬
  const handleGroupAlign = useCallback(
    (mode: AlignMode) => {
      if (groupState.selectedComponents.length < 2) {
        toast.warning("2개 이상의 컴포넌트를 선택해주세요.");
        return;
      }
      saveToHistory(layout);
      const newComponents = alignComponents(layout.components, groupState.selectedComponents, mode);
      setLayout((prev) => ({ ...prev, components: newComponents }));

      const modeNames: Record<AlignMode, string> = {
        left: "좌측", right: "우측", centerX: "가로 중앙",
        top: "상단", bottom: "하단", centerY: "세로 중앙",
      };
      toast.success(`${modeNames[mode]} 정렬 완료`);
    },
    [groupState.selectedComponents, layout, saveToHistory]
  );

  // 컴포넌트 균등 배분
  const handleGroupDistribute = useCallback(
    (direction: DistributeDirection) => {
      if (groupState.selectedComponents.length < 3) {
        toast.warning("3개 이상의 컴포넌트를 선택해주세요.");
        return;
      }
      saveToHistory(layout);
      const newComponents = distributeComponents(layout.components, groupState.selectedComponents, direction);
      setLayout((prev) => ({ ...prev, components: newComponents }));
      toast.success(`${direction === "horizontal" ? "가로" : "세로"} 균등 배분 완료`);
    },
    [groupState.selectedComponents, layout, saveToHistory]
  );

  // 동일 크기 맞추기
  const handleMatchSize = useCallback(
    (mode: MatchSizeMode) => {
      if (groupState.selectedComponents.length < 2) {
        toast.warning("2개 이상의 컴포넌트를 선택해주세요.");
        return;
      }
      saveToHistory(layout);
      const newComponents = matchComponentSize(
        layout.components,
        groupState.selectedComponents,
        mode,
        selectedComponent?.id
      );
      setLayout((prev) => ({ ...prev, components: newComponents }));

      const modeNames: Record<MatchSizeMode, string> = {
        width: "너비", height: "높이", both: "크기",
      };
      toast.success(`${modeNames[mode]} 맞추기 완료`);
    },
    [groupState.selectedComponents, layout, selectedComponent?.id, saveToHistory]
  );

  // 라벨 일괄 토글 (선택된 컴포넌트가 있으면 선택된 것만, 없으면 전체)
  const handleToggleAllLabels = useCallback(() => {
    saveToHistory(layout);

    const selectedIds = groupState.selectedComponents;
    const isPartial = selectedIds.length > 0;

    // 토글 대상 컴포넌트 필터
    const targetComponents = layout.components.filter((c) => {
      if (!c.label || ["group", "datatable"].includes(c.type)) return false;
      if (isPartial) return selectedIds.includes(c.id);
      return true;
    });

    const hadHidden = targetComponents.some(
      (c) => (c.style as any)?.labelDisplay === false
    );

    const newComponents = toggleAllLabels(layout.components, selectedIds);
    setLayout((prev) => ({ ...prev, components: newComponents }));

    // 강제 리렌더링 트리거
    setForceRenderTrigger((prev) => prev + 1);

    const scope = isPartial ? `선택된 ${targetComponents.length}개` : "모든";
    toast.success(hadHidden ? `${scope} 라벨 표시` : `${scope} 라벨 숨기기`);
  }, [layout, saveToHistory, groupState.selectedComponents]);

  // Nudge (화살표 키 이동)
  const handleNudge = useCallback(
    (direction: "up" | "down" | "left" | "right", distance: number) => {
      const targetIds =
        groupState.selectedComponents.length > 0
          ? groupState.selectedComponents
          : selectedComponent
            ? [selectedComponent.id]
            : [];

      if (targetIds.length === 0) return;

      const newComponents = nudgeComponents(layout.components, targetIds, direction, distance);
      setLayout((prev) => ({ ...prev, components: newComponents }));

      // 선택된 컴포넌트 업데이트
      if (selectedComponent && targetIds.includes(selectedComponent.id)) {
        const updated = newComponents.find((c) => c.id === selectedComponent.id);
        if (updated) setSelectedComponent(updated);
      }
    },
    [groupState.selectedComponents, selectedComponent, layout.components]
  );

  // 저장
  const handleSave = useCallback(async () => {
    if (!selectedScreen?.screenId) {
      console.error("❌ 저장 실패: selectedScreen 또는 screenId가 없습니다.", selectedScreen);
      toast.error("화면 정보가 없습니다.");
      return;
    }

    try {
      setIsSaving(true);

      // 분할 패널 컴포넌트의 rightPanel.tableName 자동 설정
      const updatedComponents = layout.components.map((comp) => {
        if (comp.type === "component" && comp.componentType === "split-panel-layout") {
          const config = comp.componentConfig || {};
          const rightPanel = config.rightPanel || {};
          const leftPanel = config.leftPanel || {};
          const relationshipType = rightPanel.relation?.type || "detail";

          // 관계 타입이 detail이면 rightPanel.tableName을 leftPanel.tableName과 동일하게 설정
          if (relationshipType === "detail" && leftPanel.tableName) {
            console.log("🔧 분할 패널 자동 수정:", {
              componentId: comp.id,
              leftTableName: leftPanel.tableName,
              rightTableName: leftPanel.tableName,
            });

            return {
              ...comp,
              componentConfig: {
                ...config,
                rightPanel: {
                  ...rightPanel,
                  tableName: leftPanel.tableName,
                },
              },
            };
          }
        }
        return comp;
      });

      // 해상도 정보를 포함한 레이아웃 데이터 생성
      // 현재 선택된 테이블을 화면의 기본 테이블로 저장
      const currentMainTableName = tables.length > 0 ? tables[0].tableName : null;

      const layoutWithResolution = {
        ...layout,
        components: updatedComponents,
        screenResolution: screenResolution,
        mainTableName: currentMainTableName, // 화면의 기본 테이블
      };

      // V2/POP API 사용 여부에 따라 분기
      const v2Layout = convertLegacyToV2(layoutWithResolution);
      if (USE_POP_API) {
        await screenApi.saveLayoutPop(selectedScreen.screenId, v2Layout);
      } else if (USE_V2_API) {
        const currentLayerId = activeLayerIdRef.current || 1;
        await screenApi.saveLayoutV2(selectedScreen.screenId, {
          ...v2Layout,
          layerId: currentLayerId,
          mainTableName: currentMainTableName,
        });
      } else {
        await screenApi.saveLayout(selectedScreen.screenId, layoutWithResolution);
      }

      // 테이블이 변경된 경우 전용 API로 명시적으로 업데이트
      if (currentMainTableName && currentMainTableName !== selectedScreen.tableName) {
        await screenApi.updateScreenTableName(selectedScreen.screenId, currentMainTableName);
      }

      toast.success("화면이 저장되었습니다.");

      if (onScreenUpdate && currentMainTableName) {
        onScreenUpdate({ tableName: currentMainTableName });
      }

      // 저장 성공 후 메뉴 할당 모달 열기
      setShowMenuAssignmentModal(true);
    } catch (error) {
      console.error("❌ 저장 실패:", error);
      toast.error("저장 중 오류가 발생했습니다.");
    } finally {
      setIsSaving(false);
    }
  }, [selectedScreen, layout, screenResolution, tables, onScreenUpdate]);

  // POP 미리보기 핸들러 (새 창에서 열기)
  const handlePopPreview = useCallback(() => {
    if (!selectedScreen?.screenId) {
      toast.error("화면 정보가 없습니다.");
      return;
    }
    
    const deviceType = defaultDevicePreview || "tablet";
    const previewUrl = `/pop/screens/${selectedScreen.screenId}?preview=true&device=${deviceType}`;
    window.open(previewUrl, "_blank", "width=800,height=900");
  }, [selectedScreen, defaultDevicePreview]);

  // 다국어 자동 생성 핸들러
  const handleGenerateMultilang = useCallback(async () => {
    if (!selectedScreen?.screenId) {
      toast.error("화면 정보가 없습니다.");
      return;
    }

    setIsGeneratingMultilang(true);

    try {
      // 공통 유틸 사용하여 라벨 추출
      const { extractMultilangLabels, extractTableNames, applyMultilangMappings } = await import(
        "@/lib/utils/multilangLabelExtractor"
      );
      const { apiClient } = await import("@/lib/api/client");

      // 테이블별 컬럼 라벨 로드
      const tableNames = extractTableNames(layout.components);
      const columnLabelMap: Record<string, Record<string, string>> = {};

      for (const tableName of tableNames) {
        try {
          const response = await apiClient.get(`/table-management/tables/${tableName}/columns`);
          if (response.data?.success && response.data?.data) {
            const columns = response.data.data.columns || response.data.data;
            if (Array.isArray(columns)) {
              columnLabelMap[tableName] = {};
              columns.forEach((col: any) => {
                const colName = col.columnName || col.column_name || col.name;
                const colLabel = col.displayName || col.columnLabel || col.column_label || colName;
                if (colName) {
                  columnLabelMap[tableName][colName] = colLabel;
                }
              });
            }
          }
        } catch (error) {
          console.error(`컬럼 라벨 조회 실패 (${tableName}):`, error);
        }
      }

      // 라벨 추출 (다국어 설정과 동일한 로직)
      const extractedLabels = extractMultilangLabels(layout.components, columnLabelMap);
      const labels = extractedLabels.map((l) => ({
        componentId: l.componentId,
        label: l.label,
        type: l.type,
      }));

      if (labels.length === 0) {
        toast.info("다국어로 변환할 라벨이 없습니다.");
        setIsGeneratingMultilang(false);
        return;
      }

      // API 호출
      const { generateScreenLabelKeys } = await import("@/lib/api/multilang");
      const response = await generateScreenLabelKeys({
        screenId: selectedScreen.screenId,
        menuObjId: menuObjid?.toString(),
        labels,
      });

      if (response.success && response.data) {
        // 자동 매핑 적용
        const updatedComponents = applyMultilangMappings(layout.components, response.data);

        // 레이아웃 업데이트
        const updatedLayout = {
          ...layout,
          components: updatedComponents,
          screenResolution: screenResolution,
        };

        setLayout(updatedLayout);

        // 자동 저장 (매핑 정보가 손실되지 않도록)
        try {
          const v2Layout = convertLegacyToV2(updatedLayout);
          if (USE_POP_API) {
            await screenApi.saveLayoutPop(selectedScreen.screenId, v2Layout);
          } else if (USE_V2_API) {
            // 현재 활성 레이어 ID 포함 (레이어별 저장)
            const currentLayerId = activeLayerIdRef.current || 1;
            await screenApi.saveLayoutV2(selectedScreen.screenId, {
              ...v2Layout,
              layerId: currentLayerId,
            });
          } else {
            await screenApi.saveLayout(selectedScreen.screenId, updatedLayout);
          }
          toast.success(`${response.data.length}개의 다국어 키가 생성되고 자동 저장되었습니다.`);
        } catch (saveError) {
          console.error("다국어 매핑 저장 실패:", saveError);
          toast.warning(`${response.data.length}개의 다국어 키가 생성되었습니다. 저장 버튼을 눌러 매핑을 저장하세요.`);
        }
      } else {
        toast.error(response.error?.details || "다국어 키 생성에 실패했습니다.");
      }
    } catch (error) {
      console.error("다국어 생성 실패:", error);
      toast.error("다국어 키 생성 중 오류가 발생했습니다.");
    } finally {
      setIsGeneratingMultilang(false);
    }
  }, [selectedScreen, layout, screenResolution, menuObjid]);

  // 템플릿 드래그 처리
  const handleTemplateDrop = useCallback(
    (e: React.DragEvent, template: TemplateComponent) => {
      const rect = canvasRef.current?.getBoundingClientRect();
      if (!rect) return;

      const dropX = e.clientX - rect.left;
      const dropY = e.clientY - rect.top;

      // 현재 해상도에 맞는 격자 정보 계산
      const currentGridInfo = layout.gridSettings
        ? calculateGridInfo(screenResolution.width, screenResolution.height, {
            columns: layout.gridSettings.columns,
            gap: layout.gridSettings.gap,
            padding: layout.gridSettings.padding,
            snapToGrid: layout.gridSettings.snapToGrid || false,
          })
        : null;

      // 격자 스냅 적용
      const snappedPosition =
        layout.gridSettings?.snapToGrid && currentGridInfo
          ? snapToGrid({ x: dropX, y: dropY, z: 1 }, currentGridInfo, layout.gridSettings as GridUtilSettings)
          : { x: dropX, y: dropY, z: 1 };

      console.log("🎨 템플릿 드롭:", {
        templateName: template.name,
        componentsCount: template.components.length,
        dropPosition: { x: dropX, y: dropY },
        snappedPosition,
      });

      // 템플릿의 모든 컴포넌트들을 생성
      // 먼저 ID 매핑을 생성 (parentId 참조를 위해)
      const idMapping: Record<string, string> = {};
      template.components.forEach((templateComp, index) => {
        const newId = generateComponentId();
        if (index === 0) {
          // 첫 번째 컴포넌트(컨테이너)는 "form-container"로 매핑
          idMapping["form-container"] = newId;
        }
        idMapping[templateComp.parentId || `temp_${index}`] = newId;
      });

      const newComponents: ComponentData[] = template.components.map((templateComp, index) => {
        const componentId = index === 0 ? idMapping["form-container"] : generateComponentId();

        // 템플릿 컴포넌트의 상대 위치를 드롭 위치 기준으로 조정
        const absoluteX = snappedPosition.x + templateComp.position.x;
        const absoluteY = snappedPosition.y + templateComp.position.y;

        // 격자 스냅 적용
        const finalPosition =
          layout.gridSettings?.snapToGrid && currentGridInfo
            ? snapPositionTo10px(
                { x: absoluteX, y: absoluteY, z: 1 },
                currentGridInfo,
                layout.gridSettings as GridUtilSettings,
              )
            : { x: absoluteX, y: absoluteY, z: 1 };

        if (templateComp.type === "container") {
          // 그리드 컬럼 기반 크기 계산
          const gridColumns =
            typeof templateComp.size.width === "number" && templateComp.size.width <= 12 ? templateComp.size.width : 4; // 기본 4컬럼

          const calculatedSize =
            currentGridInfo && layout.gridSettings?.snapToGrid
              ? (() => {
                  const newWidth = calculateWidthFromColumns(
                    gridColumns,
                    currentGridInfo,
                    layout.gridSettings as GridUtilSettings,
                  );
                  return {
                    width: newWidth,
                    height: templateComp.size.height,
                  };
                })()
              : { width: 400, height: templateComp.size.height }; // 폴백 크기

          return {
            id: componentId,
            type: "container",
            label: templateComp.label,
            tableName: selectedScreen?.tableName || "",
            title: templateComp.title || templateComp.label,
            position: finalPosition,
            size: calculatedSize,
            gridColumns,
            style: {
              labelDisplay: true,
              labelFontSize: "14px",
              labelColor: "#212121",
              labelFontWeight: "600",
              labelMarginBottom: "8px",
              ...templateComp.style,
            },
          };
        } else if (templateComp.type === "datatable") {
          // 데이터 테이블 컴포넌트 생성
          const gridColumns = 6; // 기본값: 6컬럼 (50% 너비)

          // gridColumns에 맞는 크기 계산
          const calculatedSize =
            currentGridInfo && layout.gridSettings?.snapToGrid
              ? (() => {
                  const newWidth = calculateWidthFromColumns(
                    gridColumns,
                    currentGridInfo,
                    layout.gridSettings as GridUtilSettings,
                  );
                  return {
                    width: newWidth,
                    height: templateComp.size.height, // 높이는 템플릿 값 유지
                  };
                })()
              : templateComp.size;

          console.log("📊 데이터 테이블 생성 시 크기 계산:", {
            gridColumns,
            templateSize: templateComp.size,
            calculatedSize,
            hasGridInfo: !!currentGridInfo,
            hasGridSettings: !!layout.gridSettings,
          });

          return {
            id: componentId,
            type: "datatable",
            label: templateComp.label,
            tableName: selectedScreen?.tableName || "",
            position: finalPosition,
            size: calculatedSize,
            title: templateComp.label,
            columns: [], // 초기에는 빈 배열, 나중에 설정
            filters: [], // 초기에는 빈 배열, 나중에 설정
            pagination: {
              enabled: true,
              pageSize: 10,
              pageSizeOptions: [5, 10, 20, 50],
              showPageSizeSelector: true,
              showPageInfo: true,
              showFirstLast: true,
            },
            showSearchButton: true,
            searchButtonText: "검색",
            enableExport: true,
            enableRefresh: true,
            enableAdd: true,
            enableEdit: true,
            enableDelete: true,
            addButtonText: "추가",
            editButtonText: "수정",
            deleteButtonText: "삭제",
            addModalConfig: {
              title: "새 데이터 추가",
              description: `${templateComp.label}에 새로운 데이터를 추가합니다.`,
              width: "lg",
              layout: "two-column",
              gridColumns: 2,
              fieldOrder: [], // 초기에는 빈 배열, 나중에 컬럼 추가 시 설정
              requiredFields: [],
              hiddenFields: [],
              advancedFieldConfigs: {}, // 초기에는 빈 객체, 나중에 컬럼별 설정
              submitButtonText: "추가",
              cancelButtonText: "취소",
            },
            gridColumns,
            style: {
              labelDisplay: true,
              labelFontSize: "14px",
              labelColor: "#212121",
              labelFontWeight: "600",
              labelMarginBottom: "8px",
              ...templateComp.style,
            },
          } as ComponentData;
        } else if (templateComp.type === "file") {
          // 파일 첨부 컴포넌트 생성
          const gridColumns = 6; // 기본값: 6컬럼

          const calculatedSize =
            currentGridInfo && layout.gridSettings?.snapToGrid
              ? (() => {
                  const newWidth = calculateWidthFromColumns(
                    gridColumns,
                    currentGridInfo,
                    layout.gridSettings as GridUtilSettings,
                  );
                  return {
                    width: newWidth,
                    height: templateComp.size.height,
                  };
                })()
              : templateComp.size;

          return {
            id: componentId,
            type: "file",
            label: templateComp.label,
            position: finalPosition,
            size: calculatedSize,
            gridColumns,
            fileConfig: {
              accept: ["image/*", ".pdf", ".doc", ".docx", ".xls", ".xlsx"],
              multiple: true,
              maxSize: 10, // 10MB
              maxFiles: 5,
              docType: "DOCUMENT",
              docTypeName: "일반 문서",
              targetObjid: selectedScreen?.screenId || "",
              showPreview: true,
              showProgress: true,
              dragDropText: "파일을 드래그하여 업로드하세요",
              uploadButtonText: "파일 선택",
              autoUpload: true,
              chunkedUpload: false,
            },
            uploadedFiles: [],
            style: {
              labelDisplay: true,
              labelFontSize: "14px",
              labelColor: "#212121",
              labelFontWeight: "600",
              labelMarginBottom: "8px",
              ...templateComp.style,
            },
          } as ComponentData;
        } else if (templateComp.type === "area") {
          // 영역 컴포넌트 생성
          const gridColumns = 6; // 기본값: 6컬럼 (50% 너비)

          const calculatedSize =
            currentGridInfo && layout.gridSettings?.snapToGrid
              ? (() => {
                  const newWidth = calculateWidthFromColumns(
                    gridColumns,
                    currentGridInfo,
                    layout.gridSettings as GridUtilSettings,
                  );
                  return {
                    width: newWidth,
                    height: templateComp.size.height,
                  };
                })()
              : templateComp.size;

          return {
            id: componentId,
            type: "area",
            label: templateComp.label,
            position: finalPosition,
            size: calculatedSize,
            gridColumns,
            layoutType: (templateComp as any).layoutType || "box",
            title: (templateComp as any).title || templateComp.label,
            description: (templateComp as any).description,
            layoutConfig: (templateComp as any).layoutConfig || {},
            areaStyle: {
              backgroundColor: "#ffffff",
              borderWidth: 1,
              borderStyle: "solid",
              borderColor: "#e5e7eb",
              borderRadius: 8,
              padding: 0,
              margin: 0,
              shadow: "sm",
              ...(templateComp as any).areaStyle,
            },
            children: [],
            style: {
              labelDisplay: true,
              labelFontSize: "14px",
              labelColor: "#212121",
              labelFontWeight: "600",
              labelMarginBottom: "8px",
              ...templateComp.style,
            },
          } as ComponentData;
        } else {
          // 위젯 컴포넌트
          const widgetType = templateComp.widgetType || "text";

          // 웹타입별 기본 그리드 컬럼 수 계산
          const getDefaultGridColumnsForTemplate = (wType: string): number => {
            const widthMap: Record<string, number> = {
              text: 4,
              email: 4,
              tel: 3,
              url: 4,
              textarea: 6,
              number: 2,
              decimal: 2,
              date: 3,
              datetime: 3,
              time: 2,
              select: 3,
              radio: 3,
              checkbox: 2,
              boolean: 2,
              code: 3,
              entity: 4,
              file: 4,
              image: 3,
              button: 2,
              label: 2,
            };
            return widthMap[wType] || 3;
          };

          // 웹타입별 기본 설정 생성
          const getDefaultWebTypeConfig = (wType: string) => {
            switch (wType) {
              case "date":
                return {
                  format: "YYYY-MM-DD" as const,
                  showTime: false,
                  placeholder: templateComp.placeholder || "날짜를 선택하세요",
                };
              case "select":
              case "dropdown":
                return {
                  options: [
                    { label: "옵션 1", value: "option1" },
                    { label: "옵션 2", value: "option2" },
                    { label: "옵션 3", value: "option3" },
                  ],
                  multiple: false,
                  searchable: false,
                  placeholder: templateComp.placeholder || "옵션을 선택하세요",
                };
              case "text":
                return {
                  format: "none" as const,
                  placeholder: templateComp.placeholder || "텍스트를 입력하세요",
                  multiline: false,
                };
              case "email":
                return {
                  format: "email" as const,
                  placeholder: templateComp.placeholder || "이메일을 입력하세요",
                  multiline: false,
                };
              case "tel":
                return {
                  format: "phone" as const,
                  placeholder: templateComp.placeholder || "전화번호를 입력하세요",
                  multiline: false,
                };
              case "textarea":
                return {
                  rows: 3,
                  placeholder: templateComp.placeholder || "텍스트를 입력하세요",
                  resizable: true,
                  wordWrap: true,
                };
              default:
                return {
                  placeholder: templateComp.placeholder || "입력하세요",
                };
            }
          };

          // 위젯 크기도 격자에 맞게 조정
          const widgetSize =
            currentGridInfo && layout.gridSettings?.snapToGrid
              ? {
                  width: calculateWidthFromColumns(1, currentGridInfo, layout.gridSettings as GridUtilSettings),
                  height: templateComp.size.height,
                }
              : templateComp.size;

          return {
            id: componentId,
            type: "widget",
            widgetType: widgetType as any,
            label: templateComp.label,
            placeholder: templateComp.placeholder,
            columnName: `field_${index + 1}`,
            parentId: templateComp.parentId ? idMapping[templateComp.parentId] : undefined,
            position: finalPosition,
            size: widgetSize,
            required: templateComp.required || false,
            readonly: templateComp.readonly || false,
            gridColumns: getDefaultGridColumnsForTemplate(widgetType),
            webTypeConfig: getDefaultWebTypeConfig(widgetType),
            style: {
              labelDisplay: true,
              labelFontSize: "14px",
              labelColor: "#212121",
              labelFontWeight: "600",
              labelMarginBottom: "8px",
              ...templateComp.style,
            },
          } as ComponentData;
        }
      });

      // 🆕 현재 활성 레이어에 컴포넌트 추가 (ref 사용으로 클로저 문제 방지)
      const componentsWithLayerId = newComponents.map((comp) => ({
        ...comp,
        layerId: activeLayerIdRef.current || 1,
      }));

      // 레이아웃에 새 컴포넌트들 추가
      const newLayout = {
        ...layout,
        components: [...layout.components, ...componentsWithLayerId],
      };

      setLayout(newLayout);
      saveToHistory(newLayout);

      // 첫 번째 컴포넌트 선택
      if (componentsWithLayerId.length > 0) {
        setSelectedComponent(componentsWithLayerId[0]);
      }

      toast.success(`${template.name} 템플릿이 추가되었습니다.`);
    },
    [layout, selectedScreen, saveToHistory],
  );

  // 레이아웃 드래그 처리
  const handleLayoutDrop = useCallback(
    (e: React.DragEvent, layoutData: any) => {
      const rect = canvasRef.current?.getBoundingClientRect();
      if (!rect) return;

      // 🔥 중요: 줌 레벨을 고려한 마우스 위치 계산
      const dropX = (e.clientX - rect.left) / zoomLevel;
      const dropY = (e.clientY - rect.top) / zoomLevel;

      // 현재 해상도에 맞는 격자 정보 계산
      const currentGridInfo = layout.gridSettings
        ? calculateGridInfo(screenResolution.width, screenResolution.height, {
            columns: layout.gridSettings.columns,
            gap: layout.gridSettings.gap,
            padding: layout.gridSettings.padding,
            snapToGrid: layout.gridSettings.snapToGrid || false,
          })
        : null;

      // 격자 스냅 적용
      const snappedPosition =
        layout.gridSettings?.snapToGrid && currentGridInfo
          ? snapToGrid({ x: dropX, y: dropY, z: 1 }, currentGridInfo, layout.gridSettings as GridUtilSettings)
          : { x: dropX, y: dropY, z: 1 };

      console.log("🏗️ 레이아웃 드롭 (줌 보정):", {
        zoomLevel,
        layoutType: layoutData.layoutType,
        zonesCount: layoutData.zones.length,
        mouseRaw: { x: e.clientX - rect.left, y: e.clientY - rect.top },
        dropPosition: { x: dropX, y: dropY },
        snappedPosition,
      });

      // 레이아웃 컴포넌트 생성
      const newLayoutComponent: ComponentData = {
        id: layoutData.id,
        type: "layout",
        layoutType: layoutData.layoutType,
        layoutConfig: layoutData.layoutConfig,
        zones: layoutData.zones.map((zone: any) => ({
          ...zone,
          id: `${layoutData.id}_${zone.id}`, // 레이아웃 ID를 접두사로 추가
        })),
        children: [],
        position: snappedPosition,
        size: layoutData.size,
        label: layoutData.label,
        allowedComponentTypes: layoutData.allowedComponentTypes,
        dropZoneConfig: layoutData.dropZoneConfig,
        layerId: activeLayerIdRef.current || 1, // 🆕 현재 활성 레이어에 추가 (ref 사용)
      } as ComponentData;

      // 레이아웃에 새 컴포넌트 추가
      const newLayout = {
        ...layout,
        components: [...layout.components, newLayoutComponent],
      };

      setLayout(newLayout);
      saveToHistory(newLayout);

      // 레이아웃 컴포넌트 선택
      setSelectedComponent(newLayoutComponent);

      toast.success(`${layoutData.label} 레이아웃이 추가되었습니다.`);
    },
    [layout, screenResolution, saveToHistory, zoomLevel],
  );

  // handleZoneComponentDrop은 handleComponentDrop으로 대체됨

  // 존 클릭 핸들러
  const handleZoneClick = useCallback((zoneId: string) => {
    // console.log("🎯 존 클릭:", zoneId);
    // 필요시 존 선택 로직 추가
  }, []);

  // 웹타입별 기본 설정 생성 함수를 상위로 이동
  const getDefaultWebTypeConfig = useCallback((webType: string) => {
    switch (webType) {
      case "button":
        return {
          actionType: "custom",
          variant: "default",
          confirmationMessage: "",
          popupTitle: "",
          popupContent: "",
          navigateUrl: "",
        };
      case "date":
        return {
          format: "YYYY-MM-DD",
          showTime: false,
          placeholder: "날짜를 선택하세요",
        };
      case "number":
        return {
          format: "integer",
          placeholder: "숫자를 입력하세요",
        };
      case "select":
        return {
          options: [
            { label: "옵션 1", value: "option1" },
            { label: "옵션 2", value: "option2" },
            { label: "옵션 3", value: "option3" },
          ],
          multiple: false,
          searchable: false,
          placeholder: "옵션을 선택하세요",
        };
      case "file":
        return {
          accept: ["*/*"],
          maxSize: 10485760, // 10MB
          multiple: false,
          showPreview: true,
          autoUpload: false,
        };
      default:
        return {};
    }
  }, []);

  // 컴포넌트 드래그 처리 (캔버스 레벨 드롭)
  const handleComponentDrop = useCallback(
    (e: React.DragEvent, component?: any, zoneId?: string, layoutId?: string) => {
      // 존별 드롭인 경우 dragData에서 컴포넌트 정보 추출
      if (!component) {
        const dragData = e.dataTransfer.getData("application/json");
        if (!dragData) return;

        try {
          const parsedData = JSON.parse(dragData);
          if (parsedData.type === "component") {
            component = parsedData.component;
          } else {
            return;
          }
        } catch (error) {
          // console.error("드래그 데이터 파싱 오류:", error);
          return;
        }
      }

      // 🎯 리피터 컨테이너 내부 드롭 처리
      const dropTarget = e.target as HTMLElement;
      const repeatContainer = dropTarget.closest('[data-repeat-container="true"]');
      if (repeatContainer) {
        const containerId = repeatContainer.getAttribute("data-component-id");
        if (containerId) {
          // 해당 리피터 컨테이너 찾기
          const targetComponent = layout.components.find((c) => c.id === containerId);
          const compType = (targetComponent as any)?.componentType;
          // v2-repeat-container 또는 repeat-container 모두 지원
          if (targetComponent && (compType === "repeat-container" || compType === "v2-repeat-container")) {
            const currentConfig = (targetComponent as any).componentConfig || {};
            const currentChildren = currentConfig.children || [];

            // 새 자식 컴포넌트 생성
            const newChild = {
              id: `slot_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
              componentType: component.id || component.componentType || "text-display",
              label: component.name || component.label || "새 컴포넌트",
              fieldName: "",
              position: { x: 0, y: currentChildren.length * 40 },
              size: component.defaultSize || { width: 200, height: 32 },
              componentConfig: component.defaultConfig || {},
            };

            // 컴포넌트 업데이트
            const updatedComponent = {
              ...targetComponent,
              componentConfig: {
                ...currentConfig,
                children: [...currentChildren, newChild],
              },
            };

            const newLayout = {
              ...layout,
              components: layout.components.map((c) => (c.id === containerId ? updatedComponent : c)),
            };

            setLayout(newLayout);
            saveToHistory(newLayout);
            return; // 리피터 컨테이너 처리 완료
          }
        }
      }

      // 🎯 탭 컨테이너 내부 드롭 처리 (중첩 구조 지원)
      const tabsContainer = dropTarget.closest('[data-tabs-container="true"]');
      if (tabsContainer) {
        const containerId = tabsContainer.getAttribute("data-component-id");
        const activeTabId = tabsContainer.getAttribute("data-active-tab-id");
        if (containerId && activeTabId) {
          // 1. 먼저 최상위 레이아웃에서 탭 컴포넌트 찾기
          let targetComponent = layout.components.find((c) => c.id === containerId);
          let parentSplitPanelId: string | null = null;
          let parentPanelSide: "left" | "right" | null = null;

          // 2. 최상위에 없으면 분할 패널 안에서 중첩된 탭 찾기
          if (!targetComponent) {
            for (const comp of layout.components) {
              const compType = (comp as any)?.componentType;
              if (compType === "split-panel-layout" || compType === "v2-split-panel-layout") {
                const config = (comp as any).componentConfig || {};

                // 좌측 패널에서 찾기
                const leftComponents = config.leftPanel?.components || [];
                const foundInLeft = leftComponents.find((c: any) => c.id === containerId);
                if (foundInLeft) {
                  targetComponent = foundInLeft;
                  parentSplitPanelId = comp.id;
                  parentPanelSide = "left";
                  break;
                }

                // 우측 패널에서 찾기
                const rightComponents = config.rightPanel?.components || [];
                const foundInRight = rightComponents.find((c: any) => c.id === containerId);
                if (foundInRight) {
                  targetComponent = foundInRight;
                  parentSplitPanelId = comp.id;
                  parentPanelSide = "right";
                  break;
                }
              }
            }
          }

          const compType = (targetComponent as any)?.componentType;
          if (targetComponent && (compType === "tabs-widget" || compType === "v2-tabs-widget")) {
            const currentConfig = (targetComponent as any).componentConfig || {};
            const tabs = currentConfig.tabs || [];

            // 활성 탭의 드롭 위치 계산
            const tabContentRect = tabsContainer.getBoundingClientRect();
            const dropX = (e.clientX - tabContentRect.left) / zoomLevel;
            const dropY = (e.clientY - tabContentRect.top) / zoomLevel;

            // 새 컴포넌트 생성
            const componentType = component.id || component.componentType || "v2-text-display";

            console.log("🎯 탭에 컴포넌트 드롭:", {
              componentId: component.id,
              componentType: componentType,
              componentName: component.name,
              isNested: !!parentSplitPanelId,
              parentSplitPanelId,
              parentPanelSide,
              // 🆕 위치 디버깅
              clientX: e.clientX,
              clientY: e.clientY,
              tabContentRect: { left: tabContentRect.left, top: tabContentRect.top },
              zoomLevel,
              calculatedPosition: { x: dropX, y: dropY },
            });

            const newTabComponent = {
              id: `tab_comp_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
              componentType: componentType,
              label: component.name || component.label || "새 컴포넌트",
              position: { x: Math.max(0, dropX), y: Math.max(0, dropY) },
              size: component.defaultSize || { width: 200, height: 100 },
              componentConfig: component.defaultConfig || {},
            };

            // 해당 탭에 컴포넌트 추가
            const updatedTabs = tabs.map((tab: any) => {
              if (tab.id === activeTabId) {
                return {
                  ...tab,
                  components: [...(tab.components || []), newTabComponent],
                };
              }
              return tab;
            });

            const updatedTabsComponent = {
              ...targetComponent,
              componentConfig: {
                ...currentConfig,
                tabs: updatedTabs,
              },
            };

            let newLayout;

            if (parentSplitPanelId && parentPanelSide) {
              // 🆕 중첩 구조: 분할 패널 안의 탭 업데이트
              newLayout = {
                ...layout,
                components: layout.components.map((c) => {
                  if (c.id === parentSplitPanelId) {
                    const splitConfig = (c as any).componentConfig || {};
                    const panelKey = parentPanelSide === "left" ? "leftPanel" : "rightPanel";
                    const panelConfig = splitConfig[panelKey] || {};
                    const panelComponents = panelConfig.components || [];

                    return {
                      ...c,
                      componentConfig: {
                        ...splitConfig,
                        [panelKey]: {
                          ...panelConfig,
                          components: panelComponents.map((pc: any) =>
                            pc.id === containerId ? updatedTabsComponent : pc,
                          ),
                        },
                      },
                    };
                  }
                  return c;
                }),
              };
              toast.success("컴포넌트가 중첩된 탭에 추가되었습니다");
            } else {
              // 일반 구조: 최상위 탭 업데이트
              newLayout = {
                ...layout,
                components: layout.components.map((c) => (c.id === containerId ? updatedTabsComponent : c)),
              };
              toast.success("컴포넌트가 탭에 추가되었습니다");
            }

            setLayout(newLayout);
            saveToHistory(newLayout);
            return; // 탭 컨테이너 처리 완료
          }
        }
      }

      // 🎯 분할 패널 커스텀 모드 컨테이너 내부 드롭 처리
      const splitPanelContainer = dropTarget.closest('[data-split-panel-container="true"]');
      if (splitPanelContainer) {
        const containerId = splitPanelContainer.getAttribute("data-component-id");
        const panelSide = splitPanelContainer.getAttribute("data-panel-side"); // "left" or "right"
        if (containerId && panelSide) {
          const targetComponent = layout.components.find((c) => c.id === containerId);
          const compType = (targetComponent as any)?.componentType;
          if (targetComponent && (compType === "split-panel-layout" || compType === "v2-split-panel-layout")) {
            const currentConfig = (targetComponent as any).componentConfig || {};
            const panelKey = panelSide === "left" ? "leftPanel" : "rightPanel";
            const panelConfig = currentConfig[panelKey] || {};
            const currentComponents = panelConfig.components || [];

            // 드롭 위치 계산
            const panelRect = splitPanelContainer.getBoundingClientRect();
            const dropX = (e.clientX - panelRect.left) / zoomLevel;
            const dropY = (e.clientY - panelRect.top) / zoomLevel;

            // 새 컴포넌트 생성
            const componentType = component.id || component.componentType || "v2-text-display";

            console.log("🎯 분할 패널에 컴포넌트 드롭:", {
              componentId: component.id,
              componentType: componentType,
              panelSide: panelSide,
              dropPosition: { x: dropX, y: dropY },
            });

            const newPanelComponent = {
              id: `panel_comp_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
              componentType: componentType,
              label: component.name || component.label || "새 컴포넌트",
              position: { x: Math.max(0, dropX), y: Math.max(0, dropY) },
              size: component.defaultSize || { width: 200, height: 100 },
              componentConfig: component.defaultConfig || {},
            };

            const updatedPanelConfig = {
              ...panelConfig,
              components: [...currentComponents, newPanelComponent],
            };

            const updatedComponent = {
              ...targetComponent,
              componentConfig: {
                ...currentConfig,
                [panelKey]: updatedPanelConfig,
              },
            };

            const newLayout = {
              ...layout,
              components: layout.components.map((c) => (c.id === containerId ? updatedComponent : c)),
            };

            setLayout(newLayout);
            saveToHistory(newLayout);
            toast.success(`컴포넌트가 ${panelSide === "left" ? "좌측" : "우측"} 패널에 추가되었습니다`);
            return; // 분할 패널 처리 완료
          }
        }
      }

      const rect = canvasRef.current?.getBoundingClientRect();
      if (!rect) return;

      // 컴포넌트 크기 정보
      const componentWidth = component.defaultSize?.width || 120;
      const componentHeight = component.defaultSize?.height || 36;

      // 🔥 중요: 줌 레벨과 transform-origin을 고려한 마우스 위치 계산
      // 1. 캔버스가 scale() 변환되어 있음 (transform-origin: top center)
      // 2. 캔버스가 justify-center로 중앙 정렬되어 있음

      // 실제 캔버스 논리적 크기
      const canvasLogicalWidth = screenResolution.width;

      // 화면상 캔버스 실제 크기 (스케일 적용 후)
      const canvasVisualWidth = canvasLogicalWidth * zoomLevel;

      // 중앙 정렬로 인한 왼쪽 오프셋 계산
      // rect.left는 이미 중앙 정렬된 위치를 반영하고 있음

      // 마우스의 캔버스 내 상대 위치 (스케일 보정)
      const mouseXInCanvas = (e.clientX - rect.left) / zoomLevel;
      const mouseYInCanvas = (e.clientY - rect.top) / zoomLevel;

      // 방법 1: 마우스 포인터를 컴포넌트 중심으로
      const dropX_centered = mouseXInCanvas - componentWidth / 2;
      const dropY_centered = mouseYInCanvas - componentHeight / 2;

      // 방법 2: 마우스 포인터를 컴포넌트 좌상단으로
      const dropX_topleft = mouseXInCanvas;
      const dropY_topleft = mouseYInCanvas;

      // 사용자가 원하는 방식으로 변경: 마우스 포인터가 좌상단에 오도록
      const dropX = dropX_topleft;
      const dropY = dropY_topleft;

      console.log("🎯 위치 계산 디버깅 (줌 레벨 + 중앙정렬 반영):", {
        "1. 줌 레벨": zoomLevel,
        "2. 마우스 위치 (화면)": { clientX: e.clientX, clientY: e.clientY },
        "3. 캔버스 위치 (rect)": { left: rect.left, top: rect.top, width: rect.width, height: rect.height },
        "4. 캔버스 논리적 크기": { width: canvasLogicalWidth, height: screenResolution.height },
        "5. 캔버스 시각적 크기": { width: canvasVisualWidth, height: screenResolution.height * zoomLevel },
        "6. 마우스 캔버스 내 상대위치 (줌 전)": { x: e.clientX - rect.left, y: e.clientY - rect.top },
        "7. 마우스 캔버스 내 상대위치 (줌 보정)": { x: mouseXInCanvas, y: mouseYInCanvas },
        "8. 컴포넌트 크기": { width: componentWidth, height: componentHeight },
        "9a. 중심 방식": { x: dropX_centered, y: dropY_centered },
        "9b. 좌상단 방식": { x: dropX_topleft, y: dropY_topleft },
        "10. 최종 선택": { dropX, dropY },
      });

      // 현재 해상도에 맞는 격자 정보 계산
      const currentGridInfo = layout.gridSettings
        ? calculateGridInfo(screenResolution.width, screenResolution.height, {
            columns: layout.gridSettings.columns,
            gap: layout.gridSettings.gap,
            padding: layout.gridSettings.padding,
            snapToGrid: layout.gridSettings.snapToGrid || false,
          })
        : null;

      // 캔버스 경계 내로 위치 제한 (조건부 레이어 편집 시 Zone 크기 기준)
      const currentLayerId = activeLayerIdRef.current || 1;
      const activeLayerRegion = currentLayerId > 1 ? activeLayerZone : null;
      const canvasBoundW = activeLayerRegion ? activeLayerRegion.width : screenResolution.width;
      const canvasBoundH = activeLayerRegion ? activeLayerRegion.height : screenResolution.height;
      const boundedX = Math.max(0, Math.min(dropX, canvasBoundW - componentWidth));
      const boundedY = Math.max(0, Math.min(dropY, canvasBoundH - componentHeight));

      // 격자 스냅 적용
      const snappedPosition =
        layout.gridSettings?.snapToGrid && currentGridInfo
          ? snapPositionTo10px(
              { x: boundedX, y: boundedY, z: 1 },
              currentGridInfo,
              layout.gridSettings as GridUtilSettings,
            )
          : { x: boundedX, y: boundedY, z: 1 };

      console.log("🧩 컴포넌트 드롭:", {
        componentName: component.name,
        webType: component.webType,
        rawPosition: { x: dropX, y: dropY },
        boundedPosition: { x: boundedX, y: boundedY },
        snappedPosition,
      });

      // 새 컴포넌트 생성 (새 컴포넌트 시스템 지원)
      console.log("🔍 ScreenDesigner handleComponentDrop:", {
        componentName: component.name,
        componentId: component.id,
        webType: component.webType,
        category: component.category,
        defaultConfig: component.defaultConfig,
        defaultSize: component.defaultSize,
      });

      // 컴포넌트별 gridColumns 설정 및 크기 계산
      let componentSize = component.defaultSize;
      const isCardDisplay = component.id === "card-display";
      const isTableList = component.id === "table-list";

      // 컴포넌트 타입별 기본 그리드 컬럼 수 설정
      const currentGridColumns = layout.gridSettings.columns; // 현재 격자 컬럼 수
      let gridColumns = 1; // 기본값

      // 특수 컴포넌트
      if (isCardDisplay) {
        gridColumns = Math.round(currentGridColumns * 0.667); // 약 66.67%
      } else if (isTableList) {
        gridColumns = currentGridColumns; // 테이블은 전체 너비
      } else {
        // 웹타입별 적절한 그리드 컬럼 수 설정
        const webType = component.webType;
        const componentId = component.id;

        // 웹타입별 기본 비율 매핑 (12컬럼 기준 비율)
        const gridColumnsRatioMap: Record<string, number> = {
          // 입력 컴포넌트 (INPUT 카테고리)
          "text-input": 4 / 12, // 텍스트 입력 (33%)
          "number-input": 2 / 12, // 숫자 입력 (16.67%)
          "email-input": 4 / 12, // 이메일 입력 (33%)
          "tel-input": 3 / 12, // 전화번호 입력 (25%)
          "date-input": 3 / 12, // 날짜 입력 (25%)
          "datetime-input": 4 / 12, // 날짜시간 입력 (33%)
          "time-input": 2 / 12, // 시간 입력 (16.67%)
          "textarea-basic": 6 / 12, // 텍스트 영역 (50%)
          "select-basic": 3 / 12, // 셀렉트 (25%)
          "checkbox-basic": 2 / 12, // 체크박스 (16.67%)
          "radio-basic": 3 / 12, // 라디오 (25%)
          "file-basic": 4 / 12, // 파일 (33%)
          "file-upload": 4 / 12, // 파일 업로드 (33%)
          "slider-basic": 3 / 12, // 슬라이더 (25%)
          "toggle-switch": 2 / 12, // 토글 스위치 (16.67%)
          "repeater-field-group": 6 / 12, // 반복 필드 그룹 (50%)

          // 표시 컴포넌트 (DISPLAY 카테고리)
          "label-basic": 2 / 12, // 라벨 (16.67%)
          "text-display": 3 / 12, // 텍스트 표시 (25%)
          "card-display": 8 / 12, // 카드 (66.67%)
          "badge-basic": 1 / 12, // 배지 (8.33%)
          "alert-basic": 6 / 12, // 알림 (50%)
          "divider-basic": 1, // 구분선 (100%)
          "divider-line": 1, // 구분선 (100%)
          "accordion-basic": 1, // 아코디언 (100%)
          "table-list": 1, // 테이블 리스트 (100%)
          "image-display": 4 / 12, // 이미지 표시 (33%)
          "split-panel-layout": 6 / 12, // 분할 패널 레이아웃 (50%)
          "flow-widget": 1, // 플로우 위젯 (100%)

          // 액션 컴포넌트 (ACTION 카테고리)
          "button-basic": 1 / 12, // 버튼 (8.33%)
          "button-primary": 1 / 12, // 프라이머리 버튼 (8.33%)
          "button-secondary": 1 / 12, // 세컨더리 버튼 (8.33%)
          "icon-button": 1 / 12, // 아이콘 버튼 (8.33%)

          // 레이아웃 컴포넌트
          "container-basic": 6 / 12, // 컨테이너 (50%)
          "section-basic": 1, // 섹션 (100%)
          "panel-basic": 6 / 12, // 패널 (50%)

          // 기타
          "image-basic": 4 / 12, // 이미지 (33%)
          "icon-basic": 1 / 12, // 아이콘 (8.33%)
          "progress-bar": 4 / 12, // 프로그레스 바 (33%)
          "chart-basic": 6 / 12, // 차트 (50%)
        };

        // defaultSize에 gridColumnSpan이 "full"이면 전체 컬럼 사용
        if (component.defaultSize?.gridColumnSpan === "full") {
          gridColumns = currentGridColumns;
        } else {
          // componentId 또는 webType으로 비율 찾기, 없으면 기본값 25%
          const ratio = gridColumnsRatioMap[componentId] || gridColumnsRatioMap[webType] || 0.25;
          // 현재 격자 컬럼 수에 비율을 곱하여 계산 (최소 1, 최대 currentGridColumns)
          gridColumns = Math.max(1, Math.min(currentGridColumns, Math.round(ratio * currentGridColumns)));
        }

        console.log("🎯 컴포넌트 타입별 gridColumns 설정:", {
          componentId,
          webType,
          gridColumns,
        });
      }

      // 10px 단위로 너비 스냅
      if (layout.gridSettings?.snapToGrid) {
        componentSize = {
          ...component.defaultSize,
          width: snapTo10px(component.defaultSize.width),
          height: snapTo10px(component.defaultSize.height),
        };
      }

      console.log("🎨 최종 컴포넌트 크기:", {
        componentId: component.id,
        componentName: component.name,
        defaultSize: component.defaultSize,
        finalSize: componentSize,
        gridColumns,
      });

      // 반복 필드 그룹인 경우 테이블의 첫 번째 컬럼을 기본 필드로 추가
      let enhancedDefaultConfig = { ...component.defaultConfig };
      if (
        component.id === "repeater-field-group" &&
        tables &&
        tables.length > 0 &&
        tables[0].columns &&
        tables[0].columns.length > 0
      ) {
        const firstColumn = tables[0].columns[0];
        enhancedDefaultConfig = {
          ...enhancedDefaultConfig,
          fields: [
            {
              name: firstColumn.columnName,
              label: firstColumn.columnLabel || firstColumn.columnName,
              type: (firstColumn.widgetType as any) || "text",
              required: firstColumn.required || false,
              placeholder: `${firstColumn.columnLabel || firstColumn.columnName}을(를) 입력하세요`,
            },
          ],
        };
      }

      // gridColumns에 맞춰 width를 퍼센트로 계산
      const widthPercent = (gridColumns / currentGridColumns) * 100;

      console.log("🎨 [컴포넌트 생성] 너비 계산:", {
        componentName: component.name,
        componentId: component.id,
        currentGridColumns,
        gridColumns,
        widthPercent: `${widthPercent}%`,
        calculatedWidth: `${Math.round(widthPercent * 100) / 100}%`,
      });

      // 🆕 라벨을 기반으로 기본 columnName 생성 (한글 → 스네이크 케이스)
      // 예: "창고코드" → "warehouse_code" 또는 그대로 유지
      const generateDefaultColumnName = (label: string): string => {
        // 한글 라벨의 경우 그대로 사용 (나중에 사용자가 수정 가능)
        // 영문의 경우 스네이크 케이스로 변환
        if (/[가-힣]/.test(label)) {
          // 한글이 포함된 경우: 공백을 언더스코어로, 소문자로 변환
          return label.replace(/\s+/g, "_").toLowerCase();
        }
        // 영문의 경우: 카멜케이스/파스칼케이스를 스네이크 케이스로 변환
        return label
          .replace(/([a-z])([A-Z])/g, "$1_$2")
          .replace(/\s+/g, "_")
          .toLowerCase();
      };

      const newComponent: ComponentData = {
        id: generateComponentId(),
        type: "component", // ✅ 새 컴포넌트 시스템 사용
        label: component.name,
        columnName: generateDefaultColumnName(component.name), // 🆕 기본 columnName 자동 생성
        widgetType: component.webType,
        componentType: component.id, // 새 컴포넌트 시스템의 ID (DynamicComponentRenderer용)
        position: snappedPosition,
        size: componentSize,
        gridColumns: gridColumns, // 컴포넌트별 그리드 컬럼 수 적용
        layerId: activeLayerIdRef.current || 1, // 🆕 현재 활성 레이어에 추가 (ref 사용)
        componentConfig: {
          type: component.id, // 새 컴포넌트 시스템의 ID 사용
          webType: component.webType, // 웹타입 정보 추가
          ...enhancedDefaultConfig,
        },
        webTypeConfig: getDefaultWebTypeConfig(component.webType),
        style: {
          labelDisplay: true, // 🆕 라벨 기본 표시 (사용자가 끄고 싶으면 체크 해제)
          labelFontSize: "14px",
          labelColor: "#212121",
          labelFontWeight: "500",
          labelMarginBottom: "4px",
          width: `${componentSize.width}px`, // size와 동기화 (픽셀 단위)
          height: `${componentSize.height}px`, // size와 동기화 (픽셀 단위)
        },
      };

      // 레이아웃에 컴포넌트 추가
      const newLayout: LayoutData = {
        ...layout,
        components: [...layout.components, newComponent],
      };

      setLayout(newLayout);
      saveToHistory(newLayout);

      // 새 컴포넌트 선택
      setSelectedComponent(newComponent);
      // 🔧 테이블 패널 유지를 위해 자동 속성 패널 열기 비활성화
      // openPanel("properties");

      toast.success(`${component.name} 컴포넌트가 추가되었습니다.`);
    },
    [layout, selectedScreen, saveToHistory],
  );

  // 드래그 앤 드롭 처리
  const handleDragOver = useCallback((e: React.DragEvent) => {
    e.preventDefault();
  }, []);

  const handleDrop = useCallback(
    async (e: React.DragEvent) => {
      e.preventDefault();

      const dragData = e.dataTransfer.getData("application/json");
      // console.log("🎯 드롭 이벤트:", { dragData });
      if (!dragData) {
        // console.log("❌ 드래그 데이터가 없습니다");
        return;
      }

      try {
        const parsedData = JSON.parse(dragData);
        // console.log("📋 파싱된 데이터:", parsedData);

        // 템플릿 드래그인 경우
        if (parsedData.type === "template") {
          handleTemplateDrop(e, parsedData.template);
          return;
        }

        // 레이아웃 드래그인 경우
        if (parsedData.type === "layout") {
          handleLayoutDrop(e, parsedData.layout);
          return;
        }

        // 컴포넌트 드래그인 경우
        if (parsedData.type === "component") {
          handleComponentDrop(e, parsedData.component);
          return;
        }

        // 🆕 조건부 영역(Zone) 생성 드래그인 경우 → DB screen_conditional_zones에 저장
        if (parsedData.type === "create-zone" && selectedScreen?.screenId) {
          const canvasRect = canvasRef.current?.getBoundingClientRect();
          if (!canvasRect) return;
          const dropX = Math.round((e.clientX - canvasRect.left) / zoomLevel);
          const dropY = Math.round((e.clientY - canvasRect.top) / zoomLevel);
          try {
            await screenApi.createZone(selectedScreen.screenId, {
              zone_name: "조건부 영역",
              x: Math.max(0, dropX - 400),
              y: Math.max(0, dropY),
              width: Math.min(800, screenResolution.width),
              height: 200,
            });
            // Zone 목록 새로고침
            const loadedZones = await screenApi.getScreenZones(selectedScreen.screenId);
            setZones(loadedZones);
            toast.success("조건부 영역이 생성되었습니다.");
          } catch (error) {
            console.error("Zone 생성 실패:", error);
            toast.error("조건부 영역 생성에 실패했습니다.");
          }
          return;
        }

        // 기존 테이블/컬럼 드래그 처리
        const { type, table, column } = parsedData;

        // 드롭 대상이 폼 컨테이너인지 확인
        const dropTarget = e.target as HTMLElement;
        const formContainer = dropTarget.closest('[data-form-container="true"]');

        // 🎯 리피터 컨테이너 내부에 컬럼 드롭 시 처리
        const repeatContainer = dropTarget.closest('[data-repeat-container="true"]');
        if (repeatContainer && type === "column" && column) {
          const containerId = repeatContainer.getAttribute("data-component-id");
          if (containerId) {
            const targetComponent = layout.components.find((c) => c.id === containerId);
            const rcType = (targetComponent as any)?.componentType;
            if (targetComponent && (rcType === "repeat-container" || rcType === "v2-repeat-container")) {
              const currentConfig = (targetComponent as any).componentConfig || {};
              const currentChildren = currentConfig.children || [];

              // 새 자식 컴포넌트 생성 (컬럼 기반)
              const newChild = {
                id: `slot_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
                componentType: column.widgetType || "text-display",
                label: column.columnLabel || column.columnName,
                fieldName: column.columnName,
                position: { x: 0, y: currentChildren.length * 40 },
                size: { width: 200, height: 32 },
                componentConfig: {},
              };

              const updatedComponent = {
                ...targetComponent,
                componentConfig: {
                  ...currentConfig,
                  children: [...currentChildren, newChild],
                },
              };

              const newLayout = {
                ...layout,
                components: layout.components.map((c) => (c.id === containerId ? updatedComponent : c)),
              };

              setLayout(newLayout);
              saveToHistory(newLayout);
              return;
            }
          }
        }

        // 🎯 탭 컨테이너 내부에 컬럼 드롭 시 처리 (중첩 구조 지원)
        const tabsContainer = dropTarget.closest('[data-tabs-container="true"]');
        if (tabsContainer && type === "column" && column) {
          const containerId = tabsContainer.getAttribute("data-component-id");
          const activeTabId = tabsContainer.getAttribute("data-active-tab-id");
          if (containerId && activeTabId) {
            // 1. 먼저 최상위 레이아웃에서 탭 컴포넌트 찾기
            let targetComponent = layout.components.find((c) => c.id === containerId);
            let parentSplitPanelId: string | null = null;
            let parentPanelSide: "left" | "right" | null = null;

            // 2. 최상위에 없으면 분할 패널 안에서 중첩된 탭 찾기
            if (!targetComponent) {
              for (const comp of layout.components) {
                const compType = (comp as any)?.componentType;
                if (compType === "split-panel-layout" || compType === "v2-split-panel-layout") {
                  const config = (comp as any).componentConfig || {};

                  // 좌측 패널에서 찾기
                  const leftComponents = config.leftPanel?.components || [];
                  const foundInLeft = leftComponents.find((c: any) => c.id === containerId);
                  if (foundInLeft) {
                    targetComponent = foundInLeft;
                    parentSplitPanelId = comp.id;
                    parentPanelSide = "left";
                    break;
                  }

                  // 우측 패널에서 찾기
                  const rightComponents = config.rightPanel?.components || [];
                  const foundInRight = rightComponents.find((c: any) => c.id === containerId);
                  if (foundInRight) {
                    targetComponent = foundInRight;
                    parentSplitPanelId = comp.id;
                    parentPanelSide = "right";
                    break;
                  }
                }
              }
            }

            const compType = (targetComponent as any)?.componentType;
            if (targetComponent && (compType === "tabs-widget" || compType === "v2-tabs-widget")) {
              const currentConfig = (targetComponent as any).componentConfig || {};
              const tabs = currentConfig.tabs || [];

              // 드롭 위치 계산
              const tabContentRect = tabsContainer.getBoundingClientRect();
              const dropX = (e.clientX - tabContentRect.left) / zoomLevel;
              const dropY = (e.clientY - tabContentRect.top) / zoomLevel;

              // 🆕 V2 컴포넌트 매핑 사용 (일반 캔버스와 동일)
              const v2Mapping = createV2ConfigFromColumn({
                widgetType: column.widgetType,
                columnName: column.columnName,
                columnLabel: column.columnLabel,
                codeCategory: column.codeCategory,
                inputType: column.inputType,
                required: column.required,
                detailSettings: column.detailSettings,
                referenceTable: column.referenceTable,
                referenceColumn: column.referenceColumn,
                displayColumn: column.displayColumn,
              });

              // 웹타입별 기본 크기 계산
              const getTabComponentSize = (widgetType: string) => {
                const sizeMap: Record<string, { width: number; height: number }> = {
                  text: { width: 200, height: 36 },
                  number: { width: 150, height: 36 },
                  decimal: { width: 150, height: 36 },
                  date: { width: 180, height: 36 },
                  datetime: { width: 200, height: 36 },
                  select: { width: 200, height: 36 },
                  category: { width: 200, height: 36 },
                  code: { width: 200, height: 36 },
                  entity: { width: 220, height: 36 },
                  boolean: { width: 120, height: 36 },
                  checkbox: { width: 120, height: 36 },
                  textarea: { width: 300, height: 100 },
                  file: { width: 250, height: 80 },
                };
                return sizeMap[widgetType] || { width: 200, height: 36 };
              };

              const componentSize = getTabComponentSize(column.widgetType);

              const newTabComponent = {
                id: `tab_comp_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
                componentType: v2Mapping.componentType,
                label: column.columnLabel || column.columnName,
                position: { x: Math.max(0, dropX), y: Math.max(0, dropY) },
                size: componentSize,
                inputType: column.inputType || column.widgetType,
                widgetType: column.widgetType,
                componentConfig: {
                  ...v2Mapping.componentConfig,
                  columnName: column.columnName,
                  tableName: column.tableName,
                  inputType: column.inputType || column.widgetType,
                },
              };

              // 해당 탭에 컴포넌트 추가
              const updatedTabs = tabs.map((tab: any) => {
                if (tab.id === activeTabId) {
                  return {
                    ...tab,
                    components: [...(tab.components || []), newTabComponent],
                  };
                }
                return tab;
              });

              const updatedTabsComponent = {
                ...targetComponent,
                componentConfig: {
                  ...currentConfig,
                  tabs: updatedTabs,
                },
              };

              let newLayout;

              if (parentSplitPanelId && parentPanelSide) {
                // 🆕 중첩 구조: 분할 패널 안의 탭 업데이트
                newLayout = {
                  ...layout,
                  components: layout.components.map((c) => {
                    if (c.id === parentSplitPanelId) {
                      const splitConfig = (c as any).componentConfig || {};
                      const panelKey = parentPanelSide === "left" ? "leftPanel" : "rightPanel";
                      const panelConfig = splitConfig[panelKey] || {};
                      const panelComponents = panelConfig.components || [];

                      return {
                        ...c,
                        componentConfig: {
                          ...splitConfig,
                          [panelKey]: {
                            ...panelConfig,
                            components: panelComponents.map((pc: any) =>
                              pc.id === containerId ? updatedTabsComponent : pc,
                            ),
                          },
                        },
                      };
                    }
                    return c;
                  }),
                };
                toast.success("컬럼이 중첩된 탭에 추가되었습니다");
              } else {
                // 일반 구조: 최상위 탭 업데이트
                newLayout = {
                  ...layout,
                  components: layout.components.map((c) => (c.id === containerId ? updatedTabsComponent : c)),
                };
                toast.success("컬럼이 탭에 추가되었습니다");
              }

              setLayout(newLayout);
              saveToHistory(newLayout);
              return;
            }
          }
        }

        // 🎯 분할 패널 커스텀 모드 컨테이너 내부에 컬럼 드롭 시 처리
        const splitPanelContainer = dropTarget.closest('[data-split-panel-container="true"]');
        if (splitPanelContainer && type === "column" && column) {
          const containerId = splitPanelContainer.getAttribute("data-component-id");
          const panelSide = splitPanelContainer.getAttribute("data-panel-side"); // "left" or "right"
          if (containerId && panelSide) {
            const targetComponent = layout.components.find((c) => c.id === containerId);
            const compType = (targetComponent as any)?.componentType;
            if (targetComponent && (compType === "split-panel-layout" || compType === "v2-split-panel-layout")) {
              const currentConfig = (targetComponent as any).componentConfig || {};
              const panelKey = panelSide === "left" ? "leftPanel" : "rightPanel";
              const panelConfig = currentConfig[panelKey] || {};
              const currentComponents = panelConfig.components || [];

              // 드롭 위치 계산
              const panelRect = splitPanelContainer.getBoundingClientRect();
              const dropX = (e.clientX - panelRect.left) / zoomLevel;
              const dropY = (e.clientY - panelRect.top) / zoomLevel;

              // V2 컴포넌트 매핑 사용
              const v2Mapping = createV2ConfigFromColumn({
                widgetType: column.widgetType,
                columnName: column.columnName,
                columnLabel: column.columnLabel,
                codeCategory: column.codeCategory,
                inputType: column.inputType,
                required: column.required,
                detailSettings: column.detailSettings,
                referenceTable: column.referenceTable,
                referenceColumn: column.referenceColumn,
                displayColumn: column.displayColumn,
              });

              // 웹타입별 기본 크기 계산
              const getPanelComponentSize = (widgetType: string) => {
                const sizeMap: Record<string, { width: number; height: number }> = {
                  text: { width: 200, height: 36 },
                  number: { width: 150, height: 36 },
                  decimal: { width: 150, height: 36 },
                  date: { width: 180, height: 36 },
                  datetime: { width: 200, height: 36 },
                  select: { width: 200, height: 36 },
                  category: { width: 200, height: 36 },
                  code: { width: 200, height: 36 },
                  entity: { width: 220, height: 36 },
                  boolean: { width: 120, height: 36 },
                  checkbox: { width: 120, height: 36 },
                  textarea: { width: 300, height: 100 },
                  file: { width: 250, height: 80 },
                };
                return sizeMap[widgetType] || { width: 200, height: 36 };
              };

              const componentSize = getPanelComponentSize(column.widgetType);

              const newPanelComponent = {
                id: `panel_comp_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
                componentType: v2Mapping.componentType,
                label: column.columnLabel || column.columnName,
                position: { x: Math.max(0, dropX), y: Math.max(0, dropY) },
                size: componentSize,
                inputType: column.inputType || column.widgetType,
                widgetType: column.widgetType,
                componentConfig: {
                  ...v2Mapping.componentConfig,
                  columnName: column.columnName,
                  tableName: column.tableName,
                  inputType: column.inputType || column.widgetType,
                },
              };

              const updatedPanelConfig = {
                ...panelConfig,
                components: [...currentComponents, newPanelComponent],
              };

              const updatedComponent = {
                ...targetComponent,
                componentConfig: {
                  ...currentConfig,
                  [panelKey]: updatedPanelConfig,
                },
              };

              const newLayout = {
                ...layout,
                components: layout.components.map((c) => (c.id === containerId ? updatedComponent : c)),
              };

              setLayout(newLayout);
              saveToHistory(newLayout);
              toast.success(`컬럼이 ${panelSide === "left" ? "좌측" : "우측"} 패널에 추가되었습니다`);
              return;
            }
          }
        }

        const rect = canvasRef.current?.getBoundingClientRect();
        if (!rect) return;

        const x = e.clientX - rect.left;
        const y = e.clientY - rect.top;

        let newComponent: ComponentData;

        if (type === "table") {
          // 테이블 컨테이너 생성
          newComponent = {
            id: generateComponentId(),
            type: "container",
            label: table.tableLabel || table.tableName, // 테이블 라벨 우선, 없으면 테이블명
            tableName: table.tableName,
            position: { x, y, z: 1 } as Position,
            size: { width: 300, height: 200 },
            layerId: activeLayerIdRef.current || 1, // 🆕 현재 활성 레이어에 추가 (ref 사용)
            style: {
              labelDisplay: true,
              labelFontSize: "14px",
              labelColor: "#212121",
              labelFontWeight: "600",
              labelMarginBottom: "8px",
            },
          };
        } else if (type === "column") {
          // console.log("🔄 컬럼 드롭 처리:", { webType: column.widgetType, columnName: column.columnName });

          // 웹타입별 기본 너비 계산 (10px 단위 고정)
          const getDefaultWidth = (widgetType: string): number => {
            const widthMap: Record<string, number> = {
              // 텍스트 입력 계열
              text: 200,
              email: 200,
              tel: 150,
              url: 250,
              textarea: 300,

              // 숫자/날짜 입력
              number: 120,
              decimal: 120,
              date: 150,
              datetime: 180,
              time: 120,

              // 선택 입력
              select: 180,
              radio: 180,
              checkbox: 120,
              boolean: 120,

              // 코드/참조
              code: 180,
              entity: 200,

              // 파일/이미지
              file: 250,
              image: 200,

              // 기타
              button: 100,
              label: 100,
            };

            return widthMap[widgetType] || 200; // 기본값 200px
          };

          // 웹타입별 기본 높이 계산
          const getDefaultHeight = (widgetType: string): number => {
            const heightMap: Record<string, number> = {
              textarea: 120, // 텍스트 영역은 3줄 (40 * 3)
              checkbox: 80, // 체크박스 그룹 (40 * 2)
              radio: 80, // 라디오 버튼 (40 * 2)
              file: 240, // 파일 업로드 (40 * 6)
            };

            return heightMap[widgetType] || 30; // 기본값 30px로 변경
          };

          // 웹타입별 기본 설정 생성
          const getDefaultWebTypeConfig = (widgetType: string) => {
            switch (widgetType) {
              case "date":
                return {
                  format: "YYYY-MM-DD" as const,
                  showTime: false,
                  placeholder: "날짜를 선택하세요",
                };
              case "datetime":
                return {
                  format: "YYYY-MM-DD HH:mm" as const,
                  showTime: true,
                  placeholder: "날짜와 시간을 선택하세요",
                };
              case "number":
                return {
                  format: "integer" as const,
                  placeholder: "숫자를 입력하세요",
                };
              case "decimal":
                return {
                  format: "decimal" as const,
                  step: 0.01,
                  decimalPlaces: 2,
                  placeholder: "소수를 입력하세요",
                };
              case "select":
              case "dropdown":
                return {
                  options: [
                    { label: "옵션 1", value: "option1" },
                    { label: "옵션 2", value: "option2" },
                    { label: "옵션 3", value: "option3" },
                  ],
                  multiple: false,
                  searchable: false,
                  placeholder: "옵션을 선택하세요",
                };
              case "text":
                return {
                  format: "none" as const,
                  placeholder: "텍스트를 입력하세요",
                  multiline: false,
                };
              case "email":
                return {
                  format: "email" as const,
                  placeholder: "이메일을 입력하세요",
                  multiline: false,
                };
              case "tel":
                return {
                  format: "phone" as const,
                  placeholder: "전화번호를 입력하세요",
                  multiline: false,
                };
              case "textarea":
                return {
                  rows: 3,
                  placeholder: "텍스트를 입력하세요",
                  resizable: true,
                  autoResize: false,
                  wordWrap: true,
                };
              case "checkbox":
              case "boolean":
                return {
                  defaultChecked: false,
                  labelPosition: "right" as const,
                  checkboxText: "",
                  trueValue: true,
                  falseValue: false,
                  indeterminate: false,
                };
              case "radio":
                return {
                  options: [
                    { label: "옵션 1", value: "option1" },
                    { label: "옵션 2", value: "option2" },
                  ],
                  layout: "vertical" as const,
                  defaultValue: "",
                  allowNone: false,
                };
              case "file":
                return {
                  accept: "",
                  multiple: false,
                  maxSize: 10,
                  maxFiles: 1,
                  preview: true,
                  dragDrop: true,
                  allowedExtensions: [],
                };
              case "code":
                return {
                  codeCategory: "", // 기본값, 실제로는 컬럼 정보에서 가져옴
                  placeholder: "선택하세요",
                  options: [], // 기본 빈 배열, 실제로는 API에서 로드
                };
              case "entity":
                return {
                  entityName: "",
                  displayField: "name",
                  valueField: "id",
                  searchable: true,
                  multiple: false,
                  allowClear: true,
                  placeholder: "엔터티를 선택하세요",
                  apiEndpoint: "",
                  filters: [],
                  displayFormat: "simple" as const,
                };
              case "table":
                return {
                  tableName: "",
                  displayMode: "table" as const,
                  showHeader: true,
                  showFooter: true,
                  pagination: {
                    enabled: true,
                    pageSize: 10,
                    showPageSizeSelector: true,
                    showPageInfo: true,
                    showFirstLast: true,
                  },
                  columns: [],
                  searchable: true,
                  sortable: true,
                  filterable: true,
                  exportable: true,
                };
              default:
                return undefined;
            }
          };

          // 폼 컨테이너에 드롭한 경우
          if (formContainer) {
            const formContainerId = formContainer.getAttribute("data-component-id");
            const formContainerComponent = layout.components.find((c) => c.id === formContainerId);

            if (formContainerComponent) {
              // 폼 내부에서의 상대적 위치 계산
              const containerRect = formContainer.getBoundingClientRect();
              const relativeX = e.clientX - containerRect.left;
              const relativeY = e.clientY - containerRect.top;

              // 🆕 V2 컴포넌트 매핑 사용
              const v2Mapping = createV2ConfigFromColumn({
                widgetType: column.widgetType,
                columnName: column.columnName,
                columnLabel: column.columnLabel,
                codeCategory: column.codeCategory,
                inputType: column.inputType,
                required: column.required,
                detailSettings: column.detailSettings, // 엔티티 참조 정보 전달
                // column_labels 직접 필드도 전달
                referenceTable: column.referenceTable,
                referenceColumn: column.referenceColumn,
                displayColumn: column.displayColumn,
              });

              // 웹타입별 기본 너비 계산 (10px 단위 고정)
              const componentWidth = getDefaultWidth(column.widgetType);

              console.log("🎯 폼 컨테이너 V2 컴포넌트 생성:", {
                widgetType: column.widgetType,
                v2Type: v2Mapping.componentType,
                componentWidth,
              });

              // 엔티티 조인 컬럼인 경우 읽기 전용으로 설정
              const isEntityJoinColumn = column.isEntityJoin === true;

              newComponent = {
                id: generateComponentId(),
                type: "component", // ✅ V2 컴포넌트 시스템 사용
                label: column.columnLabel || column.columnName,
                tableName: table.tableName,
                columnName: column.columnName,
                required: isEntityJoinColumn ? false : column.required,
                readonly: false,
                parentId: formContainerId,
                componentType: v2Mapping.componentType,
                position: { x: relativeX, y: relativeY, z: 1 } as Position,
                size: { width: componentWidth, height: getDefaultHeight(column.widgetType) },
                layerId: activeLayerIdRef.current || 1, // 🆕 현재 활성 레이어에 추가 (ref 사용)
                // 코드 타입인 경우 코드 카테고리 정보 추가
                ...(column.widgetType === "code" &&
                  column.codeCategory && {
                    codeCategory: column.codeCategory,
                  }),
                // 엔티티 조인 정보 저장
                ...(isEntityJoinColumn && {
                  isEntityJoin: true,
                  entityJoinTable: column.entityJoinTable,
                  entityJoinColumn: column.entityJoinColumn,
                }),
                style: {
                  labelDisplay: true, // 🆕 라벨 기본 표시
                  labelFontSize: "12px",
                  labelColor: "#212121",
                  labelFontWeight: "500",
                  labelMarginBottom: "6px",
                },
                componentConfig: {
                  type: v2Mapping.componentType, // v2-input, v2-select 등
                  ...v2Mapping.componentConfig,
                },
              };
            } else {
              return;
            }
          } else {
            // 일반 캔버스에 드롭한 경우 - 🆕 V2 컴포넌트 시스템 사용
            const v2Mapping = createV2ConfigFromColumn({
              widgetType: column.widgetType,
              columnName: column.columnName,
              columnLabel: column.columnLabel,
              codeCategory: column.codeCategory,
              inputType: column.inputType,
              required: column.required,
              detailSettings: column.detailSettings, // 엔티티 참조 정보 전달
              // column_labels 직접 필드도 전달
              referenceTable: column.referenceTable,
              referenceColumn: column.referenceColumn,
              displayColumn: column.displayColumn,
            });

            // 웹타입별 기본 너비 계산 (10px 단위 고정)
            const componentWidth = getDefaultWidth(column.widgetType);

            console.log("🎯 캔버스 V2 컴포넌트 생성:", {
              widgetType: column.widgetType,
              v2Type: v2Mapping.componentType,
              componentWidth,
            });

            // 엔티티 조인 컬럼인 경우 읽기 전용으로 설정
            const isEntityJoinColumn = column.isEntityJoin === true;

            newComponent = {
              id: generateComponentId(),
              type: "component", // ✅ V2 컴포넌트 시스템 사용
              label: column.columnLabel || column.columnName, // 컬럼 라벨 우선, 없으면 컬럼명
              tableName: table.tableName,
              columnName: column.columnName,
              required: isEntityJoinColumn ? false : column.required,
              readonly: false,
              componentType: v2Mapping.componentType,
              position: { x, y, z: 1 } as Position,
              size: { width: componentWidth, height: getDefaultHeight(column.widgetType) },
              layerId: activeLayerIdRef.current || 1, // 🆕 현재 활성 레이어에 추가 (ref 사용)
              // 코드 타입인 경우 코드 카테고리 정보 추가
              ...(column.widgetType === "code" &&
                column.codeCategory && {
                  codeCategory: column.codeCategory,
                }),
              // 엔티티 조인 정보 저장
              ...(isEntityJoinColumn && {
                isEntityJoin: true,
                entityJoinTable: column.entityJoinTable,
                entityJoinColumn: column.entityJoinColumn,
              }),
              style: {
                labelDisplay: true, // 🆕 라벨 기본 표시
                labelFontSize: "14px",
                labelColor: "#000000", // 순수한 검정
                labelFontWeight: "500",
                labelMarginBottom: "8px",
              },
              componentConfig: {
                type: v2Mapping.componentType, // v2-input, v2-select 등
                ...v2Mapping.componentConfig,
              },
            };
          }
        } else {
          return;
        }

        // 10px 단위 스냅 적용 (그룹 컴포넌트 제외)
        if (layout.gridSettings?.snapToGrid && newComponent.type !== "group") {
          newComponent.position = snapPositionTo10px(newComponent.position);
          newComponent.size = snapSizeTo10px(newComponent.size);

          console.log("🧲 새 컴포넌트 10px 스냅 적용:", {
            type: newComponent.type,
            snappedPosition: newComponent.position,
            snappedSize: newComponent.size,
          });
        }

        const newLayout = {
          ...layout,
          components: [...layout.components, newComponent],
        };

        setLayout(newLayout);
        saveToHistory(newLayout);
        setSelectedComponent(newComponent);

        // 🔧 테이블 패널 유지를 위해 자동 속성 패널 열기 비활성화
        // openPanel("properties");
      } catch (error) {
        // console.error("드롭 처리 실패:", error);
      }
    },
    [layout, saveToHistory],
  );

  // 파일 컴포넌트 업데이트 처리
  const handleFileComponentUpdate = useCallback(
    (updates: Partial<ComponentData>) => {
      if (!selectedFileComponent) return;

      const updatedComponents = layout.components.map((comp) =>
        comp.id === selectedFileComponent.id ? { ...comp, ...updates } : comp,
      );

      const newLayout = { ...layout, components: updatedComponents };
      setLayout(newLayout);
      saveToHistory(newLayout);

      // selectedFileComponent도 업데이트
      setSelectedFileComponent((prev) => (prev ? { ...prev, ...updates } : null));

      // selectedComponent가 같은 컴포넌트라면 업데이트
      if (selectedComponent?.id === selectedFileComponent.id) {
        setSelectedComponent((prev) => (prev ? { ...prev, ...updates } : null));
      }
    },
    [selectedFileComponent, layout, saveToHistory, selectedComponent],
  );

  // 파일첨부 모달 닫기
  const handleFileAttachmentModalClose = useCallback(() => {
    setShowFileAttachmentModal(false);
    setSelectedFileComponent(null);
  }, []);

  // 컴포넌트 더블클릭 처리
  const handleComponentDoubleClick = useCallback((component: ComponentData, event?: React.MouseEvent) => {
    event?.stopPropagation();

    // 파일 컴포넌트인 경우 상세 모달 열기
    if (component.type === "file") {
      setSelectedFileComponent(component);
      setShowFileAttachmentModal(true);
      return;
    }

    // 다른 컴포넌트 타입의 더블클릭 처리는 여기에 추가
    // console.log("더블클릭된 컴포넌트:", component.type, component.id);
  }, []);

  // 컴포넌트 클릭 처리 (다중선택 지원)
  const handleComponentClick = useCallback(
    (component: ComponentData, event?: React.MouseEvent) => {
      event?.stopPropagation();

      // 드래그가 끝난 직후라면 클릭을 무시 (다중 선택 유지)
      if (dragState.justFinishedDrag) {
        return;
      }

      // 🔧 layout.components에서 최신 버전의 컴포넌트 찾기
      const latestComponent = layout.components.find((c) => c.id === component.id);
      if (!latestComponent) {
        console.warn("⚠️ 컴포넌트를 찾을 수 없습니다:", component.id);
        return;
      }

      const isShiftPressed = event?.shiftKey || false;
      const isCtrlPressed = event?.ctrlKey || event?.metaKey || false;
      const isGroupContainer = latestComponent.type === "group";

      if (isShiftPressed || isCtrlPressed || groupState.isGrouping) {
        // 다중 선택 모드
        if (isGroupContainer) {
          // 그룹 컨테이너는 단일 선택으로 처리
          handleComponentSelect(latestComponent); // 🔧 최신 버전 사용
          setGroupState((prev) => ({
            ...prev,
            selectedComponents: [latestComponent.id],
            isGrouping: false,
          }));
          return;
        }

        const isSelected = groupState.selectedComponents.includes(latestComponent.id);
        setGroupState((prev) => ({
          ...prev,
          selectedComponents: isSelected
            ? prev.selectedComponents.filter((id) => id !== latestComponent.id)
            : [...prev.selectedComponents, latestComponent.id],
        }));

        // 마지막 선택된 컴포넌트를 selectedComponent로 설정
        if (!isSelected) {
          // console.log("🎯 컴포넌트 선택 (다중 모드):", latestComponent.id);
          handleComponentSelect(latestComponent); // 🔧 최신 버전 사용
        }
      } else {
        // 단일 선택 모드
        // console.log("🎯 컴포넌트 선택 (단일 모드):", latestComponent.id);
        handleComponentSelect(latestComponent); // 🔧 최신 버전 사용
        setGroupState((prev) => ({
          ...prev,
          selectedComponents: [latestComponent.id],
        }));
      }
    },
    [
      handleComponentSelect,
      groupState.isGrouping,
      groupState.selectedComponents,
      dragState.justFinishedDrag,
      layout.components,
    ],
  );

  // 컴포넌트 드래그 시작
  const startComponentDrag = useCallback(
    (component: ComponentData, event: React.MouseEvent | React.DragEvent) => {
      event.preventDefault();
      const rect = canvasRef.current?.getBoundingClientRect();
      if (!rect) return;

      // 새로운 드래그 시작 시 justFinishedDrag 플래그 해제
      if (dragState.justFinishedDrag) {
        setDragState((prev) => ({
          ...prev,
          justFinishedDrag: false,
        }));
      }

      // 🔥 중요: 줌 레벨을 고려한 마우스 위치 계산
      // 캔버스가 scale() 변환되어 있기 때문에 마우스 위치도 역변환 필요
      const relativeMouseX = (event.clientX - rect.left) / zoomLevel;
      const relativeMouseY = (event.clientY - rect.top) / zoomLevel;

      // 다중 선택된 컴포넌트들 확인
      const isDraggedComponentSelected = groupState.selectedComponents.includes(component.id);
      let componentsToMove = isDraggedComponentSelected
        ? layout.components.filter((comp) => groupState.selectedComponents.includes(comp.id))
        : [component];

      // 레이아웃 컴포넌트인 경우 존에 속한 컴포넌트들도 함께 이동
      if (component.type === "layout") {
        const zoneComponents = layout.components.filter((comp) => comp.parentId === component.id && comp.zoneId);

        console.log("🏗️ 레이아웃 드래그 - 존 컴포넌트들 포함:", {
          layoutId: component.id,
          zoneComponentsCount: zoneComponents.length,
          zoneComponents: zoneComponents.map((c) => ({ id: c.id, zoneId: c.zoneId })),
        });

        // 중복 제거하여 추가
        const allComponentIds = new Set(componentsToMove.map((c) => c.id));
        const additionalComponents = zoneComponents.filter((c) => !allComponentIds.has(c.id));
        componentsToMove = [...componentsToMove, ...additionalComponents];
      }

      setDragState({
        isDragging: true,
        draggedComponent: component, // 주 드래그 컴포넌트 (마우스 위치 기준)
        draggedComponents: componentsToMove, // 함께 이동할 모든 컴포넌트들
        originalPosition: {
          x: component.position.x,
          y: component.position.y,
          z: (component.position as Position).z || 1,
        },
        currentPosition: {
          x: component.position.x,
          y: component.position.y,
          z: (component.position as Position).z || 1,
        },
        grabOffset: {
          x: relativeMouseX - component.position.x,
          y: relativeMouseY - component.position.y,
        },
        justFinishedDrag: false,
      });
    },
    [groupState.selectedComponents, layout.components, dragState.justFinishedDrag, zoomLevel],
  );

  // 드래그 중 위치 업데이트 (성능 최적화 + 실시간 업데이트)
  const updateDragPosition = useCallback(
    (event: MouseEvent) => {
      if (!dragState.isDragging || !dragState.draggedComponent || !canvasRef.current) return;

      const rect = canvasRef.current.getBoundingClientRect();

      // 🔥 중요: 줌 레벨을 고려한 마우스 위치 계산
      // 캔버스가 scale() 변환되어 있기 때문에 마우스 위치도 역변환 필요
      const relativeMouseX = (event.clientX - rect.left) / zoomLevel;
      const relativeMouseY = (event.clientY - rect.top) / zoomLevel;

      // 컴포넌트 크기 가져오기
      const draggedComp = layout.components.find((c) => c.id === dragState.draggedComponent.id);
      const componentWidth = draggedComp?.size?.width || 100;
      const componentHeight = draggedComp?.size?.height || 40;

      // 경계 제한 적용
      const rawX = relativeMouseX - dragState.grabOffset.x;
      const rawY = relativeMouseY - dragState.grabOffset.y;

      // 조건부 레이어 편집 시 Zone 크기 기준 경계 제한
      const dragLayerId = activeLayerIdRef.current || 1;
      const dragLayerRegion = dragLayerId > 1 ? activeLayerZone : null;
      const dragBoundW = dragLayerRegion ? dragLayerRegion.width : screenResolution.width;
      const dragBoundH = dragLayerRegion ? dragLayerRegion.height : screenResolution.height;

      const newPosition = {
        x: Math.max(0, Math.min(rawX, dragBoundW - componentWidth)),
        y: Math.max(0, Math.min(rawY, dragBoundH - componentHeight)),
        z: (dragState.draggedComponent.position as Position).z || 1,
      };

      // 드래그 상태 업데이트
      setDragState((prev) => {
        const newState = {
          ...prev,
          currentPosition: { ...newPosition }, // 새로운 객체 생성
        };
        return newState;
      });

      // 성능 최적화: 드래그 중에는 상태 업데이트만 하고,
      // 실제 레이아웃 업데이트는 endDrag에서 처리
      // 속성 패널에서는 dragState.currentPosition을 참조하여 실시간 표시
    },
    [dragState.isDragging, dragState.draggedComponent, dragState.grabOffset, zoomLevel],
  );

  // 드래그 종료
  const endDrag = useCallback(
    (mouseEvent?: MouseEvent) => {
      if (dragState.isDragging && dragState.draggedComponent) {
        // 🎯 탭 컨테이너로의 드롭 처리 (기존 컴포넌트 이동, 중첩 구조 지원)
        if (mouseEvent) {
          const dropTarget = document.elementFromPoint(mouseEvent.clientX, mouseEvent.clientY) as HTMLElement;
          const tabsContainer = dropTarget?.closest('[data-tabs-container="true"]');

          if (tabsContainer) {
            const containerId = tabsContainer.getAttribute("data-component-id");
            const activeTabId = tabsContainer.getAttribute("data-active-tab-id");

            if (containerId && activeTabId) {
              // 1. 먼저 최상위 레이아웃에서 탭 컴포넌트 찾기
              let targetComponent = layout.components.find((c) => c.id === containerId);
              let parentSplitPanelId: string | null = null;
              let parentPanelSide: "left" | "right" | null = null;

              // 2. 최상위에 없으면 분할 패널 안에서 중첩된 탭 찾기
              if (!targetComponent) {
                for (const comp of layout.components) {
                  const compType = (comp as any)?.componentType;
                  if (compType === "split-panel-layout" || compType === "v2-split-panel-layout") {
                    const config = (comp as any).componentConfig || {};

                    // 좌측 패널에서 찾기
                    const leftComponents = config.leftPanel?.components || [];
                    const foundInLeft = leftComponents.find((c: any) => c.id === containerId);
                    if (foundInLeft) {
                      targetComponent = foundInLeft;
                      parentSplitPanelId = comp.id;
                      parentPanelSide = "left";
                      break;
                    }

                    // 우측 패널에서 찾기
                    const rightComponents = config.rightPanel?.components || [];
                    const foundInRight = rightComponents.find((c: any) => c.id === containerId);
                    if (foundInRight) {
                      targetComponent = foundInRight;
                      parentSplitPanelId = comp.id;
                      parentPanelSide = "right";
                      break;
                    }
                  }
                }
              }

              const compType = (targetComponent as any)?.componentType;

              // 자기 자신을 자신에게 드롭하는 것 방지
              if (
                targetComponent &&
                (compType === "tabs-widget" || compType === "v2-tabs-widget") &&
                dragState.draggedComponent !== containerId
              ) {
                const draggedComponent = layout.components.find((c) => c.id === dragState.draggedComponent);
                if (draggedComponent) {
                  const currentConfig = (targetComponent as any).componentConfig || {};
                  const tabs = currentConfig.tabs || [];

                  // 탭 컨텐츠 영역 기준 드롭 위치 계산
                  const tabContentRect = tabsContainer.getBoundingClientRect();
                  const dropX = (mouseEvent.clientX - tabContentRect.left) / zoomLevel;
                  const dropY = (mouseEvent.clientY - tabContentRect.top) / zoomLevel;

                  // 기존 컴포넌트를 탭 내부 컴포넌트로 변환
                  const newTabComponent = {
                    id: `tab_comp_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
                    componentType: (draggedComponent as any).componentType || draggedComponent.type,
                    label: (draggedComponent as any).label || "컴포넌트",
                    position: { x: Math.max(0, dropX), y: Math.max(0, dropY) },
                    size: draggedComponent.size || { width: 200, height: 100 },
                    componentConfig: (draggedComponent as any).componentConfig || {},
                    style: (draggedComponent as any).style || {},
                  };

                  // 해당 탭에 컴포넌트 추가
                  const updatedTabs = tabs.map((tab: any) => {
                    if (tab.id === activeTabId) {
                      return {
                        ...tab,
                        components: [...(tab.components || []), newTabComponent],
                      };
                    }
                    return tab;
                  });

                  const updatedTabsComponent = {
                    ...targetComponent,
                    componentConfig: {
                      ...currentConfig,
                      tabs: updatedTabs,
                    },
                  };

                  let newLayout;

                  if (parentSplitPanelId && parentPanelSide) {
                    // 🆕 중첩 구조: 분할 패널 안의 탭 업데이트
                    newLayout = {
                      ...layout,
                      components: layout.components
                        .filter((c) => c.id !== dragState.draggedComponent)
                        .map((c) => {
                          if (c.id === parentSplitPanelId) {
                            const splitConfig = (c as any).componentConfig || {};
                            const panelKey = parentPanelSide === "left" ? "leftPanel" : "rightPanel";
                            const panelConfig = splitConfig[panelKey] || {};
                            const panelComponents = panelConfig.components || [];

                            return {
                              ...c,
                              componentConfig: {
                                ...splitConfig,
                                [panelKey]: {
                                  ...panelConfig,
                                  components: panelComponents.map((pc: any) =>
                                    pc.id === containerId ? updatedTabsComponent : pc,
                                  ),
                                },
                              },
                            };
                          }
                          return c;
                        }),
                    };
                    toast.success("컴포넌트가 중첩된 탭으로 이동되었습니다");
                  } else {
                    // 일반 구조: 최상위 탭 업데이트
                    newLayout = {
                      ...layout,
                      components: layout.components
                        .filter((c) => c.id !== dragState.draggedComponent)
                        .map((c) => {
                          if (c.id === containerId) {
                            return updatedTabsComponent;
                          }
                          return c;
                        }),
                    };
                    toast.success("컴포넌트가 탭으로 이동되었습니다");
                  }

                  setLayout(newLayout);
                  saveToHistory(newLayout);
                  setSelectedComponent(null);

                  // 드래그 상태 초기화 후 종료
                  setDragState({
                    isDragging: false,
                    draggedComponent: null,
                    draggedComponents: [],
                    originalPosition: { x: 0, y: 0, z: 1 },
                    currentPosition: { x: 0, y: 0, z: 1 },
                    grabOffset: { x: 0, y: 0 },
                    justFinishedDrag: true,
                  });

                  setTimeout(() => {
                    setDragState((prev) => ({ ...prev, justFinishedDrag: false }));
                  }, 100);

                  return; // 탭으로 이동 완료, 일반 드래그 종료 로직 스킵
                }
              }
            }
          }
        }

        // 주 드래그 컴포넌트의 최종 위치 계산
        const draggedComponent = layout.components.find((c) => c.id === dragState.draggedComponent);
        let finalPosition = dragState.currentPosition;

        // 현재 해상도에 맞는 격자 정보 계산
        const currentGridInfo = layout.gridSettings
          ? calculateGridInfo(screenResolution.width, screenResolution.height, {
              columns: layout.gridSettings.columns,
              gap: layout.gridSettings.gap,
              padding: layout.gridSettings.padding,
              snapToGrid: layout.gridSettings.snapToGrid || false,
            })
          : null;

        // 일반 컴포넌트 및 플로우 버튼 그룹에 격자 스냅 적용 (일반 그룹 제외)
        if (draggedComponent?.type !== "group" && layout.gridSettings?.snapToGrid && currentGridInfo) {
          finalPosition = snapPositionTo10px(
            {
              x: dragState.currentPosition.x,
              y: dragState.currentPosition.y,
              z: dragState.currentPosition.z ?? 1,
            },
            currentGridInfo,
            {
              columns: layout.gridSettings.columns,
              gap: layout.gridSettings.gap,
              padding: layout.gridSettings.padding,
              snapToGrid: layout.gridSettings.snapToGrid || false,
            },
          );
        }

        // 스냅으로 인한 추가 이동 거리 계산
        const snapDeltaX = finalPosition.x - dragState.currentPosition.x;
        const snapDeltaY = finalPosition.y - dragState.currentPosition.y;

        // 원래 이동 거리 + 스냅 조정 거리
        const totalDeltaX = dragState.currentPosition.x - dragState.originalPosition.x + snapDeltaX;
        const totalDeltaY = dragState.currentPosition.y - dragState.originalPosition.y + snapDeltaY;

        // 다중 컴포넌트들의 최종 위치 업데이트
        const updatedComponents = layout.components.map((comp) => {
          const isDraggedComponent = dragState.draggedComponents.some((dragComp) => dragComp.id === comp.id);
          if (isDraggedComponent) {
            const originalComponent = dragState.draggedComponents.find((dragComp) => dragComp.id === comp.id)!;
            let newPosition = {
              x: originalComponent.position.x + totalDeltaX,
              y: originalComponent.position.y + totalDeltaY,
              z: originalComponent.position.z || 1,
            };

            // 캔버스 경계 제한 (컴포넌트가 화면 밖으로 나가지 않도록)
            const componentWidth = comp.size?.width || 100;
            const componentHeight = comp.size?.height || 40;

            // 최소 위치: 0, 최대 위치: 캔버스 크기 - 컴포넌트 크기
            newPosition.x = Math.max(0, Math.min(newPosition.x, screenResolution.width - componentWidth));
            newPosition.y = Math.max(0, Math.min(newPosition.y, screenResolution.height - componentHeight));

            // 그룹 내부 컴포넌트인 경우 패딩을 고려한 격자 스냅 적용
            if (comp.parentId && layout.gridSettings?.snapToGrid && gridInfo) {
              const { columnWidth } = gridInfo;
              const { gap } = layout.gridSettings;

              // 그룹 내부 패딩 고려한 격자 정렬
              const padding = 16;
              const effectiveX = newPosition.x - padding;
              const columnIndex = Math.round(effectiveX / (columnWidth + (gap || 16)));
              const snappedX = padding + columnIndex * (columnWidth + (gap || 16));

              // Y 좌표는 20px 단위로 스냅
              const effectiveY = newPosition.y - padding;
              const rowIndex = Math.round(effectiveY / 20);
              const snappedY = padding + rowIndex * 20;

              // 크기도 외부 격자와 동일하게 스냅
              const fullColumnWidth = columnWidth + (gap || 16); // 외부 격자와 동일한 크기
              const widthInColumns = Math.max(1, Math.round(comp.size.width / fullColumnWidth));
              const snappedWidth = widthInColumns * fullColumnWidth - (gap || 16); // gap 제거하여 실제 컴포넌트 크기
              // 높이는 사용자가 입력한 값 그대로 사용 (스냅 제거)
              const snappedHeight = Math.max(40, comp.size.height);

              newPosition = {
                x: Math.max(padding, snappedX), // 패딩만큼 최소 여백 확보
                y: Math.max(padding, snappedY),
                z: newPosition.z,
              };

              // 크기도 업데이트
              const newSize = {
                width: snappedWidth,
                height: snappedHeight,
              };

              return {
                ...comp,
                position: newPosition as Position,
                size: newSize,
              };
            }

            return {
              ...comp,
              position: newPosition as Position,
            };
          }
          return comp;
        });

        const newLayout = { ...layout, components: updatedComponents };
        setLayout(newLayout);

        // 선택된 컴포넌트도 업데이트 (PropertiesPanel 동기화용)
        if (selectedComponent && dragState.draggedComponents.some((c) => c.id === selectedComponent.id)) {
          const updatedSelectedComponent = updatedComponents.find((c) => c.id === selectedComponent.id);
          if (updatedSelectedComponent) {
            console.log("🔄 ScreenDesigner: 선택된 컴포넌트 위치 업데이트", {
              componentId: selectedComponent.id,
              oldPosition: selectedComponent.position,
              newPosition: updatedSelectedComponent.position,
            });
            setSelectedComponent(updatedSelectedComponent);
          }
        }

        // 히스토리에 저장
        saveToHistory(newLayout);
      }

      setDragState({
        isDragging: false,
        draggedComponent: null,
        draggedComponents: [],
        originalPosition: { x: 0, y: 0, z: 1 },
        currentPosition: { x: 0, y: 0, z: 1 },
        grabOffset: { x: 0, y: 0 },
        justFinishedDrag: true,
      });

      // 짧은 시간 후 justFinishedDrag 플래그 해제
      setTimeout(() => {
        setDragState((prev) => ({
          ...prev,
          justFinishedDrag: false,
        }));
      }, 100);
    },
    [dragState, layout, saveToHistory],
  );

  // 드래그 선택 시작
  const startSelectionDrag = useCallback(
    (event: React.MouseEvent) => {
      if (dragState.isDragging) return; // 컴포넌트 드래그 중이면 무시

      const rect = canvasRef.current?.getBoundingClientRect();
      if (!rect) return;

      // zoom 스케일을 고려한 좌표 변환
      const startPoint = {
        x: (event.clientX - rect.left) / zoomLevel,
        y: (event.clientY - rect.top) / zoomLevel,
        z: 1,
      };

      setSelectionDrag({
        isSelecting: true,
        startPoint,
        currentPoint: startPoint,
        wasSelecting: false,
      });
    },
    [dragState.isDragging, zoomLevel],
  );

  // 드래그 선택 업데이트
  const updateSelectionDrag = useCallback(
    (event: MouseEvent) => {
      if (!selectionDrag.isSelecting || !canvasRef.current) return;

      const rect = canvasRef.current.getBoundingClientRect();
      // zoom 스케일을 고려한 좌표 변환
      const currentPoint = {
        x: (event.clientX - rect.left) / zoomLevel,
        y: (event.clientY - rect.top) / zoomLevel,
        z: 1,
      };

      setSelectionDrag((prev) => ({
        ...prev,
        currentPoint,
      }));

      // 선택 영역 내의 컴포넌트들 찾기
      const selectionRect = {
        left: Math.min(selectionDrag.startPoint.x, currentPoint.x),
        top: Math.min(selectionDrag.startPoint.y, currentPoint.y),
        right: Math.max(selectionDrag.startPoint.x, currentPoint.x),
        bottom: Math.max(selectionDrag.startPoint.y, currentPoint.y),
      };

      // 🆕 visibleComponents만 선택 대상으로 (현재 활성 레이어의 컴포넌트만)
      const selectedIds = visibleComponents
        .filter((comp) => {
          const compRect = {
            left: comp.position.x,
            top: comp.position.y,
            right: comp.position.x + comp.size.width,
            bottom: comp.position.y + comp.size.height,
          };

          return (
            compRect.left < selectionRect.right &&
            compRect.right > selectionRect.left &&
            compRect.top < selectionRect.bottom &&
            compRect.bottom > selectionRect.top
          );
        })
        .map((comp) => comp.id);

      setGroupState((prev) => ({
        ...prev,
        selectedComponents: selectedIds,
      }));
    },
    [selectionDrag.isSelecting, selectionDrag.startPoint, visibleComponents, zoomLevel],
  );

  // 드래그 선택 종료
  const endSelectionDrag = useCallback(() => {
    // 최소 드래그 거리 확인 (5픽셀)
    const minDragDistance = 5;
    const dragDistance = Math.sqrt(
      Math.pow(selectionDrag.currentPoint.x - selectionDrag.startPoint.x, 2) +
        Math.pow(selectionDrag.currentPoint.y - selectionDrag.startPoint.y, 2),
    );

    const wasActualDrag = dragDistance > minDragDistance;

    setSelectionDrag({
      isSelecting: false,
      startPoint: { x: 0, y: 0, z: 1 },
      currentPoint: { x: 0, y: 0, z: 1 },
      wasSelecting: wasActualDrag, // 실제 드래그였을 때만 클릭 이벤트 무시
    });

    // 짧은 시간 후 wasSelecting을 false로 리셋
    setTimeout(() => {
      setSelectionDrag((prev) => ({
        ...prev,
        wasSelecting: false,
      }));
    }, 100);
  }, [selectionDrag.currentPoint, selectionDrag.startPoint]);

  // 컴포넌트 삭제 (단일/다중 선택 지원)
  const deleteComponent = useCallback(() => {
    // 다중 선택된 컴포넌트가 있는 경우
    if (groupState.selectedComponents.length > 0) {
      // console.log("🗑️ 다중 컴포넌트 삭제:", groupState.selectedComponents.length, "개");

      let newComponents = [...layout.components];

      // 각 선택된 컴포넌트를 삭제 처리
      groupState.selectedComponents.forEach((componentId) => {
        const component = layout.components.find((comp) => comp.id === componentId);
        if (!component) return;

        if (component.type === "group") {
          // 그룹 삭제 시: 자식 컴포넌트들의 절대 위치 복원
          const childComponents = newComponents.filter((comp) => comp.parentId === component.id);
          const restoredChildren = restoreAbsolutePositions(childComponents, component.position);

          newComponents = newComponents
            .map((comp) => {
              if (comp.parentId === component.id) {
                // 복원된 절대 위치로 업데이트
                const restoredChild = restoredChildren.find((restored) => restored.id === comp.id);
                return restoredChild || { ...comp, parentId: undefined };
              }
              return comp;
            })
            .filter((comp) => comp.id !== component.id); // 그룹 컴포넌트 제거
        } else {
          // 일반 컴포넌트 삭제
          newComponents = newComponents.filter((comp) => comp.id !== component.id);
        }
      });

      const newLayout = { ...layout, components: newComponents };
      setLayout(newLayout);
      saveToHistory(newLayout);

      // 선택 상태 초기화
      setSelectedComponent(null);
      setGroupState((prev) => ({ ...prev, selectedComponents: [] }));

      toast.success(`${groupState.selectedComponents.length}개 컴포넌트가 삭제되었습니다.`);
      return;
    }

    // 단일 선택된 컴포넌트 삭제
    if (!selectedComponent) return;

    // console.log("🗑️ 단일 컴포넌트 삭제:", selectedComponent.id);

    let newComponents;

    if (selectedComponent.type === "group") {
      // 그룹 삭제 시: 자식 컴포넌트들의 절대 위치 복원 후 그룹 삭제
      const childComponents = layout.components.filter((comp) => comp.parentId === selectedComponent.id);
      const restoredChildren = restoreAbsolutePositions(childComponents, selectedComponent.position);

      newComponents = layout.components
        .map((comp) => {
          if (comp.parentId === selectedComponent.id) {
            // 복원된 절대 위치로 업데이트
            const restoredChild = restoredChildren.find((restored) => restored.id === comp.id);
            return restoredChild || { ...comp, parentId: undefined };
          }
          return comp;
        })
        .filter((comp) => comp.id !== selectedComponent.id); // 그룹 컴포넌트 제거
    } else {
      // 일반 컴포넌트 삭제
      newComponents = layout.components.filter((comp) => comp.id !== selectedComponent.id);
    }

    const newLayout = { ...layout, components: newComponents };

    setLayout(newLayout);
    saveToHistory(newLayout);
    setSelectedComponent(null);
    toast.success("컴포넌트가 삭제되었습니다.");
  }, [selectedComponent, groupState.selectedComponents, layout, saveToHistory]);

  // 컴포넌트 복사
  const copyComponent = useCallback(() => {
    if (groupState.selectedComponents.length > 0) {
      // 다중 선택된 컴포넌트들 복사
      const componentsToCopy = layout.components.filter((comp) => groupState.selectedComponents.includes(comp.id));
      setClipboard(componentsToCopy);
      // console.log("다중 컴포넌트 복사:", componentsToCopy.length, "개");
      toast.success(`${componentsToCopy.length}개 컴포넌트가 복사되었습니다.`);
    } else if (selectedComponent) {
      // 단일 컴포넌트 복사
      setClipboard([selectedComponent]);
      // console.log("단일 컴포넌트 복사:", selectedComponent.id);
      toast.success("컴포넌트가 복사되었습니다.");
    }
  }, [selectedComponent, groupState.selectedComponents, layout.components]);

  // 컴포넌트 붙여넣기
  const pasteComponent = useCallback(() => {
    if (clipboard.length === 0) {
      toast.warning("복사된 컴포넌트가 없습니다.");
      return;
    }

    const newComponents: ComponentData[] = [];
    const offset = 20; // 붙여넣기 시 위치 오프셋

    clipboard.forEach((clipComponent, index) => {
      const newComponent: ComponentData = {
        ...clipComponent,
        id: generateComponentId(),
        position: {
          x: clipComponent.position.x + offset + index * 10,
          y: clipComponent.position.y + offset + index * 10,
          z: clipComponent.position.z || 1,
        } as Position,
        parentId: undefined, // 붙여넣기 시 부모 관계 해제
        layerId: activeLayerIdRef.current || 1, // 🆕 현재 활성 레이어에 붙여넣기 (ref 사용)
      };
      newComponents.push(newComponent);
    });

    const newLayout = {
      ...layout,
      components: [...layout.components, ...newComponents],
    };

    setLayout(newLayout);
    saveToHistory(newLayout);

    // 붙여넣은 컴포넌트들을 선택 상태로 만들기
    setGroupState((prev) => ({
      ...prev,
      selectedComponents: newComponents.map((comp) => comp.id),
    }));

    // console.log("컴포넌트 붙여넣기 완료:", newComponents.length, "개");
    toast.success(`${newComponents.length}개 컴포넌트가 붙여넣어졌습니다.`);
  }, [clipboard, layout, saveToHistory]);

  // 🆕 플로우 버튼 그룹 생성 (다중 선택된 버튼들을 한 번에 그룹으로)
  // 🆕 플로우 버튼 그룹 다이얼로그 상태
  const [groupDialogOpen, setGroupDialogOpen] = useState(false);

  const handleFlowButtonGroup = useCallback(() => {
    const selectedComponents = layout.components.filter((comp) => groupState.selectedComponents.includes(comp.id));

    // 선택된 컴포넌트가 없거나 1개 이하면 그룹화 불가
    if (selectedComponents.length < 2) {
      toast.error("그룹으로 묶을 버튼을 2개 이상 선택해주세요");
      return;
    }

    // 모두 버튼인지 확인
    if (!areAllButtons(selectedComponents)) {
      toast.error("버튼 컴포넌트만 그룹으로 묶을 수 있습니다");
      return;
    }

    // 🆕 다이얼로그 열기
    setGroupDialogOpen(true);
  }, [layout, groupState.selectedComponents]);

  // 🆕 그룹 생성 확인 핸들러
  const handleGroupConfirm = useCallback(
    (settings: {
      direction: "horizontal" | "vertical";
      gap: number;
      align: "start" | "center" | "end" | "space-between" | "space-around";
    }) => {
      const selectedComponents = layout.components.filter((comp) => groupState.selectedComponents.includes(comp.id));

      // 고유한 그룹 ID 생성
      const newGroupId = generateGroupId();

      // 🔧 그룹 위치 및 버튼 재배치 계산
      const align = settings.align;
      const direction = settings.direction;
      const gap = settings.gap;

      const groupY = Math.min(...selectedComponents.map((b) => b.position.y));
      let anchorButton; // 기준이 되는 버튼
      let groupX: number;

      // align에 따라 기준 버튼과 그룹 시작점 결정
      if (direction === "horizontal") {
        if (align === "end") {
          // 끝점 정렬: 가장 오른쪽 버튼이 기준
          anchorButton = selectedComponents.reduce((max, btn) => {
            const rightEdge = btn.position.x + (btn.size?.width || 100);
            const maxRightEdge = max.position.x + (max.size?.width || 100);
            return rightEdge > maxRightEdge ? btn : max;
          });

          // 전체 그룹 너비 계산
          const totalWidth = selectedComponents.reduce((total, btn, index) => {
            const buttonWidth = btn.size?.width || 100;
            const gapWidth = index < selectedComponents.length - 1 ? gap : 0;
            return total + buttonWidth + gapWidth;
          }, 0);

          // 그룹 시작점 = 기준 버튼의 오른쪽 끝 - 전체 그룹 너비
          groupX = anchorButton.position.x + (anchorButton.size?.width || 100) - totalWidth;
        } else if (align === "center") {
          // 중앙 정렬: 버튼들의 중심점을 기준으로
          const minX = Math.min(...selectedComponents.map((b) => b.position.x));
          const maxX = Math.max(...selectedComponents.map((b) => b.position.x + (b.size?.width || 100)));
          const centerX = (minX + maxX) / 2;

          const totalWidth = selectedComponents.reduce((total, btn, index) => {
            const buttonWidth = btn.size?.width || 100;
            const gapWidth = index < selectedComponents.length - 1 ? gap : 0;
            return total + buttonWidth + gapWidth;
          }, 0);

          groupX = centerX - totalWidth / 2;
          anchorButton = selectedComponents[0]; // 중앙 정렬은 첫 번째 버튼 기준
        } else {
          // 시작점 정렬: 가장 왼쪽 버튼이 기준
          anchorButton = selectedComponents.reduce((min, btn) => {
            return btn.position.x < min.position.x ? btn : min;
          });
          groupX = anchorButton.position.x;
        }
      } else {
        // 세로 정렬: 가장 위쪽 버튼이 기준
        anchorButton = selectedComponents.reduce((min, btn) => {
          return btn.position.y < min.position.y ? btn : min;
        });
        groupX = Math.min(...selectedComponents.map((b) => b.position.x));
      }

      // 🔧 버튼들의 위치를 그룹 기준으로 재배치
      // 기준 버튼의 절대 위치를 유지하고, FlexBox가 나머지를 자동 정렬
      const groupedButtons = selectedComponents.map((button) => {
        const currentConfig = (button as any).webTypeConfig?.flowVisibilityConfig || {};

        // 모든 버튼을 그룹 시작점에 배치
        // FlexBox가 자동으로 정렬하여 기준 버튼의 위치가 유지됨
        const newPosition = {
          x: groupX,
          y: groupY,
          z: button.position.z || 1,
        };

        return {
          ...button,
          position: newPosition,
          webTypeConfig: {
            ...(button as any).webTypeConfig,
            flowVisibilityConfig: {
              ...currentConfig,
              enabled: true,
              layoutBehavior: "auto-compact",
              groupId: newGroupId,
              groupDirection: settings.direction,
              groupGap: settings.gap,
              groupAlign: settings.align,
            },
          },
        };
      });

      // 레이아웃 업데이트
      const updatedComponents = layout.components.map((comp) => {
        const grouped = groupedButtons.find((gb) => gb.id === comp.id);
        return grouped || comp;
      });

      const newLayout = {
        ...layout,
        components: updatedComponents,
      };

      setLayout(newLayout);
      saveToHistory(newLayout);

      toast.success(`${selectedComponents.length}개의 버튼이 플로우 그룹으로 묶였습니다`, {
        description: `그룹 ID: ${newGroupId} / ${settings.direction === "horizontal" ? "가로" : "세로"} / ${settings.gap}px 간격`,
      });

      console.log("✅ 플로우 버튼 그룹 생성 완료:", {
        groupId: newGroupId,
        buttonCount: selectedComponents.length,
        buttons: selectedComponents.map((b) => ({ id: b.id, position: b.position })),
        groupPosition: { x: groupX, y: groupY },
        settings,
      });
    },
    [layout, groupState.selectedComponents, saveToHistory],
  );

  // 🆕 플로우 버튼 그룹 해제
  const handleFlowButtonUngroup = useCallback(() => {
    const selectedComponents = layout.components.filter((comp) => groupState.selectedComponents.includes(comp.id));

    if (selectedComponents.length === 0) {
      toast.error("그룹 해제할 버튼을 선택해주세요");
      return;
    }

    // 버튼이 아닌 것 필터링
    const buttons = selectedComponents.filter((comp) => areAllButtons([comp]));

    if (buttons.length === 0) {
      toast.error("선택된 버튼 중 그룹화된 버튼이 없습니다");
      return;
    }

    // 그룹 해제
    const ungroupedButtons = ungroupButtons(buttons);

    // 레이아웃 업데이트 + 플로우 표시 제어 초기화
    const updatedComponents = layout.components.map((comp, index) => {
      const ungrouped = ungroupedButtons.find((ub) => ub.id === comp.id);

      if (ungrouped) {
        // 원래 위치 복원 또는 현재 위치 유지 + 간격 추가
        const buttonIndex = buttons.findIndex((b) => b.id === comp.id);
        const basePosition = comp.position;

        // 버튼들을 오른쪽으로 조금씩 이동 (겹치지 않도록)
        const offsetX = buttonIndex * 120; // 각 버튼당 120px 간격

        // 그룹 해제된 버튼의 플로우 표시 제어를 끄고 설정 초기화
        return {
          ...ungrouped,
          position: {
            x: basePosition.x + offsetX,
            y: basePosition.y,
            z: basePosition.z || 1,
          },
          webTypeConfig: {
            ...ungrouped.webTypeConfig,
            flowVisibilityConfig: {
              enabled: false,
              targetFlowComponentId: null,
              mode: "whitelist",
              visibleSteps: [],
              hiddenSteps: [],
              layoutBehavior: "auto-compact",
              groupId: null,
              groupDirection: "horizontal",
              groupGap: 8,
              groupAlign: "start",
            },
          },
        };
      }

      return comp;
    });

    const newLayout = {
      ...layout,
      components: updatedComponents,
    };

    setLayout(newLayout);
    saveToHistory(newLayout);

    toast.success(`${buttons.length}개의 버튼 그룹이 해제되고 플로우 표시 제어가 비활성화되었습니다`);
  }, [layout, groupState.selectedComponents, saveToHistory]);

  // 그룹 생성 (임시 비활성화)
  const handleGroupCreate = useCallback(
    (componentIds: string[], title: string, style?: any) => {
      // console.log("그룹 생성 기능이 임시 비활성화되었습니다.");
      toast.info("그룹 기능이 임시 비활성화되었습니다.");
      return;

      // 격자 정보 계산
      const currentGridInfo =
        gridInfo ||
        calculateGridInfo(
          1200,
          800,
          layout.gridSettings || {
            columns: 12,
            gap: 16,
            padding: 0,
            snapToGrid: true,
            showGrid: false,
            gridColor: "#d1d5db",
            gridOpacity: 0.5,
          },
        );

      console.log("🔧 그룹 생성 시작:", {
        selectedCount: selectedComponents.length,
        snapToGrid: true,
      });

      // 컴포넌트 크기 조정 기반 그룹 크기 계산
      const calculateOptimalGroupSize = () => {
        if (!currentGridInfo || !layout.gridSettings?.snapToGrid) {
          // 격자 스냅이 비활성화된 경우 기본 패딩 사용
          const boundingBox = calculateBoundingBox(selectedComponents);
          const padding = 40;
          return {
            boundingBox,
            groupPosition: { x: boundingBox.minX - padding, y: boundingBox.minY - padding, z: 1 },
            groupSize: { width: boundingBox.width + padding * 2, height: boundingBox.height + padding * 2 },
            gridColumns: 1,
            scaledComponents: selectedComponents, // 크기 조정 없음
            padding: padding,
          };
        }

        const { columnWidth } = currentGridInfo;
        const gap = layout.gridSettings?.gap || 16;
        const contentBoundingBox = calculateBoundingBox(selectedComponents);

        // 1. 간단한 접근: 컴포넌트들의 시작점에서 가장 가까운 격자 시작점 찾기
        const startColumn = Math.floor(contentBoundingBox.minX / (columnWidth + gap));

        // 2. 컴포넌트들의 끝점까지 포함할 수 있는 컬럼 수 계산
        const groupStartX = startColumn * (columnWidth + gap);
        const availableWidthFromStart = contentBoundingBox.maxX - groupStartX;
        const currentWidthInColumns = Math.ceil(availableWidthFromStart / (columnWidth + gap));

        // 2. 그룹은 격자에 정확히 맞게 위치와 크기 설정
        const padding = 20;
        const groupX = startColumn * (columnWidth + gap); // 격자 시작점에 정확히 맞춤
        const groupY = contentBoundingBox.minY - padding;
        const groupWidth = currentWidthInColumns * columnWidth + (currentWidthInColumns - 1) * gap; // 컬럼 크기 + gap
        const groupHeight = contentBoundingBox.height + padding * 2;

        // 4. 내부 컴포넌트들을 그룹 크기에 맞게 스케일링
        const availableWidth = groupWidth - padding * 2; // 패딩 제외한 실제 사용 가능 너비
        const scaleFactorX = availableWidth / contentBoundingBox.width;

        const scaledComponents = selectedComponents.map((comp) => {
          // 컴포넌트의 원래 위치에서 컨텐츠 영역 시작점까지의 상대 위치 계산
          const relativeX = comp.position.x - contentBoundingBox.minX;
          const relativeY = comp.position.y - contentBoundingBox.minY;

          return {
            ...comp,
            position: {
              x: padding + relativeX * scaleFactorX, // 패딩 + 스케일된 상대 위치
              y: padding + relativeY, // Y는 스케일링 없이 패딩만 적용
              z: comp.position.z || 1,
            },
            size: {
              width: comp.size.width * scaleFactorX, // X 방향 스케일링
              height: comp.size.height, // Y는 원본 크기 유지
            },
          };
        });

        console.log("🎯 컴포넌트 크기 조정 기반 그룹 생성:", {
          originalBoundingBox: contentBoundingBox,
          gridCalculation: {
            columnWidthPlusGap: columnWidth + gap,
            startColumn: `Math.floor(${contentBoundingBox.minX} / ${columnWidth + gap}) = ${startColumn}`,
            groupStartX: `${startColumn} * ${columnWidth + gap} = ${groupStartX}`,
            availableWidthFromStart: `${contentBoundingBox.maxX} - ${groupStartX} = ${availableWidthFromStart}`,
            currentWidthInColumns: `Math.ceil(${availableWidthFromStart} / ${columnWidth + gap}) = ${currentWidthInColumns}`,
            finalGroupX: `${startColumn} * ${columnWidth + gap} = ${groupX}`,
            actualGroupWidth: `${currentWidthInColumns}컬럼 * ${columnWidth}px + ${currentWidthInColumns - 1}gap * ${gap}px = ${groupWidth}px`,
          },
          groupPosition: { x: groupX, y: groupY },
          groupSize: { width: groupWidth, height: groupHeight },
          scaleFactorX,
          availableWidth,
          padding,
          scaledComponentsCount: scaledComponents.length,
          scaledComponentsDetails: scaledComponents.map((comp) => {
            const original = selectedComponents.find((c) => c.id === comp.id);
            return {
              id: comp.id,
              originalPos: original?.position,
              scaledPos: comp.position,
              originalSize: original?.size,
              scaledSize: comp.size,
              deltaX: comp.position.x - (original?.position.x || 0),
              deltaY: comp.position.y - (original?.position.y || 0),
            };
          }),
        });

        return {
          boundingBox: contentBoundingBox,
          groupPosition: { x: groupX, y: groupY, z: 1 },
          groupSize: { width: groupWidth, height: groupHeight },
          gridColumns: currentWidthInColumns,
          scaledComponents: scaledComponents, // 스케일된 컴포넌트들
          padding: padding,
        };
      };

      const {
        boundingBox,
        groupPosition,
        groupSize: optimizedGroupSize,
        gridColumns,
        scaledComponents,
        padding,
      } = calculateOptimalGroupSize();

      // 스케일된 컴포넌트들로 상대 위치 계산 (이미 최적화되어 추가 격자 정렬 불필요)
      const relativeChildren = calculateRelativePositions(
        scaledComponents,
        groupPosition,
        "temp", // 임시 그룹 ID
      );

      console.log("📏 최적화된 그룹 생성 (컴포넌트 스케일링):", {
        gridColumns,
        groupSize: optimizedGroupSize,
        groupPosition,
        scaledComponentsCount: scaledComponents.length,
        padding,
        strategy: "내부 컴포넌트 크기 조정으로 격자 정확 맞춤",
      });

      // 그룹 컴포넌트 생성 (gridColumns 속성 포함)
      const groupComponent = createGroupComponent(componentIds, title, groupPosition, optimizedGroupSize, style);

      // 그룹에 계산된 gridColumns 속성 추가
      groupComponent.gridColumns = gridColumns;

      // 실제 그룹 ID로 자식들 업데이트
      const finalChildren = relativeChildren.map((child) => ({
        ...child,
        parentId: groupComponent.id,
      }));

      const newLayout = {
        ...layout,
        components: [
          ...layout.components.filter((comp) => !componentIds.includes(comp.id)),
          groupComponent,
          ...finalChildren,
        ],
      };

      setLayout(newLayout);
      saveToHistory(newLayout);
      setGroupState((prev) => ({
        ...prev,
        selectedComponents: [groupComponent.id],
        isGrouping: false,
      }));
      setSelectedComponent(groupComponent);

      console.log("🎯 최적화된 그룹 생성 완료:", {
        groupId: groupComponent.id,
        childrenCount: finalChildren.length,
        position: groupPosition,
        size: optimizedGroupSize,
        gridColumns: groupComponent.gridColumns,
        componentsScaled: !!scaledComponents.length,
        gridAligned: true,
      });

      toast.success(`그룹이 생성되었습니다 (${finalChildren.length}개 컴포넌트)`);
    },
    [layout, saveToHistory],
  );

  // 그룹 생성 함수 (다이얼로그 표시)
  const createGroup = useCallback(() => {
    if (groupState.selectedComponents.length < 2) {
      toast.warning("그룹을 만들려면 2개 이상의 컴포넌트를 선택해야 합니다.");
      return;
    }

    // console.log("🔄 그룹 생성 다이얼로그 표시");
    setShowGroupCreateDialog(true);
  }, [groupState.selectedComponents]);

  // 그룹 해제 함수 (임시 비활성화)
  const ungroupComponents = useCallback(() => {
    // console.log("그룹 해제 기능이 임시 비활성화되었습니다.");
    toast.info("그룹 해제 기능이 임시 비활성화되었습니다.");
    return;

    const groupId = selectedComponent.id;

    // 자식 컴포넌트들의 절대 위치 복원
    const childComponents = layout.components.filter((comp) => comp.parentId === groupId);
    const restoredChildren = restoreAbsolutePositions(childComponents, selectedComponent.position);

    // 자식 컴포넌트들의 위치 복원 및 parentId 제거
    const updatedComponents = layout.components
      .map((comp) => {
        if (comp.parentId === groupId) {
          const restoredChild = restoredChildren.find((restored) => restored.id === comp.id);
          return restoredChild || { ...comp, parentId: undefined };
        }
        return comp;
      })
      .filter((comp) => comp.id !== groupId); // 그룹 컴포넌트 제거

    const newLayout = { ...layout, components: updatedComponents };
    setLayout(newLayout);
    saveToHistory(newLayout);

    // 선택 상태 초기화
    setSelectedComponent(null);
    setGroupState((prev) => ({ ...prev, selectedComponents: [] }));
  }, [selectedComponent, layout, saveToHistory]);

  // 마우스 이벤트 처리 (드래그 및 선택) - 성능 최적화
  useEffect(() => {
    let animationFrameId: number;

    const handleMouseMove = (e: MouseEvent) => {
      if (dragState.isDragging) {
        // requestAnimationFrame으로 부드러운 애니메이션
        if (animationFrameId) {
          cancelAnimationFrame(animationFrameId);
        }
        animationFrameId = requestAnimationFrame(() => {
          updateDragPosition(e);
        });
      } else if (selectionDrag.isSelecting) {
        updateSelectionDrag(e);
      }
    };

    const handleMouseUp = (e: MouseEvent) => {
      if (dragState.isDragging) {
        if (animationFrameId) {
          cancelAnimationFrame(animationFrameId);
        }
        endDrag(e);
      } else if (selectionDrag.isSelecting) {
        endSelectionDrag();
      }
    };

    if (dragState.isDragging || selectionDrag.isSelecting) {
      document.addEventListener("mousemove", handleMouseMove, { passive: true });
      document.addEventListener("mouseup", handleMouseUp);

      return () => {
        if (animationFrameId) {
          cancelAnimationFrame(animationFrameId);
        }
        document.removeEventListener("mousemove", handleMouseMove);
        document.removeEventListener("mouseup", handleMouseUp);
      };
    }
  }, [
    dragState.isDragging,
    selectionDrag.isSelecting,
    updateDragPosition,
    endDrag,
    updateSelectionDrag,
    endSelectionDrag,
  ]);

  // 키보드 이벤트 처리 (브라우저 기본 기능 완전 차단)
  useEffect(() => {
    const handleKeyDown = async (e: KeyboardEvent) => {
      // console.log("🎯 키 입력 감지:", { key: e.key, ctrlKey: e.ctrlKey, shiftKey: e.shiftKey, metaKey: e.metaKey });

      // 🚫 브라우저 기본 단축키 완전 차단 목록
      const browserShortcuts = [
        // 검색 관련
        { ctrl: true, key: "f" }, // 페이지 내 검색
        { ctrl: true, key: "g" }, // 다음 검색 결과
        { ctrl: true, shift: true, key: "g" }, // 이전 검색 결과
        { ctrl: true, key: "h" }, // 검색 기록

        // 탭/창 관리
        { ctrl: true, key: "t" }, // 새 탭
        { ctrl: true, key: "w" }, // 탭 닫기
        { ctrl: true, shift: true, key: "t" }, // 닫힌 탭 복원
        { ctrl: true, key: "n" }, // 새 창
        { ctrl: true, shift: true, key: "n" }, // 시크릿 창

        // 페이지 관리
        { ctrl: true, key: "r" }, // 새로고침
        { ctrl: true, shift: true, key: "r" }, // 강제 새로고침
        { ctrl: true, key: "d" }, // 북마크 추가
        { ctrl: true, shift: true, key: "d" }, // 모든 탭 북마크

        // 편집 관련 (필요시에만 허용)
        { ctrl: true, key: "s" }, // 저장 (필요시 차단 해제)
        { ctrl: true, key: "p" }, // 인쇄
        { ctrl: true, key: "o" }, // 파일 열기
        { ctrl: true, key: "v" }, // 붙여넣기 (브라우저 기본 동작 차단)

        // 개발자 도구
        { key: "F12" }, // 개발자 도구
        { ctrl: true, shift: true, key: "i" }, // 개발자 도구
        { ctrl: true, shift: true, key: "c" }, // 요소 검사
        { ctrl: true, shift: true, key: "j" }, // 콘솔
        { ctrl: true, key: "u" }, // 소스 보기

        // 기타
        { ctrl: true, key: "j" }, // 다운로드
        { ctrl: true, shift: true, key: "delete" }, // 브라우징 데이터 삭제
        { ctrl: true, key: "+" }, // 확대
        { ctrl: true, key: "-" }, // 축소
        { ctrl: true, key: "0" }, // 확대/축소 초기화
      ];

      // 브라우저 기본 단축키 체크 및 차단
      const isBrowserShortcut = browserShortcuts.some((shortcut) => {
        const ctrlMatch = shortcut.ctrl ? e.ctrlKey || e.metaKey : true;
        const shiftMatch = shortcut.shift ? e.shiftKey : !e.shiftKey;
        const keyMatch = e.key?.toLowerCase() === shortcut.key?.toLowerCase();
        return ctrlMatch && shiftMatch && keyMatch;
      });

      if (isBrowserShortcut) {
        // console.log("🚫 브라우저 기본 단축키 차단:", e.key);
        e.preventDefault();
        e.stopPropagation();
        e.stopImmediatePropagation();
      }

      // ✅ 애플리케이션 전용 단축키 처리

      // 1. 그룹 관련 단축키
      if ((e.ctrlKey || e.metaKey) && e.key?.toLowerCase() === "g" && !e.shiftKey) {
        // console.log("🔄 그룹 생성 단축키");
        e.preventDefault();
        e.stopPropagation();
        e.stopImmediatePropagation();

        if (groupState.selectedComponents.length >= 2) {
          // console.log("✅ 그룹 생성 실행");
          createGroup();
        } else {
          // console.log("⚠️ 선택된 컴포넌트가 부족함 (2개 이상 필요)");
        }
        return false;
      }

      if ((e.ctrlKey || e.metaKey) && e.shiftKey && e.key?.toLowerCase() === "g") {
        // console.log("🔄 그룹 해제 단축키");
        e.preventDefault();
        e.stopPropagation();
        e.stopImmediatePropagation();

        if (selectedComponent && selectedComponent.type === "group") {
          // console.log("✅ 그룹 해제 실행");
          ungroupComponents();
        } else {
          // console.log("⚠️ 선택된 그룹이 없음");
        }
        return false;
      }

      // 2. 전체 선택 (애플리케이션 내에서만)
      if ((e.ctrlKey || e.metaKey) && e.key?.toLowerCase() === "a") {
        // console.log("🔄 전체 선택 (애플리케이션 내)");
        e.preventDefault();
        e.stopPropagation();
        e.stopImmediatePropagation();
        const allComponentIds = layout.components.map((comp) => comp.id);
        setGroupState((prev) => ({ ...prev, selectedComponents: allComponentIds }));
        return false;
      }

      // 3. 실행취소/다시실행
      if ((e.ctrlKey || e.metaKey) && e.key?.toLowerCase() === "z" && !e.shiftKey) {
        // console.log("🔄 실행취소");
        e.preventDefault();
        e.stopPropagation();
        e.stopImmediatePropagation();
        undo();
        return false;
      }

      if (
        ((e.ctrlKey || e.metaKey) && e.key?.toLowerCase() === "y") ||
        ((e.ctrlKey || e.metaKey) && e.shiftKey && e.key?.toLowerCase() === "z")
      ) {
        // console.log("🔄 다시실행");
        e.preventDefault();
        e.stopPropagation();
        e.stopImmediatePropagation();
        redo();
        return false;
      }

      // 4. 복사 (컴포넌트 복사)
      if ((e.ctrlKey || e.metaKey) && e.key?.toLowerCase() === "c") {
        // console.log("🔄 컴포넌트 복사");
        e.preventDefault();
        e.stopPropagation();
        e.stopImmediatePropagation();
        copyComponent();
        return false;
      }

      // 5. 붙여넣기 (컴포넌트 붙여넣기)
      if ((e.ctrlKey || e.metaKey) && e.key?.toLowerCase() === "v") {
        // console.log("🔄 컴포넌트 붙여넣기");
        e.preventDefault();
        e.stopPropagation();
        e.stopImmediatePropagation();
        pasteComponent();
        return false;
      }

      // 6. 삭제 (단일/다중 선택 지원) - Delete 또는 Backspace(Mac)
      const isInputFocused = document.activeElement instanceof HTMLInputElement || 
                             document.activeElement instanceof HTMLTextAreaElement ||
                             document.activeElement instanceof HTMLSelectElement ||
                             (document.activeElement as HTMLElement)?.isContentEditable;
      if ((e.key === "Delete" || (e.key === "Backspace" && !isInputFocused)) && (selectedComponent || groupState.selectedComponents.length > 0)) {
        // console.log("🗑️ 컴포넌트 삭제 (단축키)");
        e.preventDefault();
        e.stopPropagation();
        e.stopImmediatePropagation();
        deleteComponent();
        return false;
      }

      // 7. 선택 해제
      if (e.key === "Escape") {
        // console.log("🔄 선택 해제");
        setSelectedComponent(null);
        setGroupState((prev) => ({ ...prev, selectedComponents: [], isGrouping: false }));
        return false;
      }

      // 8. 저장 (Ctrl+S는 레이아웃 저장용으로 사용)
      if ((e.ctrlKey || e.metaKey) && e.key?.toLowerCase() === "s") {
        // console.log("💾 레이아웃 저장");
        e.preventDefault();
        e.stopPropagation();
        e.stopImmediatePropagation();

        // 레이아웃 저장 실행
        if (layout.components.length > 0 && selectedScreen?.screenId) {
          setIsSaving(true);
          try {
            const currentMainTableName = tables.length > 0 ? tables[0].tableName : null;

            const layoutWithResolution = {
              ...layout,
              screenResolution: screenResolution,
              mainTableName: currentMainTableName,
            };
            // V2/POP API 사용 여부에 따라 분기
            const v2Layout = convertLegacyToV2(layoutWithResolution);
            if (USE_POP_API) {
              await screenApi.saveLayoutPop(selectedScreen.screenId, v2Layout);
            } else if (USE_V2_API) {
              const currentLayerId = activeLayerIdRef.current || 1;
              await screenApi.saveLayoutV2(selectedScreen.screenId, {
                ...v2Layout,
                layerId: currentLayerId,
                mainTableName: currentMainTableName,
              });
            } else {
              await screenApi.saveLayout(selectedScreen.screenId, layoutWithResolution);
            }

            if (currentMainTableName && currentMainTableName !== selectedScreen.tableName) {
              await screenApi.updateScreenTableName(selectedScreen.screenId, currentMainTableName);
            }

            toast.success("레이아웃이 저장되었습니다.");

            if (onScreenUpdate && currentMainTableName) {
              onScreenUpdate({ tableName: currentMainTableName });
            }
          } catch (error) {
            toast.error("레이아웃 저장에 실패했습니다.");
          } finally {
            setIsSaving(false);
          }
        } else {
          // console.log("⚠️ 저장할 컴포넌트가 없습니다");
          toast.warning("저장할 컴포넌트가 없습니다.");
        }
        return false;
      }

      // === 9. 화살표 키 Nudge (컴포넌트 미세 이동) ===
      if (["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight"].includes(e.key)) {
        // 입력 필드에서는 무시
        const active = document.activeElement;
        if (
          active instanceof HTMLInputElement ||
          active instanceof HTMLTextAreaElement ||
          active?.getAttribute("contenteditable") === "true"
        ) {
          return;
        }

        if (selectedComponent || groupState.selectedComponents.length > 0) {
          e.preventDefault();
          e.stopPropagation();
          e.stopImmediatePropagation();
          const distance = e.shiftKey ? 10 : 1; // Shift 누르면 10px
          const dirMap: Record<string, "up" | "down" | "left" | "right"> = {
            ArrowUp: "up", ArrowDown: "down", ArrowLeft: "left", ArrowRight: "right",
          };
          handleNudge(dirMap[e.key], distance);
          return false;
        }
      }

      // === 10. 정렬 단축키 (Alt + 키) - 다중 선택 시 ===
      if (e.altKey && !e.ctrlKey && !e.metaKey) {
        const alignKey = e.key?.toLowerCase();
        const alignMap: Record<string, AlignMode> = {
          l: "left", r: "right", c: "centerX",
          t: "top", b: "bottom", m: "centerY",
        };

        if (alignMap[alignKey] && groupState.selectedComponents.length >= 2) {
          e.preventDefault();
          e.stopPropagation();
          e.stopImmediatePropagation();
          handleGroupAlign(alignMap[alignKey]);
          return false;
        }

        // 균등 배분 (Alt+H: 가로, Alt+V: 세로)
        if (alignKey === "h" && groupState.selectedComponents.length >= 3) {
          e.preventDefault();
          e.stopPropagation();
          e.stopImmediatePropagation();
          handleGroupDistribute("horizontal");
          return false;
        }
        if (alignKey === "v" && groupState.selectedComponents.length >= 3) {
          e.preventDefault();
          e.stopPropagation();
          e.stopImmediatePropagation();
          handleGroupDistribute("vertical");
          return false;
        }

        // 동일 크기 맞추기 (Alt+W: 너비, Alt+E: 높이)
        if (alignKey === "w" && groupState.selectedComponents.length >= 2) {
          e.preventDefault();
          e.stopPropagation();
          e.stopImmediatePropagation();
          handleMatchSize("width");
          return false;
        }
        if (alignKey === "e" && groupState.selectedComponents.length >= 2) {
          e.preventDefault();
          e.stopPropagation();
          e.stopImmediatePropagation();
          handleMatchSize("height");
          return false;
        }
      }

      // === 11. 라벨 일괄 토글 (Alt+Shift+L) ===
      if (e.altKey && e.shiftKey && e.key?.toLowerCase() === "l") {
        e.preventDefault();
        e.stopPropagation();
        e.stopImmediatePropagation();
        handleToggleAllLabels();
        return false;
      }

      // === 12. 단축키 도움말 (? 키) ===
      if (e.key === "?" && !e.ctrlKey && !e.metaKey && !e.altKey) {
        // 입력 필드에서는 무시
        const active = document.activeElement;
        if (
          active instanceof HTMLInputElement ||
          active instanceof HTMLTextAreaElement ||
          active?.getAttribute("contenteditable") === "true"
        ) {
          return;
        }
        e.preventDefault();
        setShowShortcutsModal(true);
        return false;
      }
    };

    // window 레벨에서 캡처 단계에서 가장 먼저 처리
    window.addEventListener("keydown", handleKeyDown, { capture: true, passive: false });
    return () => window.removeEventListener("keydown", handleKeyDown, { capture: true });
  }, [
    selectedComponent,
    deleteComponent,
    copyComponent,
    pasteComponent,
    undo,
    redo,
    createGroup,
    ungroupComponents,
    groupState.selectedComponents,
    layout,
    selectedScreen,
    handleNudge,
    handleGroupAlign,
    handleGroupDistribute,
    handleMatchSize,
    handleToggleAllLabels,
    tables,
    onScreenUpdate,
  ]);

  // 플로우 위젯 높이 자동 업데이트 이벤트 리스너
  useEffect(() => {
    const handleComponentSizeUpdate = (event: CustomEvent) => {
      const { componentId, height } = event.detail;

      // 해당 컴포넌트 찾기
      const targetComponent = layout.components.find((c) => c.id === componentId);
      if (!targetComponent) {
        return;
      }

      // 이미 같은 높이면 업데이트 안함
      if (targetComponent.size?.height === height) {
        return;
      }

      // 컴포넌트 높이 업데이트
      const updatedComponents = layout.components.map((comp) => {
        if (comp.id === componentId) {
          return {
            ...comp,
            size: {
              ...comp.size,
              width: comp.size?.width || 100,
              height: height,
            },
          };
        }
        return comp;
      });

      const newLayout = {
        ...layout,
        components: updatedComponents,
      };

      setLayout(newLayout);

      // 선택된 컴포넌트도 업데이트
      if (selectedComponent?.id === componentId) {
        const updatedComponent = updatedComponents.find((c) => c.id === componentId);
        if (updatedComponent) {
          setSelectedComponent(updatedComponent);
        }
      }
    };

    window.addEventListener("updateComponentSize", handleComponentSizeUpdate as EventListener);
    return () => {
      window.removeEventListener("updateComponentSize", handleComponentSizeUpdate as EventListener);
    };
  }, [layout, selectedComponent]);

  // 🆕 조건부 영역 드래그 핸들러 (이동/리사이즈, DB 기반)
  const handleRegionMouseDown = useCallback((
    e: React.MouseEvent,
    layerId: string,
    mode: "move" | "resize",
    handle?: string,
  ) => {
    e.stopPropagation();
    e.preventDefault();
    const zoneId = Number(layerId); // layerId는 실제로 zoneId
    const zone = zones.find(z => z.zone_id === zoneId);
    if (!zone) return;

    const canvasRect = canvasRef.current?.getBoundingClientRect();
    if (!canvasRect) return;

    const x = (e.clientX - canvasRect.left) / zoomLevel;
    const y = (e.clientY - canvasRect.top) / zoomLevel;

    setRegionDrag({
      isDrawing: false,
      isDragging: mode === "move",
      isResizing: mode === "resize",
      targetLayerId: String(zoneId),
      startX: x,
      startY: y,
      currentX: x,
      currentY: y,
      resizeHandle: handle || null,
      originalRegion: { x: zone.x, y: zone.y, width: zone.width, height: zone.height },
    });
  }, [zones, zoomLevel]);

  // 🆕 캔버스 마우스 이벤트 (영역 이동/리사이즈)
  const handleRegionCanvasMouseMove = useCallback((e: React.MouseEvent) => {
    if (!regionDrag.isDragging && !regionDrag.isResizing) return;
    if (!regionDrag.targetLayerId) return;

    const canvasRect = canvasRef.current?.getBoundingClientRect();
    if (!canvasRect) return;

    const x = (e.clientX - canvasRect.left) / zoomLevel;
    const y = (e.clientY - canvasRect.top) / zoomLevel;

    if (regionDrag.isDragging && regionDrag.originalRegion) {
      const dx = x - regionDrag.startX;
      const dy = y - regionDrag.startY;
      const newRegion = {
        x: Math.max(0, Math.round(regionDrag.originalRegion.x + dx)),
        y: Math.max(0, Math.round(regionDrag.originalRegion.y + dy)),
        width: regionDrag.originalRegion.width,
        height: regionDrag.originalRegion.height,
      };
      const zoneId = Number(regionDrag.targetLayerId);
      setZones((prev) => prev.map(z => z.zone_id === zoneId ? { ...z, ...newRegion } : z));
    } else if (regionDrag.isResizing && regionDrag.originalRegion) {
      const dx = x - regionDrag.startX;
      const dy = y - regionDrag.startY;
      const orig = regionDrag.originalRegion;
      const newRegion = { ...orig };

      const handle = regionDrag.resizeHandle;
      if (handle?.includes("e")) newRegion.width = Math.max(50, Math.round(orig.width + dx));
      if (handle?.includes("s")) newRegion.height = Math.max(30, Math.round(orig.height + dy));
      if (handle?.includes("w")) {
        newRegion.x = Math.max(0, Math.round(orig.x + dx));
        newRegion.width = Math.max(50, Math.round(orig.width - dx));
      }
      if (handle?.includes("n")) {
        newRegion.y = Math.max(0, Math.round(orig.y + dy));
        newRegion.height = Math.max(30, Math.round(orig.height - dy));
      }

      const zoneId = Number(regionDrag.targetLayerId);
      setZones((prev) => prev.map(z => z.zone_id === zoneId ? { ...z, ...newRegion } : z));
    }
  }, [regionDrag, zoomLevel]);

  const handleRegionCanvasMouseUp = useCallback(async () => {
    // 드래그 완료 시 DB에 Zone 저장
    if ((regionDrag.isDragging || regionDrag.isResizing) && regionDrag.targetLayerId) {
      const zoneId = Number(regionDrag.targetLayerId);
      const zone = zones.find(z => z.zone_id === zoneId);
      if (zone) {
        try {
          await screenApi.updateZone(zoneId, {
            x: zone.x, y: zone.y, width: zone.width, height: zone.height,
          });
        } catch {
          console.error("Zone 저장 실패");
        }
      }
    }
    // 드래그 상태 초기화
    setRegionDrag({
      isDrawing: false,
      isDragging: false,
      isResizing: false,
      targetLayerId: null,
      startX: 0, startY: 0, currentX: 0, currentY: 0,
      resizeHandle: null,
      originalRegion: null,
    });
  }, [regionDrag, zones]);

  // 🆕 레이어 변경 핸들러 - 레이어 컨텍스트에서 레이어가 변경되면 layout에도 반영
  // Zone 기반이므로 displayRegion 보존 불필요
  const handleLayersChange = useCallback((newLayers: LayerDefinition[]) => {
    setLayout((prevLayout) => ({
      ...prevLayout,
      layers: newLayers,
    }));
  }, []);

  // 🆕 활성 레이어 변경 핸들러
  const handleActiveLayerChange = useCallback((newActiveLayerId: number) => {
    setActiveLayerIdWithRef(newActiveLayerId);
  }, [setActiveLayerIdWithRef]);

  // 🆕 초기 레이어 계산 - layout에서 layers가 있으면 사용, 없으면 기본 레이어 생성
  // 주의: components는 layout.components에 layerId 속성으로 저장되므로, layer.components는 비워둠
  const initialLayers = useMemo<LayerDefinition[]>(() => {
    if (layout.layers && layout.layers.length > 0) {
      // 기존 레이어 구조 사용 (layer.components는 무시하고 빈 배열로 설정)
      return layout.layers.map(layer => ({
        ...layer,
        components: [], // layout.components + layerId 방식 사용
      }));
    }
    // layers가 없으면 기본 레이어 생성 (components는 빈 배열)
    return [createDefaultLayer()];
  }, [layout.layers]);

  if (!selectedScreen) {
    return (
      <div className="bg-background flex h-full items-center justify-center">
        <div className="space-y-4 text-center">
          <div className="bg-muted mx-auto flex h-16 w-16 items-center justify-center rounded-full">
            <Database className="text-muted-foreground h-8 w-8" />
          </div>
          <h3 className="text-foreground text-lg font-semibold">화면을 선택하세요</h3>
          <p className="text-muted-foreground max-w-sm text-sm">설계할 화면을 먼저 선택해주세요.</p>
        </div>
      </div>
    );
  }

  // 🔧 ScreenDesigner 렌더링 확인 (디버그 완료 - 주석 처리)
  // console.log("🏠 ScreenDesigner 렌더!", Date.now());

  return (
    <ScreenPreviewProvider isPreviewMode={false}>
      <LayerProvider 
        initialLayers={initialLayers} 
        onLayersChange={handleLayersChange}
        onActiveLayerChange={handleActiveLayerChange}
      >
        <TableOptionsProvider>
        <div className="bg-background flex h-full w-full flex-col">
          {/* 상단 슬림 툴바 */}
          <SlimToolbar
            screenName={selectedScreen?.screenName}
            tableName={selectedScreen?.tableName}
            screenResolution={screenResolution}
            onBack={onBackToList}
            onSave={handleSave}
            isSaving={isSaving}
            onPreview={isPop ? handlePopPreview : undefined}
            onResolutionChange={setScreenResolution}
            gridSettings={layout.gridSettings}
            onGridSettingsChange={updateGridSettings}
            onGenerateMultilang={handleGenerateMultilang}
            isGeneratingMultilang={isGeneratingMultilang}
            onOpenMultilangSettings={() => setShowMultilangSettingsModal(true)}
            isPanelOpen={panelStates.v2?.isOpen || false}
            onTogglePanel={() => togglePanel("v2")}
            selectedCount={groupState.selectedComponents.length}
            onAlign={handleGroupAlign}
            onDistribute={handleGroupDistribute}
            onMatchSize={handleMatchSize}
            onToggleLabels={handleToggleAllLabels}
            onShowShortcuts={() => setShowShortcutsModal(true)}
          />
          {/* 메인 컨테이너 (패널들 + 캔버스) */}
          <div className="flex flex-1 overflow-hidden">
            {/* 통합 패널 - 좌측 사이드바 제거 후 너비 300px로 확장 */}
            {panelStates.v2?.isOpen && (
              <div className="border-border bg-card flex h-full w-[300px] flex-col overflow-hidden border-r shadow-sm">
                <div className="border-border flex shrink-0 items-center justify-between border-b px-4 py-3">
                  <h3 className="text-foreground text-sm font-semibold">패널</h3>
                  <button
                    onClick={() => closePanel("v2")}
                    className="text-muted-foreground hover:text-foreground focus-visible:ring-ring rounded-sm transition-colors focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-none"
                  >
                    ✕
                  </button>
                </div>
                <div className="flex min-h-0 flex-1 flex-col overflow-hidden">
                  <Tabs value={leftPanelTab} onValueChange={setLeftPanelTab} className="flex min-h-0 flex-1 flex-col">
                    <TabsList className="mx-4 mt-2 grid h-8 w-auto grid-cols-3 gap-1">
                      <TabsTrigger value="components" className="text-xs">
                        컴포넌트
                      </TabsTrigger>
                      <TabsTrigger value="layers" className="text-xs">
                        레이어
                      </TabsTrigger>
                      <TabsTrigger value="properties" className="text-xs">
                        편집
                      </TabsTrigger>
                    </TabsList>

                    <TabsContent value="components" className="mt-0 flex-1 overflow-hidden">
                      <ComponentsPanel
                        tables={filteredTables}
                        searchTerm={searchTerm}
                        onSearchChange={setSearchTerm}
                        onTableDragStart={(e, table, column) => {
                          const dragData = {
                            type: column ? "column" : "table",
                            table,
                            column,
                          };
                          e.dataTransfer.setData("application/json", JSON.stringify(dragData));
                        }}
                        selectedTableName={selectedScreen?.tableName}
                        placedColumns={placedColumns}
                        onTableSelect={handleTableSelect}
                        showTableSelector={true}
                      />
                    </TabsContent>

                    {/* 🆕 레이어 관리 탭 (DB 기반) */}
                    <TabsContent value="layers" className="mt-0 flex-1 overflow-hidden">
                      <LayerManagerPanel 
                        screenId={selectedScreen?.screenId || null}
                        activeLayerId={Number(activeLayerIdRef.current) || 1}
                        onLayerChange={async (layerId) => {
                          if (!selectedScreen?.screenId) return;
                          try {
                            // 1. 현재 레이어 저장
                            const curId = Number(activeLayerIdRef.current) || 1;
                            const v2Layout = convertLegacyToV2({ ...layout, screenResolution });
                            await screenApi.saveLayoutV2(selectedScreen.screenId, { ...v2Layout, layerId: curId });
                              
                            // 2. 새 레이어 로드
                            const data = await screenApi.getLayerLayout(selectedScreen.screenId, layerId);
                            if (data && data.components) {
                              const legacy = convertV2ToLegacy(data);
                              if (legacy) {
                                setLayout((prev) => ({ ...prev, components: legacy.components }));
                              } else {
                                setLayout((prev) => ({ ...prev, components: [] }));
                              }
                            } else {
                              setLayout((prev) => ({ ...prev, components: [] }));
                            }
                              
                            setActiveLayerIdWithRef(layerId);
                            setSelectedComponent(null);
                          } catch (error) {
                            console.error("레이어 전환 실패:", error);
                            toast.error("레이어 전환에 실패했습니다.");
                          }
                        }}
                        components={layout.components}
                        zones={zones}
                        onZonesChange={setZones}
                      />
                    </TabsContent>

                    <TabsContent value="properties" className="mt-0 flex-1 overflow-hidden">
                      {/* 탭 내부 컴포넌트 선택 시에도 V2PropertiesPanel 사용 */}
                      {selectedTabComponentInfo ? (
                        (() => {
                          const tabComp = selectedTabComponentInfo.component;

                          // 탭 내부 컴포넌트를 ComponentData 형식으로 변환
                          const tabComponentAsComponentData: ComponentData = {
                            id: tabComp.id,
                            type: "component",
                            componentType: tabComp.componentType,
                            label: tabComp.label,
                            position: tabComp.position || { x: 0, y: 0 },
                            size: tabComp.size || { width: 200, height: 100 },
                            componentConfig: tabComp.componentConfig || {},
                            style: tabComp.style || {},
                          } as ComponentData;

                          // 탭 내부 컴포넌트용 속성 업데이트 핸들러 (중첩 구조 지원)
                          const updateTabComponentProperty = (componentId: string, path: string, value: any) => {
                            const { tabsComponentId, tabId, parentSplitPanelId, parentPanelSide } =
                              selectedTabComponentInfo;

                            console.log("🔧 updateTabComponentProperty 호출:", {
                              componentId,
                              path,
                              value,
                              parentSplitPanelId,
                              parentPanelSide,
                            });

                            // 🆕 안전한 깊은 경로 업데이트 헬퍼 함수
                            const setNestedValue = (obj: any, pathStr: string, val: any): any => {
                              // 깊은 복사로 시작
                              const result = JSON.parse(JSON.stringify(obj));
                              const parts = pathStr.split(".");
                              let current = result;

                              for (let i = 0; i < parts.length - 1; i++) {
                                const part = parts[i];
                                if (!current[part] || typeof current[part] !== "object") {
                                  current[part] = {};
                                }
                                current = current[part];
                              }
                              current[parts[parts.length - 1]] = val;
                              return result;
                            };

                            // 탭 컴포넌트 업데이트 함수
                            const updateTabsComponent = (tabsComponent: any) => {
                              const currentConfig = JSON.parse(JSON.stringify(tabsComponent.componentConfig || {}));
                              const tabs = currentConfig.tabs || [];

                              const updatedTabs = tabs.map((tab: any) => {
                                if (tab.id === tabId) {
                                  return {
                                    ...tab,
                                    components: (tab.components || []).map((comp: any) => {
                                      if (comp.id !== componentId) return comp;

                                      // 🆕 안전한 깊은 경로 업데이트 사용
                                      const updatedComp = setNestedValue(comp, path, value);
                                      console.log("🔧 컴포넌트 업데이트 결과:", updatedComp);
                                      return updatedComp;
                                    }),
                                  };
                                }
                                return tab;
                              });

                              return {
                                ...tabsComponent,
                                componentConfig: { ...currentConfig, tabs: updatedTabs },
                              };
                            };

                            setLayout((prevLayout) => {
                              let newLayout;
                              let updatedTabs;

                              if (parentSplitPanelId && parentPanelSide) {
                                // 🆕 중첩 구조: 분할 패널 안의 탭 업데이트
                                newLayout = {
                                  ...prevLayout,
                                  components: prevLayout.components.map((c) => {
                                    if (c.id === parentSplitPanelId) {
                                      const splitConfig = (c as any).componentConfig || {};
                                      const panelKey = parentPanelSide === "left" ? "leftPanel" : "rightPanel";
                                      const panelConfig = splitConfig[panelKey] || {};
                                      const panelComponents = panelConfig.components || [];

                                      const tabsComponent = panelComponents.find(
                                        (pc: any) => pc.id === tabsComponentId,
                                      );
                                      if (!tabsComponent) return c;

                                      const updatedTabsComponent = updateTabsComponent(tabsComponent);
                                      updatedTabs = updatedTabsComponent.componentConfig.tabs;

                                      return {
                                        ...c,
                                        componentConfig: {
                                          ...splitConfig,
                                          [panelKey]: {
                                            ...panelConfig,
                                            components: panelComponents.map((pc: any) =>
                                              pc.id === tabsComponentId ? updatedTabsComponent : pc,
                                            ),
                                          },
                                        },
                                      };
                                    }
                                    return c;
                                  }),
                                };
                              } else {
                                // 일반 구조: 최상위 탭 업데이트
                                const tabsComponent = prevLayout.components.find((c) => c.id === tabsComponentId);
                                if (!tabsComponent) return prevLayout;

                                const updatedTabsComponent = updateTabsComponent(tabsComponent);
                                updatedTabs = updatedTabsComponent.componentConfig.tabs;

                                newLayout = {
                                  ...prevLayout,
                                  components: prevLayout.components.map((c) =>
                                    c.id === tabsComponentId ? updatedTabsComponent : c,
                                  ),
                                };
                              }

                              // 선택된 컴포넌트 정보 업데이트
                              if (updatedTabs) {
                                const updatedComp = updatedTabs
                                  .find((t: any) => t.id === tabId)
                                  ?.components?.find((c: any) => c.id === componentId);
                                if (updatedComp) {
                                  setSelectedTabComponentInfo((prev) =>
                                    prev ? { ...prev, component: updatedComp } : null,
                                  );
                                }
                              }

                              return newLayout;
                            });
                          };

                          // 탭 내부 컴포넌트 삭제 핸들러 (중첩 구조 지원)
                          const deleteTabComponent = (componentId: string) => {
                            const { tabsComponentId, tabId, parentSplitPanelId, parentPanelSide } =
                              selectedTabComponentInfo;

                            // 탭 컴포넌트에서 특정 컴포넌트 삭제
                            const updateTabsComponentForDelete = (tabsComponent: any) => {
                              const currentConfig = tabsComponent.componentConfig || {};
                              const tabs = currentConfig.tabs || [];

                              const updatedTabs = tabs.map((tab: any) => {
                                if (tab.id === tabId) {
                                  return {
                                    ...tab,
                                    components: (tab.components || []).filter((c: any) => c.id !== componentId),
                                  };
                                }
                                return tab;
                              });

                              return {
                                ...tabsComponent,
                                componentConfig: { ...currentConfig, tabs: updatedTabs },
                              };
                            };

                            setLayout((prevLayout) => {
                              let newLayout;

                              if (parentSplitPanelId && parentPanelSide) {
                                // 🆕 중첩 구조: 분할 패널 안의 탭에서 삭제
                                newLayout = {
                                  ...prevLayout,
                                  components: prevLayout.components.map((c) => {
                                    if (c.id === parentSplitPanelId) {
                                      const splitConfig = (c as any).componentConfig || {};
                                      const panelKey = parentPanelSide === "left" ? "leftPanel" : "rightPanel";
                                      const panelConfig = splitConfig[panelKey] || {};
                                      const panelComponents = panelConfig.components || [];

                                      const tabsComponent = panelComponents.find(
                                        (pc: any) => pc.id === tabsComponentId,
                                      );
                                      if (!tabsComponent) return c;

                                      const updatedTabsComponent = updateTabsComponentForDelete(tabsComponent);

                                      return {
                                        ...c,
                                        componentConfig: {
                                          ...splitConfig,
                                          [panelKey]: {
                                            ...panelConfig,
                                            components: panelComponents.map((pc: any) =>
                                              pc.id === tabsComponentId ? updatedTabsComponent : pc,
                                            ),
                                          },
                                        },
                                      };
                                    }
                                    return c;
                                  }),
                                };
                              } else {
                                // 일반 구조: 최상위 탭에서 삭제
                                const tabsComponent = prevLayout.components.find((c) => c.id === tabsComponentId);
                                if (!tabsComponent) return prevLayout;

                                const updatedTabsComponent = updateTabsComponentForDelete(tabsComponent);

                                newLayout = {
                                  ...prevLayout,
                                  components: prevLayout.components.map((c) =>
                                    c.id === tabsComponentId ? updatedTabsComponent : c,
                                  ),
                                };
                              }

                              setSelectedTabComponentInfo(null);
                              return newLayout;
                            });
                          };

                          return (
                            <div className="flex h-full flex-col">
                              <div className="flex items-center justify-between border-b px-4 py-2">
                                <span className="text-muted-foreground text-xs">탭 내부 컴포넌트</span>
                                <button
                                  className="text-muted-foreground hover:text-foreground text-xs"
                                  onClick={() => setSelectedTabComponentInfo(null)}
                                >
                                  선택 해제
                                </button>
                              </div>
                              <div className="flex-1 overflow-hidden">
                                <V2PropertiesPanel
                                  selectedComponent={tabComponentAsComponentData}
                                  tables={tables}
                                  onUpdateProperty={updateTabComponentProperty}
                                  onDeleteComponent={deleteTabComponent}
                                  currentTable={tables.length > 0 ? tables[0] : undefined}
                                  currentTableName={selectedScreen?.tableName}
                                  currentScreenCompanyCode={selectedScreen?.companyCode}
                                  onStyleChange={(style) => {
                                    updateTabComponentProperty(tabComp.id, "style", style);
                                  }}
                                  allComponents={layout.components}
                                  menuObjid={menuObjid}
                                />
                              </div>
                            </div>
                          );
                        })()
                      ) : selectedPanelComponentInfo ? (
                        // 🆕 분할 패널 내부 컴포넌트 선택 시 V2PropertiesPanel 사용
                        (() => {
                          const panelComp = selectedPanelComponentInfo.component;

                          // 분할 패널 내부 컴포넌트를 ComponentData 형식으로 변환
                          const panelComponentAsComponentData: ComponentData = {
                            id: panelComp.id,
                            type: "component",
                            componentType: panelComp.componentType,
                            label: panelComp.label,
                            position: panelComp.position || { x: 0, y: 0 },
                            size: panelComp.size || { width: 200, height: 100 },
                            componentConfig: panelComp.componentConfig || {},
                            style: panelComp.style || {},
                          } as ComponentData;

                          // 분할 패널 내부 컴포넌트용 속성 업데이트 핸들러
                          const updatePanelComponentProperty = (componentId: string, path: string, value: any) => {
                            const { splitPanelId, panelSide } = selectedPanelComponentInfo;
                            const panelKey = panelSide === "left" ? "leftPanel" : "rightPanel";

                            console.log("🔧 updatePanelComponentProperty 호출:", {
                              componentId,
                              path,
                              value,
                              splitPanelId,
                              panelSide,
                            });

                            // 🆕 안전한 깊은 경로 업데이트 헬퍼 함수
                            const setNestedValue = (obj: any, pathStr: string, val: any): any => {
                              const result = JSON.parse(JSON.stringify(obj));
                              const parts = pathStr.split(".");
                              let current = result;

                              for (let i = 0; i < parts.length - 1; i++) {
                                const part = parts[i];
                                if (!current[part] || typeof current[part] !== "object") {
                                  current[part] = {};
                                }
                                current = current[part];
                              }
                              current[parts[parts.length - 1]] = val;
                              return result;
                            };

                            setLayout((prevLayout) => {
                              const splitPanelComponent = prevLayout.components.find((c) => c.id === splitPanelId);
                              if (!splitPanelComponent) return prevLayout;

                              const currentConfig = (splitPanelComponent as any).componentConfig || {};
                              const panelConfig = currentConfig[panelKey] || {};
                              const components = panelConfig.components || [];

                              // 해당 컴포넌트 찾기
                              const targetCompIndex = components.findIndex((c: any) => c.id === componentId);
                              if (targetCompIndex === -1) return prevLayout;

                              // 🆕 안전한 깊은 경로 업데이트 사용
                              const targetComp = components[targetCompIndex];
                              const updatedComp =
                                path === "style"
                                  ? { ...targetComp, style: value }
                                  : setNestedValue(targetComp, path, value);

                              console.log("🔧 분할 패널 컴포넌트 업데이트 결과:", updatedComp);

                              const updatedComponents = [
                                ...components.slice(0, targetCompIndex),
                                updatedComp,
                                ...components.slice(targetCompIndex + 1),
                              ];

                              const updatedComponent = {
                                ...splitPanelComponent,
                                componentConfig: {
                                  ...currentConfig,
                                  [panelKey]: {
                                    ...panelConfig,
                                    components: updatedComponents,
                                  },
                                },
                              };

                              // selectedPanelComponentInfo 업데이트
                              setSelectedPanelComponentInfo((prev) =>
                                prev ? { ...prev, component: updatedComp } : null,
                              );

                              return {
                                ...prevLayout,
                                components: prevLayout.components.map((c) =>
                                  c.id === splitPanelId ? updatedComponent : c,
                                ),
                              };
                            });
                          };

                          // 분할 패널 내부 컴포넌트 삭제 핸들러
                          const deletePanelComponent = (componentId: string) => {
                            const { splitPanelId, panelSide } = selectedPanelComponentInfo;
                            const panelKey = panelSide === "left" ? "leftPanel" : "rightPanel";

                            setLayout((prevLayout) => {
                              const splitPanelComponent = prevLayout.components.find((c) => c.id === splitPanelId);
                              if (!splitPanelComponent) return prevLayout;

                              const currentConfig = (splitPanelComponent as any).componentConfig || {};
                              const panelConfig = currentConfig[panelKey] || {};
                              const components = panelConfig.components || [];

                              const updatedComponents = components.filter((c: any) => c.id !== componentId);

                              const updatedComponent = {
                                ...splitPanelComponent,
                                componentConfig: {
                                  ...currentConfig,
                                  [panelKey]: {
                                    ...panelConfig,
                                    components: updatedComponents,
                                  },
                                },
                              };

                              setSelectedPanelComponentInfo(null);

                              return {
                                ...prevLayout,
                                components: prevLayout.components.map((c) =>
                                  c.id === splitPanelId ? updatedComponent : c,
                                ),
                              };
                            });
                          };

                          return (
                            <div className="flex h-full flex-col">
                              <div className="flex items-center justify-between border-b px-4 py-2">
                                <span className="text-muted-foreground text-xs">
                                  분할 패널 ({selectedPanelComponentInfo.panelSide === "left" ? "좌측" : "우측"})
                                  컴포넌트
                                </span>
                                <button
                                  className="text-muted-foreground hover:text-foreground text-xs"
                                  onClick={() => setSelectedPanelComponentInfo(null)}
                                >
                                  선택 해제
                                </button>
                              </div>
                              <div className="flex-1 overflow-hidden">
                                <V2PropertiesPanel
                                  selectedComponent={panelComponentAsComponentData}
                                  tables={tables}
                                  onUpdateProperty={updatePanelComponentProperty}
                                  onDeleteComponent={deletePanelComponent}
                                  currentTable={tables.length > 0 ? tables[0] : undefined}
                                  currentTableName={selectedScreen?.tableName}
                                  currentScreenCompanyCode={selectedScreen?.companyCode}
                                  onStyleChange={(style) => {
                                    updatePanelComponentProperty(panelComp.id, "style", style);
                                  }}
                                  allComponents={layout.components}
                                  menuObjid={menuObjid}
                                />
                              </div>
                            </div>
                          );
                        })()
                      ) : (
                        <V2PropertiesPanel
                          selectedComponent={selectedComponent || undefined}
                          tables={tables}
                          onUpdateProperty={updateComponentProperty}
                          onDeleteComponent={deleteComponent}
                          onCopyComponent={copyComponent}
                          currentTable={tables.length > 0 ? tables[0] : undefined}
                          currentTableName={selectedScreen?.tableName}
                          currentScreenCompanyCode={selectedScreen?.companyCode}
                          dragState={dragState}
                          onStyleChange={(style) => {
                            if (selectedComponent) {
                              updateComponentProperty(selectedComponent.id, "style", style);
                            }
                          }}
                          allComponents={[...layout.components, ...otherLayerComponents]}
                          menuObjid={menuObjid}
                        />
                      )}
                    </TabsContent>
                  </Tabs>
                </div>
              </div>
            )}

            {/* 메인 캔버스 영역 (스크롤 가능한 컨테이너) - GPU 가속 스크롤 적용 */}
            <div
              ref={canvasContainerRef}
              className="bg-muted relative flex-1 overflow-auto px-16 py-6"
              style={{ willChange: "scroll-position" }}
            >
              {/* Pan 모드 안내 - 제거됨 */}
              {/* 줌 레벨 표시 */}
              <div className="bg-card text-foreground border-border pointer-events-none fixed right-6 bottom-6 z-50 rounded-lg border px-4 py-2 text-sm font-medium shadow-md">
                🔍 {Math.round(zoomLevel * 100)}%
              </div>
              {/* 🆕 플로우 버튼 그룹 제어 (버튼 선택 시 표시) */}
              {(() => {
                // 선택된 컴포넌트들
                const selectedComps = layout.components.filter((c) => groupState.selectedComponents.includes(c.id));

                // 버튼 컴포넌트만 필터링
                const selectedButtons = selectedComps.filter((comp) => areAllButtons([comp]));

                // 플로우 그룹에 속한 버튼이 있는지 확인
                const hasFlowGroupButton = selectedButtons.some((btn) => {
                  const flowConfig = (btn as any).webTypeConfig?.flowVisibilityConfig;
                  return flowConfig?.enabled && flowConfig.layoutBehavior === "auto-compact" && flowConfig.groupId;
                });

                // 버튼이 선택되었거나 플로우 그룹 버튼이 있으면 표시
                const shouldShow = selectedButtons.length >= 1 && (selectedButtons.length >= 2 || hasFlowGroupButton);

                if (!shouldShow) return null;

                return (
                  <div className="bg-card border-border fixed right-6 bottom-20 z-50 rounded-lg border shadow-lg">
                    <div className="flex flex-col gap-2 p-3">
                      <div className="text-muted-foreground mb-1 flex items-center gap-2 text-xs">
                        <svg
                          xmlns="http://www.w3.org/2000/svg"
                          width="14"
                          height="14"
                          viewBox="0 0 24 24"
                          fill="none"
                          stroke="currentColor"
                          strokeWidth="2"
                          strokeLinecap="round"
                          strokeLinejoin="round"
                        >
                          <path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"></path>
                          <polyline points="3.29 7 12 12 20.71 7"></polyline>
                          <line x1="12" y1="22" x2="12" y2="12"></line>
                        </svg>
                        <span className="font-medium">{selectedButtons.length}개 버튼 선택됨</span>
                      </div>

                      {/* 그룹 생성 버튼 (2개 이상 선택 시) */}
                      {selectedButtons.length >= 2 && (
                        <Button
                          size="sm"
                          variant="default"
                          onClick={handleFlowButtonGroup}
                          disabled={selectedButtons.length < 2}
                          className="flex items-center gap-2 text-xs"
                        >
                          <svg
                            xmlns="http://www.w3.org/2000/svg"
                            width="14"
                            height="14"
                            viewBox="0 0 24 24"
                            fill="none"
                            stroke="currentColor"
                            strokeWidth="2"
                            strokeLinecap="round"
                            strokeLinejoin="round"
                          >
                            <rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect>
                            <line x1="9" y1="3" x2="9" y2="21"></line>
                            <line x1="15" y1="3" x2="15" y2="21"></line>
                          </svg>
                          플로우 그룹으로 묶기
                        </Button>
                      )}

                      {/* 그룹 해제 버튼 (플로우 그룹 버튼이 있으면 항상 표시) */}
                      {hasFlowGroupButton && (
                        <Button
                          size="sm"
                          variant="outline"
                          onClick={handleFlowButtonUngroup}
                          className="flex items-center gap-2 text-xs"
                        >
                          <svg
                            xmlns="http://www.w3.org/2000/svg"
                            width="14"
                            height="14"
                            viewBox="0 0 24 24"
                            fill="none"
                            stroke="currentColor"
                            strokeWidth="2"
                            strokeLinecap="round"
                            strokeLinejoin="round"
                          >
                            <rect x="3" y="3" width="7" height="7"></rect>
                            <rect x="14" y="3" width="7" height="7"></rect>
                            <rect x="14" y="14" width="7" height="7"></rect>
                            <rect x="3" y="14" width="7" height="7"></rect>
                          </svg>
                          그룹 해제
                        </Button>
                      )}

                      {/* 상태 표시 */}
                      {hasFlowGroupButton && <p className="mt-1 text-[10px] text-blue-600">✓ 플로우 그룹 버튼</p>}
                    </div>
                  </div>
                );
              })()}
              {/* 🆕 활성 레이어 인디케이터 (기본 레이어가 아닌 경우 표시) */}
              {activeLayerId > 1 && (
                <div className="sticky top-0 z-30 flex items-center justify-center gap-2 border-b bg-amber-50 px-4 py-1.5 backdrop-blur-sm dark:bg-amber-950/30">
                  <div className="h-2 w-2 rounded-full bg-amber-500" />
                  <span className="text-xs font-medium">
                    레이어 {activeLayerId} 편집 중
                    {activeLayerZone && (
                      <span className="ml-2 text-amber-600">
                        (캔버스: {activeLayerZone.width} x {activeLayerZone.height}px - {activeLayerZone.zone_name})
                      </span>
                    )}
                    {!activeLayerZone && (
                      <span className="ml-2 text-red-500">
                        (조건부 영역 미설정 - 기본 레이어에서 Zone을 먼저 생성하세요)
                      </span>
                    )}
                  </span>
                </div>
              )}

              {/* 줌 적용 시 스크롤 영역 확보를 위한 래퍼 - 중앙 정렬 + contain 최적화 */}
              {(() => {
                // 🆕 조건부 레이어 편집 시 캔버스 크기를 Zone에 맞춤
                const activeRegion = activeLayerId > 1 ? activeLayerZone : null;
                const canvasW = activeRegion ? activeRegion.width : screenResolution.width;
                const canvasH = activeRegion ? activeRegion.height : screenResolution.height;

                return (
              <div
                className="flex justify-center"
                style={{
                  width: "100%",
                  minHeight: canvasH * zoomLevel,
                  contain: "layout style", // 레이아웃 재계산 범위 제한
                }}
              >
                {/* 실제 작업 캔버스 (해상도 크기 또는 조건부 레이어 영역 크기) */}
                <div
                  className={cn(
                    "bg-background border shadow-lg",
                    activeRegion ? "border-amber-400 border-2" : "border-border"
                  )}
                  style={{
                    width: `${canvasW}px`,
                    height: `${canvasH}px`,
                    minWidth: `${canvasW}px`,
                    maxWidth: `${canvasW}px`,
                    minHeight: `${canvasH}px`,
                    flexShrink: 0,
                    transform: `scale3d(${zoomLevel}, ${zoomLevel}, 1)`,
                    transformOrigin: "top center", // 중앙 기준으로 스케일
                    willChange: "transform", // GPU 가속 레이어 생성
                    backfaceVisibility: "hidden" as const, // 리페인트 최적화
                  }}
                >
                  <div
                    ref={canvasRef}
                    className="bg-background relative h-full w-full overflow-visible"
                    onClick={(e) => {
                      if (e.target === e.currentTarget && !selectionDrag.wasSelecting && !isPanMode) {
                        setSelectedComponent(null);
                        setGroupState((prev) => ({ ...prev, selectedComponents: [] }));
                      }
                    }}
                    onMouseDown={(e) => {
                      // Pan 모드가 아닐 때만 다중 선택 시작
                      if (e.target === e.currentTarget && !isPanMode) {
                        startSelectionDrag(e);
                      }
                    }}
                    onMouseMove={(e) => {
                      // 영역 이동/리사이즈 처리
                      if (regionDrag.isDragging || regionDrag.isResizing) {
                        handleRegionCanvasMouseMove(e);
                      }
                    }}
                    onMouseUp={() => {
                      if (regionDrag.isDragging || regionDrag.isResizing) {
                        handleRegionCanvasMouseUp();
                      }
                    }}
                    onMouseLeave={() => {
                      if (regionDrag.isDragging || regionDrag.isResizing) {
                        handleRegionCanvasMouseUp();
                      }
                    }}
                    onDragOver={(e) => {
                      e.preventDefault();
                      e.dataTransfer.dropEffect = "copy";
                    }}
                    onDropCapture={(e) => {
                      // 캡처 단계에서 드롭 이벤트를 처리하여 자식 요소 드롭도 감지
                      e.preventDefault();
                      handleDrop(e);
                    }}
                  >
                    {/* 격자 라인 */}
                    {gridLines.map((line, index) => (
                      <div
                        key={index}
                        className="bg-border pointer-events-none absolute"
                        style={{
                          left: line.type === "vertical" ? `${line.position}px` : 0,
                          top: line.type === "horizontal" ? `${line.position}px` : 0,
                          width: line.type === "vertical" ? "1px" : "100%",
                          height: line.type === "horizontal" ? "1px" : "100%",
                          opacity: layout.gridSettings?.gridOpacity || 0.3,
                        }}
                      />
                    ))}

                    {/* 컴포넌트들 */}
                    {(() => {
                      // 🆕 플로우 버튼 그룹 감지 및 처리
                      // visibleComponents를 사용하여 활성 레이어의 컴포넌트만 표시
                      const topLevelComponents = visibleComponents.filter((component) => !component.parentId);

                      // auto-compact 모드의 버튼들을 그룹별로 묶기
                      const buttonGroups: Record<string, ComponentData[]> = {};
                      const processedButtonIds = new Set<string>();

                      topLevelComponents.forEach((component) => {
                        const isButton =
                          component.type === "button" ||
                          (component.type === "component" &&
                            ["button-primary", "button-secondary"].includes((component as any).componentType));

                        if (isButton) {
                          const flowConfig = (component as any).webTypeConfig?.flowVisibilityConfig as
                            | FlowVisibilityConfig
                            | undefined;

                          if (
                            flowConfig?.enabled &&
                            flowConfig.layoutBehavior === "auto-compact" &&
                            flowConfig.groupId
                          ) {
                            if (!buttonGroups[flowConfig.groupId]) {
                              buttonGroups[flowConfig.groupId] = [];
                            }
                            buttonGroups[flowConfig.groupId].push(component);
                            processedButtonIds.add(component.id);
                          }
                        }
                      });

                      // 그룹에 속하지 않은 일반 컴포넌트들
                      const regularComponents = topLevelComponents.filter((c) => !processedButtonIds.has(c.id));

                      // 🔧 렌더링 확인 로그 (디버그 완료 - 주석 처리)
                      // console.log("🔄 ScreenDesigner 렌더링:", { componentsCount: regularComponents.length, forceRenderTrigger, timestamp: Date.now() });

                      return (
                        <>
                          {/* 조건부 영역(Zone) (기본 레이어에서만 표시, DB 기반) */}
                          {/* 내부는 pointerEvents: none으로 아래 컴포넌트 클릭/드래그 통과 */}
                          {activeLayerId === 1 && zones.map((zone) => {
                            const layerId = zone.zone_id; // 렌더링용 ID
                            const region = zone;
                            const resizeHandles = ["nw", "ne", "sw", "se", "n", "s", "e", "w"];
                            const handleCursors: Record<string, string> = {
                              nw: "nwse-resize", ne: "nesw-resize", sw: "nesw-resize", se: "nwse-resize",
                              n: "ns-resize", s: "ns-resize", e: "ew-resize", w: "ew-resize",
                            };
                            const handlePositions: Record<string, React.CSSProperties> = {
                              nw: { top: -4, left: -4 }, ne: { top: -4, right: -4 },
                              sw: { bottom: -4, left: -4 }, se: { bottom: -4, right: -4 },
                              n: { top: -4, left: "50%", transform: "translateX(-50%)" },
                              s: { bottom: -4, left: "50%", transform: "translateX(-50%)" },
                              e: { top: "50%", right: -4, transform: "translateY(-50%)" },
                              w: { top: "50%", left: -4, transform: "translateY(-50%)" },
                            };
                            // 테두리 두께 (이동 핸들 영역)
                            const borderWidth = 6;
                            return (
                              <div
                                key={`region-${layerId}`}
                                className="absolute"
                                style={{
                                  left: `${region.x}px`,
                                  top: `${region.y}px`,
                                  width: `${region.width}px`,
                                  height: `${region.height}px`,
                                  border: "2px dashed hsl(var(--primary))",
                                  borderRadius: "4px",
                                  backgroundColor: "hsl(var(--primary) / 0.05)",
                                  zIndex: 50,
                                  pointerEvents: "none", // 내부 클릭은 아래 컴포넌트로 통과
                                }}
                              >
                                {/* 테두리 이동 핸들: 상/하/좌/우 얇은 영역만 pointerEvents 활성 */}
                                {/* 상단 */}
                                <div
                                  className="absolute left-0 right-0 top-0"
                                  style={{ height: borderWidth, cursor: "move", pointerEvents: "auto" }}
                                  onMouseDown={(e) => handleRegionMouseDown(e, String(layerId), "move")}
                                />
                                {/* 하단 */}
                                <div
                                  className="absolute bottom-0 left-0 right-0"
                                  style={{ height: borderWidth, cursor: "move", pointerEvents: "auto" }}
                                  onMouseDown={(e) => handleRegionMouseDown(e, String(layerId), "move")}
                                />
                                {/* 좌측 */}
                                <div
                                  className="absolute bottom-0 left-0 top-0"
                                  style={{ width: borderWidth, cursor: "move", pointerEvents: "auto" }}
                                  onMouseDown={(e) => handleRegionMouseDown(e, String(layerId), "move")}
                                />
                                {/* 우측 */}
                                <div
                                  className="absolute bottom-0 right-0 top-0"
                                  style={{ width: borderWidth, cursor: "move", pointerEvents: "auto" }}
                                  onMouseDown={(e) => handleRegionMouseDown(e, String(layerId), "move")}
                                />
                                {/* 라벨 */}
                                <span
                                  className="absolute left-2 top-1 select-none text-[10px] font-medium text-primary"
                                  style={{ pointerEvents: "auto", cursor: "move" }}
                                  onMouseDown={(e) => handleRegionMouseDown(e, String(layerId), "move")}
                                >
                                  Zone {zone.zone_id} - {zone.zone_name}
                                </span>
                                {/* 리사이즈 핸들 */}
                                {resizeHandles.map((handle) => (
                                  <div
                                    key={handle}
                                    className="absolute z-10 h-2 w-2 rounded-sm border border-primary bg-background"
                                    style={{ ...handlePositions[handle], cursor: handleCursors[handle], pointerEvents: "auto" }}
                                    onMouseDown={(e) => handleRegionMouseDown(e, String(layerId), "resize", handle)}
                                  />
                                ))}
                                {/* 삭제 버튼 */}
                                <button
                                  className="absolute -right-1 -top-3 flex h-4 w-4 items-center justify-center rounded-full bg-destructive text-[8px] text-destructive-foreground hover:bg-destructive/80"
                                  style={{ pointerEvents: "auto" }}
                                  onClick={async (e) => {
                                    e.stopPropagation();
                                    if (!selectedScreen?.screenId) return;
                                    try {
                                      await screenApi.deleteZone(zone.zone_id);
                                      setZones((prev) => prev.filter(z => z.zone_id !== zone.zone_id));
                                      toast.success("조건부 영역이 삭제되었습니다.");
                                    } catch { toast.error("Zone 삭제 실패"); }
                                  }}
                                  title="영역 삭제"
                                >
                                  x
                                </button>
                              </div>
                            );
                          })}


                          {/* 일반 컴포넌트들 */}
                          {regularComponents.map((component) => {
                            const children =
                              component.type === "group"
                                ? layout.components.filter((child) => child.parentId === component.id)
                                : [];

                            // 드래그 중 시각적 피드백 (다중 선택 지원)
                            const isDraggingThis =
                              dragState.isDragging && dragState.draggedComponent?.id === component.id;
                            const isBeingDragged =
                              dragState.isDragging &&
                              dragState.draggedComponents.some((dragComp) => dragComp.id === component.id);

                            let displayComponent = component;

                            if (isBeingDragged) {
                              if (isDraggingThis) {
                                // 주 드래그 컴포넌트: 마우스 위치 기반으로 실시간 위치 업데이트
                                displayComponent = {
                                  ...component,
                                  position: dragState.currentPosition,
                                  style: {
                                    ...component.style,
                                    opacity: 0.8,
                                    transform: "scale(1.02)",
                                    transition: "none",
                                    zIndex: 50,
                                  },
                                };
                              } else {
                                // 다른 선택된 컴포넌트들: 상대적 위치로 실시간 업데이트
                                const originalComponent = dragState.draggedComponents.find(
                                  (dragComp) => dragComp.id === component.id,
                                );
                                if (originalComponent) {
                                  const deltaX = dragState.currentPosition.x - dragState.originalPosition.x;
                                  const deltaY = dragState.currentPosition.y - dragState.originalPosition.y;

                                  displayComponent = {
                                    ...component,
                                    position: {
                                      x: originalComponent.position.x + deltaX,
                                      y: originalComponent.position.y + deltaY,
                                      z: originalComponent.position.z || 1,
                                    } as Position,
                                    style: {
                                      ...component.style,
                                      opacity: 0.8,
                                      transition: "none",
                                      zIndex: 40, // 주 컴포넌트보다 약간 낮게
                                    },
                                  };
                                }
                              }
                            }

                            // 전역 파일 상태도 key에 포함하여 실시간 리렌더링
                            const globalFileState =
                              typeof window !== "undefined" ? (window as any).globalFileState || {} : {};
                            const globalFiles = globalFileState[component.id] || [];
                            const componentFiles = (component as any).uploadedFiles || [];
                            const fileStateKey = `${globalFiles.length}-${JSON.stringify(globalFiles.map((f: any) => f.objid) || [])}-${componentFiles.length}`;
                            // 🆕 style 변경 시 리렌더링을 위한 key 추가
                            const styleKey =
                              component.style?.labelDisplay !== undefined
                                ? `label-${component.style.labelDisplay}`
                                : "";
                            const fullKey = `${component.id}-${fileStateKey}-${styleKey}-${(component as any).lastFileUpdate || 0}-${forceRenderTrigger}`;

                            // 🔧 v2-input 계열 컴포넌트 key 변경 로그 (디버그 완료 - 주석 처리)
                            // if (component.id.includes("v2-") || component.widgetType?.includes("v2-")) { console.log("🔑 RealtimePreview key:", { id: component.id, styleKey, labelDisplay: component.style?.labelDisplay, forceRenderTrigger, fullKey }); }

                            // 🆕 labelDisplay 변경 시 새 객체로 강제 변경 감지
                            const componentWithLabel = {
                              ...displayComponent,
                              _labelDisplayKey: component.style?.labelDisplay,
                            };

                            return (
                              <RealtimePreview
                                key={fullKey}
                                component={componentWithLabel}
                                isSelected={
                                  selectedComponent?.id === component.id ||
                                  groupState.selectedComponents.includes(component.id)
                                }
                                isDesignMode={true} // 편집 모드로 설정
                                onClick={(e) => handleComponentClick(component, e)}
                                onDoubleClick={(e) => handleComponentDoubleClick(component, e)}
                                onDragStart={(e) => startComponentDrag(component, e)}
                                onDragEnd={endDrag}
                                selectedScreen={selectedScreen}
                                tableName={selectedScreen?.tableName} // 🆕 디자인 모드에서도 옵션 로딩을 위해 전달
                                menuObjid={menuObjid} // 🆕 메뉴 OBJID 전달
                                // onZoneComponentDrop 제거
                                onZoneClick={handleZoneClick}
                                // 설정 변경 핸들러 (테이블 페이지 크기 등 설정을 상세설정에 반영)
                                onConfigChange={(config) => {
                                  // console.log("📤 테이블 설정 변경을 상세설정에 반영:", config);

                                  // 컴포넌트의 componentConfig 업데이트
                                  const updatedComponents = layout.components.map((comp) => {
                                    if (comp.id === component.id) {
                                      return {
                                        ...comp,
                                        componentConfig: {
                                          ...comp.componentConfig,
                                          ...config,
                                        },
                                      };
                                    }
                                    return comp;
                                  });

                                  const newLayout = {
                                    ...layout,
                                    components: updatedComponents,
                                  };

                                  setLayout(newLayout);
                                  saveToHistory(newLayout);

                                  console.log("✅ 컴포넌트 설정 업데이트 완료:", {
                                    componentId: component.id,
                                    updatedConfig: config,
                                  });
                                }}
                                // 🆕 컴포넌트 전체 업데이트 핸들러 (탭 내부 컴포넌트 위치 조정 등)
                                onUpdateComponent={(updatedComponent) => {
                                  const updatedComponents = layout.components.map((comp) =>
                                    comp.id === updatedComponent.id ? updatedComponent : comp,
                                  );

                                  const newLayout = {
                                    ...layout,
                                    components: updatedComponents,
                                  };

                                  setLayout(newLayout);
                                  saveToHistory(newLayout);
                                }}
                                // 🆕 리사이즈 핸들러 (10px 스냅 적용됨)
                                onResize={(componentId, newSize) => {
                                  setLayout((prevLayout) => {
                                    const updatedComponents = prevLayout.components.map((comp) =>
                                      comp.id === componentId ? { ...comp, size: newSize } : comp,
                                    );

                                    const newLayout = {
                                      ...prevLayout,
                                      components: updatedComponents,
                                    };

                                    // saveToHistory는 별도로 호출 (prevLayout 기반)
                                    setTimeout(() => saveToHistory(newLayout), 0);
                                    return newLayout;
                                  });
                                }}
                                // 🆕 탭 내부 컴포넌트 선택 핸들러
                                onSelectTabComponent={(tabId, compId, comp) =>
                                  handleSelectTabComponent(component.id, tabId, compId, comp)
                                }
                                selectedTabComponentId={
                                  selectedTabComponentInfo?.tabsComponentId === component.id
                                    ? selectedTabComponentInfo.componentId
                                    : undefined
                                }
                                // 🆕 분할 패널 내부 컴포넌트 선택 핸들러
                                onSelectPanelComponent={(panelSide, compId, comp) =>
                                  handleSelectPanelComponent(component.id, panelSide, compId, comp)
                                }
                                selectedPanelComponentId={
                                  selectedPanelComponentInfo?.splitPanelId === component.id
                                    ? selectedPanelComponentInfo.componentId
                                    : undefined
                                }
                              >
                                {/* 컨테이너, 그룹, 영역, 컴포넌트의 자식 컴포넌트들 렌더링 (레이아웃은 독립적으로 렌더링) */}
                                {(component.type === "group" ||
                                  component.type === "container" ||
                                  component.type === "area" ||
                                  component.type === "component") &&
                                  layout.components
                                    .filter((child) => child.parentId === component.id)
                                    .map((child) => {
                                      // 자식 컴포넌트에도 드래그 피드백 적용
                                      const isChildDraggingThis =
                                        dragState.isDragging && dragState.draggedComponent?.id === child.id;
                                      const isChildBeingDragged =
                                        dragState.isDragging &&
                                        dragState.draggedComponents.some((dragComp) => dragComp.id === child.id);

                                      let displayChild = child;

                                      if (isChildBeingDragged) {
                                        if (isChildDraggingThis) {
                                          // 주 드래그 자식 컴포넌트
                                          displayChild = {
                                            ...child,
                                            position: dragState.currentPosition,
                                            style: {
                                              ...child.style,
                                              opacity: 0.8,
                                              transform: "scale(1.02)",
                                              transition: "none",
                                              zIndex: 50,
                                            },
                                          };
                                        } else {
                                          // 다른 선택된 자식 컴포넌트들
                                          const originalChildComponent = dragState.draggedComponents.find(
                                            (dragComp) => dragComp.id === child.id,
                                          );
                                          if (originalChildComponent) {
                                            const deltaX = dragState.currentPosition.x - dragState.originalPosition.x;
                                            const deltaY = dragState.currentPosition.y - dragState.originalPosition.y;

                                            displayChild = {
                                              ...child,
                                              position: {
                                                x: originalChildComponent.position.x + deltaX,
                                                y: originalChildComponent.position.y + deltaY,
                                                z: originalChildComponent.position.z || 1,
                                              } as Position,
                                              style: {
                                                ...child.style,
                                                opacity: 0.8,
                                                transition: "none",
                                                zIndex: 8888,
                                              },
                                            };
                                          }
                                        }
                                      }

                                      // 자식 컴포넌트의 위치를 부모 기준 상대 좌표로 조정
                                      const relativeChildComponent = {
                                        ...displayChild,
                                        position: {
                                          x: displayChild.position.x - component.position.x,
                                          y: displayChild.position.y - component.position.y,
                                          z: displayChild.position.z || 1,
                                        },
                                      };

                                      return (
                                        <RealtimePreview
                                          key={`${child.id}-${(child as any).uploadedFiles?.length || 0}-${JSON.stringify((child as any).uploadedFiles?.map((f: any) => f.objid) || [])}`}
                                          component={relativeChildComponent}
                                          isSelected={
                                            selectedComponent?.id === child.id ||
                                            groupState.selectedComponents.includes(child.id)
                                          }
                                          isDesignMode={true} // 편집 모드로 설정
                                          onClick={(e) => handleComponentClick(child, e)}
                                          onDoubleClick={(e) => handleComponentDoubleClick(child, e)}
                                          onDragStart={(e) => startComponentDrag(child, e)}
                                          onDragEnd={endDrag}
                                          selectedScreen={selectedScreen}
                                          tableName={selectedScreen?.tableName} // 🆕 디자인 모드에서도 옵션 로딩을 위해 전달
                                          // onZoneComponentDrop 제거
                                          onZoneClick={handleZoneClick}
                                          // 설정 변경 핸들러 (자식 컴포넌트용)
                                          onConfigChange={(config) => {
                                            // console.log("📤 자식 컴포넌트 설정 변경을 상세설정에 알림:", config);
                                            // TODO: 실제 구현은 DetailSettingsPanel과의 연동 필요
                                          }}
                                          // 🆕 자식 컴포넌트 리사이즈 핸들러
                                          onResize={(componentId, newSize) => {
                                            setLayout((prevLayout) => {
                                              const updatedComponents = prevLayout.components.map((comp) =>
                                                comp.id === componentId ? { ...comp, size: newSize } : comp,
                                              );

                                              const newLayout = {
                                                ...prevLayout,
                                                components: updatedComponents,
                                              };

                                              setTimeout(() => saveToHistory(newLayout), 0);
                                              return newLayout;
                                            });
                                          }}
                                        />
                                      );
                                    })}
                              </RealtimePreview>
                            );
                          })}

                          {/* 🆕 플로우 버튼 그룹들 */}
                          {Object.entries(buttonGroups).map(([groupId, buttons]) => {
                            if (buttons.length === 0) return null;

                            const firstButton = buttons[0];
                            const groupConfig = (firstButton as any).webTypeConfig
                              ?.flowVisibilityConfig as FlowVisibilityConfig;

                            // 🔧 그룹의 위치 및 크기 계산
                            // 모든 버튼이 같은 위치(groupX, groupY)에 배치되어 있으므로
                            // 첫 번째 버튼의 위치를 그룹 시작점으로 사용
                            const direction = groupConfig.groupDirection || "horizontal";
                            const gap = groupConfig.groupGap ?? 8;
                            const align = groupConfig.groupAlign || "start";

                            const groupPosition = {
                              x: buttons[0].position.x,
                              y: buttons[0].position.y,
                              z: buttons[0].position.z || 2,
                            };

                            // 버튼들의 실제 크기 계산
                            let groupWidth = 0;
                            let groupHeight = 0;

                            if (direction === "horizontal") {
                              // 가로 정렬: 모든 버튼의 너비 + 간격
                              groupWidth = buttons.reduce((total, button, index) => {
                                const buttonWidth = button.size?.width || 100;
                                const gapWidth = index < buttons.length - 1 ? gap : 0;
                                return total + buttonWidth + gapWidth;
                              }, 0);
                              groupHeight = Math.max(...buttons.map((b) => b.size?.height || 40));
                            } else {
                              // 세로 정렬
                              groupWidth = Math.max(...buttons.map((b) => b.size?.width || 100));
                              groupHeight = buttons.reduce((total, button, index) => {
                                const buttonHeight = button.size?.height || 40;
                                const gapHeight = index < buttons.length - 1 ? gap : 0;
                                return total + buttonHeight + gapHeight;
                              }, 0);
                            }

                            // 🆕 그룹 전체가 선택되었는지 확인
                            const isGroupSelected = buttons.every(
                              (btn) =>
                                selectedComponent?.id === btn.id || groupState.selectedComponents.includes(btn.id),
                            );
                            const hasAnySelected = buttons.some(
                              (btn) =>
                                selectedComponent?.id === btn.id || groupState.selectedComponents.includes(btn.id),
                            );

                            return (
                              <div
                                key={`flow-button-group-${groupId}`}
                                style={{
                                  position: "absolute",
                                  left: `${groupPosition.x}px`,
                                  top: `${groupPosition.y}px`,
                                  zIndex: groupPosition.z,
                                  width: `${groupWidth}px`, // 🆕 명시적 너비
                                  height: `${groupHeight}px`, // 🆕 명시적 높이
                                  pointerEvents: "none", // 그룹 컨테이너는 이벤트 차단하여 개별 버튼 클릭 가능
                                }}
                                className={hasAnySelected ? "rounded outline-2 outline-offset-2 outline-blue-500" : ""}
                              >
                                <FlowButtonGroup
                                  buttons={buttons}
                                  groupConfig={groupConfig}
                                  isDesignMode={true}
                                  renderButton={(button, isVisible) => {
                                    // 드래그 피드백
                                    const isDraggingThis =
                                      dragState.isDragging && dragState.draggedComponent?.id === button.id;
                                    const isBeingDragged =
                                      dragState.isDragging &&
                                      dragState.draggedComponents.some((dragComp) => dragComp.id === button.id);

                                    let displayButton = button;

                                    if (isBeingDragged) {
                                      if (isDraggingThis) {
                                        displayButton = {
                                          ...button,
                                          position: dragState.currentPosition,
                                          style: {
                                            ...button.style,
                                            opacity: 0.8,
                                            transform: "scale(1.02)",
                                            transition: "none",
                                            zIndex: 50,
                                          },
                                        };
                                      }
                                    }

                                    // 🔧 그룹 내부에서는 상대 위치 사용 (wrapper로 처리)
                                    const relativeButton = {
                                      ...displayButton,
                                      position: {
                                        x: 0,
                                        y: 0,
                                        z: displayButton.position.z || 1,
                                      },
                                    };

                                    return (
                                      <div
                                        key={button.id}
                                        style={{
                                          position: "relative",
                                          opacity: isVisible ? 1 : 0.5,
                                          display: "inline-block",
                                          width: button.size?.width || 100,
                                          height: button.size?.height || 40,
                                          pointerEvents: "auto", // 개별 버튼은 이벤트 활성화
                                          cursor: "pointer",
                                        }}
                                        onMouseDown={(e) => {
                                          // 클릭이 아닌 드래그인 경우에만 드래그 시작
                                          e.preventDefault();
                                          e.stopPropagation();

                                          const startX = e.clientX;
                                          const startY = e.clientY;
                                          let isDragging = false;
                                          let dragStarted = false;

                                          const handleMouseMove = (moveEvent: MouseEvent) => {
                                            const deltaX = Math.abs(moveEvent.clientX - startX);
                                            const deltaY = Math.abs(moveEvent.clientY - startY);

                                            // 5픽셀 이상 움직이면 드래그로 간주
                                            if ((deltaX > 5 || deltaY > 5) && !dragStarted) {
                                              isDragging = true;
                                              dragStarted = true;

                                              // Shift 키를 누르지 않았으면 같은 그룹의 버튼들도 모두 선택
                                              if (!e.shiftKey) {
                                                const buttonIds = buttons.map((b) => b.id);
                                                setGroupState((prev) => ({
                                                  ...prev,
                                                  selectedComponents: buttonIds,
                                                }));
                                              }

                                              // 드래그 시작
                                              startComponentDrag(button, e as any);
                                            }
                                          };

                                          const handleMouseUp = () => {
                                            document.removeEventListener("mousemove", handleMouseMove);
                                            document.removeEventListener("mouseup", handleMouseUp);

                                            // 드래그가 아니면 클릭으로 처리
                                            if (!isDragging) {
                                              // Shift 키를 누르지 않았으면 같은 그룹의 버튼들도 모두 선택
                                              if (!e.shiftKey) {
                                                const buttonIds = buttons.map((b) => b.id);
                                                setGroupState((prev) => ({
                                                  ...prev,
                                                  selectedComponents: buttonIds,
                                                }));
                                              }
                                              handleComponentClick(button, e);
                                            }
                                          };

                                          document.addEventListener("mousemove", handleMouseMove);
                                          document.addEventListener("mouseup", handleMouseUp);
                                        }}
                                        onDoubleClick={(e) => {
                                          e.stopPropagation();
                                          handleComponentDoubleClick(button, e);
                                        }}
                                        className={
                                          selectedComponent?.id === button.id ||
                                          groupState.selectedComponents.includes(button.id)
                                            ? "outline-1 outline-offset-1 outline-blue-400"
                                            : ""
                                        }
                                      >
                                        {/* 그룹 내부에서는 DynamicComponentRenderer로 직접 렌더링 */}
                                        <div style={{ width: "100%", height: "100%", pointerEvents: "none" }}>
                                          <DynamicComponentRenderer
                                            component={relativeButton}
                                            isDesignMode={true}
                                            formData={{}}
                                            tableName={selectedScreen?.tableName}
                                            onDataflowComplete={() => {}}
                                          />
                                        </div>
                                      </div>
                                    );
                                  }}
                                />
                              </div>
                            );
                          })}
                        </>
                      );
                    })()}

                    {/* 드래그 선택 영역 */}
                    {selectionDrag.isSelecting && (
                      <div
                        className="border-primary bg-primary/5 pointer-events-none absolute rounded-md border-2 border-dashed"
                        style={{
                          left: `${Math.min(selectionDrag.startPoint.x, selectionDrag.currentPoint.x)}px`,
                          top: `${Math.min(selectionDrag.startPoint.y, selectionDrag.currentPoint.y)}px`,
                          width: `${Math.abs(selectionDrag.currentPoint.x - selectionDrag.startPoint.x)}px`,
                          height: `${Math.abs(selectionDrag.currentPoint.y - selectionDrag.startPoint.y)}px`,
                        }}
                      />
                    )}

                    {/* 빈 캔버스 안내 */}
                    {layout.components.length === 0 && (
                      <div className="pointer-events-none absolute inset-0 flex items-center justify-center">
                        <div className="max-w-2xl space-y-4 px-6 text-center">
                          <div className="bg-muted mx-auto flex h-16 w-16 items-center justify-center rounded-full">
                            <Database className="text-muted-foreground h-8 w-8" />
                          </div>
                          <h3 className="text-foreground text-xl font-semibold">캔버스가 비어있습니다</h3>
                          <p className="text-muted-foreground text-sm">
                            좌측 패널에서 테이블/컬럼이나 템플릿을 드래그하여 화면을 설계하세요
                          </p>
                          <div className="text-muted-foreground space-y-2 text-xs">
                            <p>
                              <span className="font-medium">단축키:</span> T(테이블), M(템플릿), P(속성), S(스타일),
                              R(격자), D(상세설정), E(해상도)
                            </p>
                            <p>
                              <span className="font-medium">편집:</span> Ctrl+C(복사), Ctrl+V(붙여넣기), Ctrl+S(저장),
                              Ctrl+Z(실행취소), Delete/Backspace(삭제)
                            </p>
                            <p className="text-warning flex items-center justify-center gap-2">
                              <span>⚠️</span>
                              <span>브라우저 기본 단축키가 차단되어 애플리케이션 기능만 작동합니다</span>
                            </p>
                          </div>
                        </div>
                      </div>
                    )}
                  </div>
                </div>
              </div>
              ); /* 🔥 줌 래퍼 닫기 */
              })()}
            </div>
          </div>{" "}
          {/* 메인 컨테이너 닫기 */}
          {/* 🆕 플로우 버튼 그룹 생성 다이얼로그 */}
          <FlowButtonGroupDialog
            open={groupDialogOpen}
            onOpenChange={setGroupDialogOpen}
            buttonCount={groupState.selectedComponents.length}
            onConfirm={handleGroupConfirm}
          />
          {/* 모달들 */}
          {/* 메뉴 할당 모달 */}
          {showMenuAssignmentModal && selectedScreen && (
            <MenuAssignmentModal
              screenInfo={selectedScreen}
              isOpen={showMenuAssignmentModal}
              onClose={() => setShowMenuAssignmentModal(false)}
              onAssignmentComplete={() => {
                // 모달을 즉시 닫지 않고, MenuAssignmentModal이 3초 후 자동으로 닫히도록 함
                // setShowMenuAssignmentModal(false);
                // toast.success("메뉴에 화면이 할당되었습니다.");
              }}
              onBackToList={onBackToList}
            />
          )}
          {/* 파일첨부 상세 모달 */}
          {showFileAttachmentModal && selectedFileComponent && (
            <FileAttachmentDetailModal
              isOpen={showFileAttachmentModal}
              onClose={() => {
                setShowFileAttachmentModal(false);
                setSelectedFileComponent(null);
              }}
              component={selectedFileComponent}
              screenId={selectedScreen.screenId}
            />
          )}
          {/* 다국어 설정 모달 */}
          <MultilangSettingsModal
            isOpen={showMultilangSettingsModal}
            onClose={() => setShowMultilangSettingsModal(false)}
            components={layout.components}
            onSave={async (updates) => {
              if (updates.length === 0) {
                toast.info("저장할 변경사항이 없습니다.");
                return;
              }

              try {
                // 공통 유틸 사용하여 매핑 적용
                const { applyMultilangMappings } = await import("@/lib/utils/multilangLabelExtractor");

                // 매핑 형식 변환
                const mappings = updates.map((u) => ({
                  componentId: u.componentId,
                  keyId: u.langKeyId,
                  langKey: u.langKey,
                }));

                // 레이아웃 업데이트
                const updatedComponents = applyMultilangMappings(layout.components, mappings);
                setLayout((prev) => ({
                  ...prev,
                  components: updatedComponents,
                }));

                toast.success(`${updates.length}개 항목의 다국어 설정이 저장되었습니다.`);
              } catch (error) {
                console.error("다국어 설정 저장 실패:", error);
                toast.error("다국어 설정 저장 중 오류가 발생했습니다.");
              }
            }}
          />
          {/* 단축키 도움말 모달 */}
          <KeyboardShortcutsModal
            isOpen={showShortcutsModal}
            onClose={() => setShowShortcutsModal(false)}
          />
        </div>
      </TableOptionsProvider>
      </LayerProvider>
    </ScreenPreviewProvider>
  );
}