"use client";

import React, { useState, useEffect, useCallback } from "react";
import {
  ReactFlow,
  Controls,
  MiniMap,
  Background,
  BackgroundVariant,
  Node,
  Edge,
  useNodesState,
  useEdgesState,
  MarkerType,
  useReactFlow,
  ReactFlowProvider,
} from "@xyflow/react";
import "@xyflow/react/dist/style.css";

import { ScreenDefinition } from "@/types/screen";
import { ScreenNode, TableNode, ScreenNodeData, TableNodeData, ReferenceInfo } from "./ScreenNode";
import {
  getFieldJoins,
  getDataFlows,
  getTableRelations,
  getMultipleScreenLayoutSummary,
  getScreenGroup,
  getScreenSubTables,
  ScreenLayoutSummary,
  ScreenSubTablesData,
  SubTableInfo,
  inferVisualRelationType,
  VisualRelationType,
} from "@/lib/api/screenGroup";
import { getTableColumns, ColumnTypeInfo } from "@/lib/api/tableManagement";
import { ScreenSettingModal } from "./ScreenSettingModal";
import { TableSettingModal } from "./TableSettingModal";
import { AnimatedFlowEdge } from "./AnimatedFlowEdge";
import { Monitor, Database, FolderOpen } from "lucide-react";

// 관계 유형별 색상 정의 (CSS 변수 기반 - 다크모드 자동 대응)
const RELATION_COLORS: Record<VisualRelationType, { stroke: string; strokeLight: string; label: string }> = {
  filter: { stroke: 'hsl(var(--primary))', strokeLight: 'hsl(var(--primary) / 0.4)', label: '마스터-디테일' },
  hierarchy: { stroke: 'hsl(var(--info))', strokeLight: 'hsl(var(--info) / 0.4)', label: '계층 구조' },
  lookup: { stroke: 'hsl(var(--warning))', strokeLight: 'hsl(var(--warning) / 0.4)', label: '코드 참조' },
  mapping: { stroke: 'hsl(var(--success))', strokeLight: 'hsl(var(--success) / 0.4)', label: '데이터 매핑' },
  join: { stroke: 'hsl(var(--warning))', strokeLight: 'hsl(var(--warning) / 0.4)', label: '엔티티 조인' },
};

// 엣지 필터 카테고리 (UI 토글용)
type EdgeCategory = 'main' | 'filter' | 'join' | 'lookup' | 'flow';

// 노드 타입 등록
const nodeTypes = {
  screenNode: ScreenNode,
  tableNode: TableNode,
};

const edgeTypes = {
  animatedFlow: AnimatedFlowEdge,
};

// 레이아웃 상수
const SCREEN_Y = 50; // 화면 노드 Y 위치 (상단)
const TABLE_Y = 420; // 메인 테이블 노드 Y 위치 (중단)
const SUB_TABLE_Y = 740; // 서브 테이블 노드 Y 위치 (하단) - 메인과 320px 간격
const NODE_WIDTH = 260; // 노드 너비
const NODE_GAP = 40; // 노드 간격

interface ScreenRelationFlowProps {
  screen: ScreenDefinition | null;
  selectedGroup?: { id: number; name: string; company_code?: string } | null;
  initialFocusedScreenId?: number | null;
  isPop?: boolean;
}

// 노드 타입 (Record<string, unknown> 확장)
type ScreenNodeType = Node<ScreenNodeData & Record<string, unknown>>;
type TableNodeType = Node<TableNodeData & Record<string, unknown>>;
type AllNodeType = ScreenNodeType | TableNodeType;

// 내부 컴포넌트 (useReactFlow 사용 가능)
function ScreenRelationFlowInner({ screen, selectedGroup, initialFocusedScreenId, isPop = false }: ScreenRelationFlowProps) {
  const [nodes, setNodes, onNodesChange] = useNodesState<AllNodeType>([]);
  const [edges, setEdges, onEdgesChange] = useEdgesState<Edge>([]);
  const [loading, setLoading] = useState(false);
  const [tableColumns, setTableColumns] = useState<Record<string, ColumnTypeInfo[]>>({});
  
  // ReactFlow 인스턴스 (fitView 제어용)
  const reactFlowInstance = useReactFlow();
  
  // 데이터 로드 버전 (초기 로드 시에만 fitView 호출)
  const [dataLoadVersion, setDataLoadVersion] = useState(0);
  
  // 뷰 준비 상태 (fitView 완료 후 true로 설정하여 깜빡임 방지)
  const [isViewReady, setIsViewReady] = useState(false);
  
  
  // 그룹 내 포커스된 화면 ID (그룹 모드에서만 사용)
  const [focusedScreenId, setFocusedScreenId] = useState<number | null>(null);
  
  // 엣지 필터 상태 (유형별 표시/숨김)
  const [edgeFilterState, setEdgeFilterState] = useState<Record<EdgeCategory, boolean>>({
    main: true,
    filter: true,
    join: true,
    lookup: false,
    flow: true,
  });
  
  // 노드 설정 모달 상태
  const [isSettingModalOpen, setIsSettingModalOpen] = useState(false);
  const [settingModalNode, setSettingModalNode] = useState<{
    nodeType: "screen" | "table";
    nodeId: string;
    screenId: number;
    screenName: string;
    tableName?: string;
    tableLabel?: string;
    companyCode?: string; // 프리뷰용 회사 코드
    // 기존 설정 정보 (화면 디자이너에서 추출)
    existingConfig?: {
      joinColumnRefs?: Array<{
        column: string;
        refTable: string;
        refTableLabel?: string;
        refColumn: string;
      }>;
      filterColumns?: string[];
      fieldMappings?: Array<{
        targetField: string;
        sourceField: string;
        sourceTable?: string;
        sourceDisplayName?: string;
      }>;
      referencedBy?: Array<{
        fromTable: string;
        fromTableLabel?: string;
        fromColumn: string;
        toColumn: string;
        toColumnLabel?: string;
        relationType: string;
      }>;
      columns?: Array<{
        name: string;
        originalName?: string;
        type: string;
        isPrimaryKey?: boolean;
        isForeignKey?: boolean;
      }>;
      // 화면 노드용 테이블 정보
      mainTable?: string;
      filterTables?: Array<{
        tableName: string;
        tableLabel: string;
        filterColumns: string[];
        joinColumnRefs: Array<{
          column: string;
          refTable: string;
          refTableLabel?: string;
          refColumn: string;
        }>;
      }>;
    };
  } | null>(null);
  
  // 강제 새로고침용 키 (설정 저장 후 시각화 재로딩)
  const [refreshKey, setRefreshKey] = useState(0);
  
  // 화면 삭제/추가 시 노드 플로워 새로고침 (screen-list-refresh 이벤트 구독)
  useEffect(() => {
    const handleScreenListRefresh = () => {
      // refreshKey 증가로 데이터 재로드 트리거
      setRefreshKey(prev => prev + 1);
    };
    
    window.addEventListener("screen-list-refresh", handleScreenListRefresh);
    return () => {
      window.removeEventListener("screen-list-refresh", handleScreenListRefresh);
    };
  }, []);
  
  // 그룹 또는 화면이 변경될 때 포커스 초기화
  useEffect(() => {
    setFocusedScreenId(null);
  }, [selectedGroup?.id, screen?.screenId]);
  
  // 외부에서 전달된 초기 포커스 ID 적용 (화면 이동 없이 강조만)
  useEffect(() => {
    if (initialFocusedScreenId !== undefined) {
      setFocusedScreenId(initialFocusedScreenId);
    }
  }, [initialFocusedScreenId]);
  
  // 화면 ID와 테이블명 매핑 (포커스 시 연결선 강조용)
  const [screenTableMap, setScreenTableMap] = useState<Record<number, string>>({});
  
  // 화면 ID별 서브 테이블 매핑 (포커스 시 서브 테이블 연결선 강조용)
  const [screenSubTableMap, setScreenSubTableMap] = useState<Record<number, string[]>>({});
  
  // 서브 테이블 데이터 저장 (조인 컬럼 정보 포함)
  const [subTablesDataMap, setSubTablesDataMap] = useState<Record<number, ScreenSubTablesData>>({});
  
  // 화면별 사용 컬럼 매핑 (화면 ID -> 테이블명 -> 사용 컬럼들)
  const [screenUsedColumnsMap, setScreenUsedColumnsMap] = useState<Record<number, Record<string, string[]>>>({});
  
  // 전역 메인 테이블 목록 (우선순위: 메인 > 서브)
  // 이 목록에 있는 테이블은 서브 테이블로 분류되지 않음
  const [globalMainTables, setGlobalMainTables] = useState<Set<string>>(new Set());

  // 테이블 컬럼 정보 로드
  const loadTableColumns = useCallback(
    async (tableName: string): Promise<ColumnTypeInfo[]> => {
      if (!tableName) return [];
      if (tableColumns[tableName]) return tableColumns[tableName];
    
    try {
        const response = await getTableColumns(tableName);
        if (response.success && response.data && response.data.columns) {
          const columns = response.data.columns;
          setTableColumns((prev) => ({ ...prev, [tableName]: columns }));
          return columns;
      }
    } catch (error) {
      console.error(`테이블 ${tableName} 컬럼 로드 실패:`, error);
    }
    return [];
    },
    [tableColumns]
  );
  
  // 중복 useEffect 제거됨 (위에서 이미 선언)

  // 데이터 로드 및 노드/엣지 생성
  useEffect(() => {
    // 그룹도 없고 화면도 없으면 빈 상태
    if (!screen && !selectedGroup) {
      setNodes([]);
      setEdges([]);
      return;
    }

    const loadRelations = async () => {
      setLoading(true);
      setIsViewReady(false); // 뷰 준비 상태 초기화
      
      try {
        let screenList: ScreenDefinition[] = [];
        
        // ========== 그룹 선택 시: 그룹의 화면들 로드 ==========
        if (selectedGroup) {
          const groupRes = await getScreenGroup(selectedGroup.id);
          if (groupRes.success && groupRes.data) {
            const groupData = groupRes.data as any;
            const groupScreens = groupData.screens || [];
            
            // display_order 순으로 정렬
            groupScreens.sort((a: any, b: any) => (a.display_order || 0) - (b.display_order || 0));
            
            // screen_definitions 형식으로 변환 (table_name 포함)
            screenList = groupScreens.map((gs: any) => ({
              screenId: gs.screen_id,
              screenName: gs.screen_name || `화면 ${gs.screen_id}`,
              screenCode: gs.screen_code || "",
              tableName: gs.table_name || "", // 테이블명 포함
              companyCode: groupData.company_code,
              isActive: "Y",
              createdDate: new Date(),
              updatedDate: new Date(),
              screenRole: gs.screen_role, // screen_role 추가
              displayOrder: gs.display_order, // display_order 추가
            } as ScreenDefinition & { screenRole?: string; displayOrder?: number }));
          }
        } else if (screen) {
          // 기존 방식: 선택된 화면 중심
          screenList = [screen];
        }
        
        if (screenList.length === 0) {
          setNodes([]);
          setEdges([]);
          setLoading(false);
          return;
        }
        
        // 화면-테이블 매핑 저장 (포커스 시 연결선 강조용)
        const newScreenTableMap: Record<number, string> = {};
        screenList.forEach((scr: any) => {
          if (scr.tableName) {
            newScreenTableMap[scr.screenId] = scr.tableName;
          }
        });
        setScreenTableMap(newScreenTableMap);

        // 관계 데이터 로드 (첫 번째 화면 기준)
        const [joinsRes, flowsRes, relationsRes] = await Promise.all([
          getFieldJoins(screenList[0].screenId).catch(() => ({ success: false, data: [] })),
          getDataFlows().catch(() => ({ success: false, data: [] })),
          getTableRelations({ screen_id: screenList[0].screenId }).catch(() => ({ success: false, data: [] })),
        ]);

        const joins = joinsRes.success ? joinsRes.data || [] : [];
        const flows = flowsRes.success ? flowsRes.data || [] : [];
        const relations = relationsRes.success ? relationsRes.data || [] : [];
        
        // 데이터 흐름에서 연결된 화면들 추가 (개별 화면 모드에서만 - 그룹 모드에서는 그룹 내 화면만 표시)
        if (!selectedGroup && screen) {
          flows.forEach((flow: any) => {
            if (flow.source_screen_id === screen.screenId && flow.target_screen_id) {
              const exists = screenList.some((s) => s.screenId === flow.target_screen_id);
              if (!exists) {
                screenList.push({
                  screenId: flow.target_screen_id,
                  screenName: flow.target_screen_name || `화면 ${flow.target_screen_id}`,
                  screenCode: "",
                  tableName: "",
                  companyCode: screen.companyCode,
                  isActive: "Y",
                  createdDate: new Date(),
                  updatedDate: new Date(),
                } as ScreenDefinition);
              }
            }
          });
        }

        // 화면 레이아웃 요약 정보 로드
        const screenIds = screenList.map((s) => s.screenId);
        let layoutSummaries: Record<number, ScreenLayoutSummary> = {};
        let subTablesData: Record<number, ScreenSubTablesData> = {};
        try {
          // 레이아웃 요약과 서브 테이블 정보 병렬 로드
          const [layoutRes, subTablesRes] = await Promise.all([
            getMultipleScreenLayoutSummary(screenIds),
            getScreenSubTables(screenIds),
          ]);
          
          if (layoutRes.success && layoutRes.data) {
            // API 응답이 Record 형태 (screenId -> summary)
            layoutSummaries = layoutRes.data as Record<number, ScreenLayoutSummary>;
          }
          
          if (subTablesRes.success && subTablesRes.data) {
            subTablesData = subTablesRes.data as Record<number, ScreenSubTablesData>;
            // 서브 테이블 데이터 저장 (조인 컬럼 정보 포함)
            setSubTablesDataMap(subTablesData);
            
            // 전역 메인 테이블 목록 저장 (우선순위 적용용)
            // 이 목록에 있는 테이블은 서브 테이블로 분류되지 않음
            const globalMainTablesArr = (subTablesRes as any).globalMainTables as string[] | undefined;
            if (globalMainTablesArr && Array.isArray(globalMainTablesArr)) {
              setGlobalMainTables(new Set(globalMainTablesArr));
            }
          }
        } catch (e) {
          console.error("레이아웃 요약/서브 테이블 로드 실패:", e);
        }
        
        // 화면별 사용 컬럼 정보 추출 (layoutSummaries에서)
        const usedColumnsMap: Record<number, Record<string, string[]>> = {};
        screenList.forEach((screenItem) => {
          const layout = layoutSummaries[screenItem.screenId];
          if (layout && layout.layoutItems) {
            const mainTable = screenItem.tableName;
            if (mainTable) {
              // layoutItems에서 사용 컬럼과 조인 컬럼 추출
              const allUsedColumns: string[] = [];
              const allJoinColumns: string[] = [];
              
              layout.layoutItems.forEach((item) => {
                // usedColumns 배열에서 추출 (columns_config에서 가져온 컬럼명)
                if (item.usedColumns && Array.isArray(item.usedColumns)) {
                  item.usedColumns.forEach((col) => {
                    if (col && !allUsedColumns.includes(col)) {
                      allUsedColumns.push(col);
                    }
                  });
                }
                // joinColumns 배열에서 추출 (isEntityJoin = true인 컬럼)
                if (item.joinColumns && Array.isArray(item.joinColumns)) {
                  item.joinColumns.forEach((col) => {
                    if (col && !allJoinColumns.includes(col)) {
                      allJoinColumns.push(col);
                    }
                  });
                }
                // 하위 호환성: bindField도 사용 컬럼에 추가
                if (item.bindField && !allUsedColumns.includes(item.bindField)) {
                  allUsedColumns.push(item.bindField);
                }
              });
              
              if (!usedColumnsMap[screenItem.screenId]) {
                usedColumnsMap[screenItem.screenId] = {};
              }
              // 사용 컬럼과 조인 컬럼을 별도 키로 저장
              usedColumnsMap[screenItem.screenId][mainTable] = allUsedColumns;
              usedColumnsMap[screenItem.screenId][`${mainTable}__join`] = allJoinColumns;
            }
          }
        });
        setScreenUsedColumnsMap(usedColumnsMap);

        // ========== 상단: 화면 노드들 ==========
        const screenNodes: ScreenNodeType[] = [];
        const screenStartX = 50;
        
        // screen_role 레이블 매핑
        const getRoleLabel = (role?: string) => {
          if (!role || role === "member") return "화면";
          const roleMap: Record<string, string> = {
            main_list: "메인 그리드",
            register_form: "등록 폼",
            popup: "팝업",
            detail: "상세",
          };
          return roleMap[role] || role;
        };

        screenList.forEach((scr: any, idx) => {
          const isMain = screen && scr.screenId === screen.screenId;
          const summary = layoutSummaries[scr.screenId];
          const roleLabel = getRoleLabel(scr.screenRole);
          
          // 포커스 여부 결정 (그룹 모드 & 개별 화면 모드 모두 지원)
          const isInGroup = !!selectedGroup;
          let isFocused: boolean;
          let isFaded: boolean;
          
          if (isInGroup) {
            // 그룹 모드: 클릭한 화면만 포커스
            isFocused = focusedScreenId === scr.screenId;
            isFaded = focusedScreenId !== null && !isFocused;
          } else {
            // 개별 화면 모드: 메인 화면(선택된 화면)만 포커스, 연결 화면은 흐리게
            isFocused = !!isMain;
            isFaded = !isMain && screenList.length > 1;
          }

          screenNodes.push({
            id: `screen-${scr.screenId}`,
            type: "screenNode",
            position: { x: screenStartX + idx * (NODE_WIDTH + NODE_GAP), y: SCREEN_Y },
            data: {
              label: scr.screenName,
              subLabel: selectedGroup ? `${roleLabel} (#${scr.displayOrder || idx + 1})` : (isMain ? "메인 화면" : "연결 화면"),
              type: "screen",
              isMain: selectedGroup ? idx === 0 : !!isMain,
              tableName: scr.tableName,
              layoutSummary: summary,
              // 화면 포커스 관련 속성 (그룹 모드 & 개별 모드 공통)
              isInGroup,
              isFocused,
              isFaded,
              screenRole: scr.screenRole,
            },
          });
        });

        // ========== 중단: 메인 테이블 노드들 ==========
        const tableNodes: TableNodeType[] = [];
        const mainTableSet = new Set<string>();
        const subTableSet = new Set<string>();

        // 모든 화면의 메인 테이블 추가
        screenList.forEach((scr) => {
          if (scr.tableName) {
            mainTableSet.add(scr.tableName);
          }
        });

        // 조인된 테이블들 (screen_field_joins에서)
        joins.forEach((join: any) => {
          if (join.save_table) mainTableSet.add(join.save_table);
          if (join.join_table) mainTableSet.add(join.join_table);
        });

        // 테이블 관계에서 추가
        relations.forEach((rel: any) => {
          if (rel.table_name) mainTableSet.add(rel.table_name);
        });
        
        // ============================================================
        // 테이블 분류 (우선순위: 메인 > 서브)
        // ============================================================
        // 메인 테이블 조건:
        // 1. screen_definitions.table_name (컴포넌트 직접 연결) - 이미 mainTableSet에 추가됨
        // 2. globalMainTables (WHERE 조건 대상, 마스터-디테일의 디테일 테이블)
        // 
        // 서브 테이블 조건:
        // - 조인(JOIN)으로만 연결된 테이블 (autocomplete 등에서 참조)
        // - 단, mainTableSet에 있으면 제외 (우선순위: 메인 > 서브)
        
        // 1. globalMainTables를 mainTableSet에 먼저 추가 (우선순위 적용)
        const filterTableSet = new Set<string>(); // 마스터-디테일의 디테일 테이블들
        globalMainTables.forEach((tableName) => {
          if (!mainTableSet.has(tableName)) {
            mainTableSet.add(tableName);
            filterTableSet.add(tableName);  // 필터 테이블로 분류 (보라색 테두리)
          }
        });
        
        // 2. 서브 테이블 수집 (mainTableSet에 없는 것만)
        const newScreenSubTableMap: Record<number, string[]> = {};
        
        Object.entries(subTablesData).forEach(([screenIdStr, screenSubData]) => {
          const screenId = parseInt(screenIdStr);
          const subTableNames: string[] = [];
          
          screenSubData.subTables.forEach((subTable) => {
            // mainTableSet에 있으면 서브 테이블에서 제외 (우선순위: 메인 > 서브)
            if (mainTableSet.has(subTable.tableName)) {
              return;
            }
            
            // 조인으로만 연결된 테이블 → 서브 테이블
            subTableSet.add(subTable.tableName);
            subTableNames.push(subTable.tableName);
          });
          
          if (subTableNames.length > 0) {
            newScreenSubTableMap[screenId] = subTableNames;
          }
        });
        
        // 화면별 서브 테이블 매핑 저장
        setScreenSubTableMap(newScreenSubTableMap);
        
        // ========== 참조 관계 수집 (어떤 테이블이 어디서 참조되는지) ==========
        // 테이블명 → 참조 정보 배열 (이 테이블을 참조하는 관계들)
        const tableReferencesMap = new Map<string, ReferenceInfo[]>();
        
        Object.entries(subTablesData).forEach(([screenIdStr, screenSubData]) => {
          const mainTable = screenSubData.mainTable;
          
          screenSubData.subTables.forEach((subTable) => {
            const visualType = inferVisualRelationType(subTable);
            
            // 1. lookup, reference 관계: 참조되는 테이블에 정보 추가
            if (subTable.relationType === 'lookup' || subTable.relationType === 'reference') {
              const existingRefs = tableReferencesMap.get(subTable.tableName) || [];
              existingRefs.push({
                fromTable: mainTable,
                fromColumn: subTable.fieldMappings?.[0]?.sourceField || '',
                toColumn: subTable.fieldMappings?.[0]?.targetField || '',
                relationType: 'lookup',
              });
              tableReferencesMap.set(subTable.tableName, existingRefs);
            }
            
            // 2. rightPanelRelation (마스터-디테일 필터링): 디테일 테이블에 정보 추가
            // 마스터(mainTable) → 디테일(subTable.tableName) 필터링 관계
            if (subTable.relationType === 'rightPanelRelation') {
              const existingRefs = tableReferencesMap.get(subTable.tableName) || [];
              existingRefs.push({
                fromTable: mainTable,
                fromColumn: subTable.leftColumn || '',  // 마스터 테이블의 선택 기준 컬럼
                toColumn: subTable.foreignKey || '',    // 디테일 테이블의 FK 컬럼
                relationType: 'filter',
              });
              tableReferencesMap.set(subTable.tableName, existingRefs);
            }
          });
        });

        // 메인 테이블 노드 배치 (화면들의 중앙 아래에 배치)
        const mainTableList = Array.from(mainTableSet);
        
        // 화면 노드들의 총 너비 계산
        const screenTotalWidth = screenList.length * NODE_WIDTH + (screenList.length - 1) * NODE_GAP;
        const screenCenterX = screenStartX + screenTotalWidth / 2;
        
        // 메인 테이블 노드들의 총 너비 계산
        const mainTableTotalWidth = mainTableList.length * NODE_WIDTH + (mainTableList.length - 1) * NODE_GAP;
        const mainTableStartX = screenCenterX - mainTableTotalWidth / 2;
        
        // 각 테이블이 어떤 화면들의 메인 테이블인지 매핑
        const tableToScreensMap = new Map<string, string[]>();
        screenList.forEach((scr: any) => {
          if (scr.tableName) {
            const screens = tableToScreensMap.get(scr.tableName) || [];
            screens.push(scr.screenName);
            tableToScreensMap.set(scr.tableName, screens);
          }
        });
        
        for (let idx = 0; idx < mainTableList.length; idx++) {
          const tableName = mainTableList[idx];
          // mainTableSet에 있는 테이블은 모두 해당 화면의 "메인 테이블"
          const linkedScreens = tableToScreensMap.get(tableName) || [];
          
          // 컬럼 정보 로드
          let columns: ColumnTypeInfo[] = [];
          try {
            columns = await loadTableColumns(tableName);
          } catch (e) {
            // ignore
          }

          // 컬럼 정보를 PK/FK 표시와 함께 변환 (전체 컬럼 저장, 표시는 TableNode에서 제한)
          const formattedColumns = columns.map((col) => ({
            name: col.displayName || col.columnName || "",
            originalName: col.columnName || "",  // 영문 컬럼명 (필터링용)
            type: col.dataType || "",
            isPrimaryKey: col.isPrimaryKey || col.columnName === "id",
            isForeignKey: !!col.referenceTable || (col.columnName?.includes("_id") && col.columnName !== "id"),
          }));

          // 테이블 분류에 따른 라벨 결정
          // 1. 필터 테이블 (마스터-디테일의 디테일): "필터 대상 테이블"
          // 2. 여러 화면이 같은 테이블 사용: "공통 메인 테이블 (N개 화면)"
          // 3. 일반 메인 테이블: "메인 테이블"
          const isFilterTable = filterTableSet.has(tableName);
          let subLabel: string;
          if (isFilterTable) {
            subLabel = "필터 대상 테이블 (마스터-디테일)";
          } else if (linkedScreens.length > 1) {
            subLabel = `메인 테이블 (${linkedScreens.length}개 화면)`;
          } else {
            subLabel = "메인 테이블";
          }

          // 이 테이블을 참조하는 관계들
          tableNodes.push({
            id: `table-${tableName}`,
            type: "tableNode",
            position: { x: mainTableStartX + idx * (NODE_WIDTH + NODE_GAP), y: TABLE_Y },
            data: {
              label: tableName,
              subLabel: subLabel,
              isMain: !isFilterTable, // 필터 테이블은 isMain: false로 설정 (보라색 테두리 표시용)
              isFilterTable: isFilterTable, // 필터 테이블 여부 표시
              columns: formattedColumns,
              // referencedBy, filterColumns, saveInfos는 styledNodes에서 포커스 상태에 따라 동적으로 설정
            },
          });
        }

        // ========== 하단: 서브 테이블 노드들 (참조/조회용) ==========
        const subTableList = Array.from(subTableSet);
        
        if (subTableList.length > 0) {
          // 서브 테이블 노드들의 총 너비 계산
          const subTableTotalWidth = subTableList.length * NODE_WIDTH + (subTableList.length - 1) * NODE_GAP;
          const subTableStartX = screenCenterX - subTableTotalWidth / 2;
          
          for (let idx = 0; idx < subTableList.length; idx++) {
            const tableName = subTableList[idx];
            
            // 컬럼 정보 로드
            let columns: ColumnTypeInfo[] = [];
            try {
              columns = await loadTableColumns(tableName);
            } catch (e) {
              // ignore
            }

            // 컬럼 정보를 PK/FK 표시와 함께 변환 (전체 컬럼 저장, 표시는 TableNode에서 제한)
            const formattedColumns = columns.map((col) => ({
              name: col.displayName || col.columnName || "",
              originalName: col.columnName || "",  // 영문 컬럼명 (필터링용)
              type: col.dataType || "",
              isPrimaryKey: col.isPrimaryKey || col.columnName === "id",
              isForeignKey: !!col.referenceTable || (col.columnName?.includes("_id") && col.columnName !== "id"),
            }));
            
            // 서브 테이블의 관계 타입 결정
            let relationType = "참조";
            Object.values(subTablesData).forEach((screenSubData) => {
              const matchedSub = screenSubData.subTables.find((st) => st.tableName === tableName);
              if (matchedSub) {
                if (matchedSub.relationType === "lookup") relationType = "조회";
                else if (matchedSub.relationType === "source") relationType = "데이터 소스";
                else if (matchedSub.relationType === "join") relationType = "조인";
              }
            });

            tableNodes.push({
              id: `subtable-${tableName}`,
              type: "tableNode",
              position: { x: subTableStartX + idx * (NODE_WIDTH + NODE_GAP), y: SUB_TABLE_Y },
              data: {
                label: tableName,
                subLabel: `서브 테이블 (${relationType})`,
                isMain: false,
                columns: formattedColumns,
                isFaded: true, // 기본적으로 흐리게 표시 (포커스 시에만 활성화)
                // referencedBy, filterColumns, saveInfos는 styledNodes에서 포커스 상태에 따라 동적으로 설정
              },
            });
          }
        }

        // ========== 엣지: 연결선 생성 ==========
        const newEdges: Edge[] = [];

        // 그룹 선택 시: 화면 간 연결선 (display_order 순)
        if (selectedGroup && screenList.length > 1) {
          for (let i = 0; i < screenList.length - 1; i++) {
            const currentScreen = screenList[i];
            const nextScreen = screenList[i + 1];
            
          newEdges.push({
              id: `edge-screen-flow-${i}`,
              source: `screen-${currentScreen.screenId}`,
              target: `screen-${nextScreen.screenId}`,
              sourceHandle: "right",
              targetHandle: "left",
            type: "animatedFlow",
              label: `${i + 1}`,
              labelStyle: { fontSize: 11, fill: "hsl(var(--info))", fontWeight: 600 },
              labelBgStyle: { fill: "hsl(var(--card))", stroke: "hsl(var(--border))", strokeWidth: 1 },
              labelBgPadding: [4, 2] as [number, number],
              markerEnd: { type: MarkerType.ArrowClosed, color: "hsl(var(--info))" },
            animated: true,
              style: { stroke: "hsl(var(--info))", strokeWidth: 2 },
              data: { edgeCategory: 'flow' as EdgeCategory },
            });
          }
        }

        // 각 화면 → 해당 메인 테이블 연결선 생성 (실선)
        // 모든 화면-테이블 연결은 동일한 스타일 (각 화면의 메인 테이블이므로)
        screenList.forEach((scr) => {
          if (scr.tableName && mainTableSet.has(scr.tableName)) {
            newEdges.push({
              id: `edge-screen-table-${scr.screenId}`,
              source: `screen-${scr.screenId}`,
              target: `table-${scr.tableName}`,
            sourceHandle: "bottom",
            targetHandle: "top",
            type: "animatedFlow",
              animated: true, // 모든 메인 테이블 연결은 애니메이션
              style: { 
                stroke: "hsl(var(--primary))", 
                strokeWidth: 2,
              },
              data: { edgeCategory: 'main' as EdgeCategory },
            });
          }
        });
        
        // 필터링 관계일 때 화면 → 필터 대상 테이블 연결선 추가 (점선)
        // rightPanelRelation (split-panel-layout의 마스터-디테일) 관계일 때
        // + 필터 대상 테이블의 조인 관계도 함께 표시
        const filterJoinEdgeSet = new Set<string>(); // 필터 테이블의 조인선 중복 방지
        
        Object.entries(subTablesData).forEach(([screenIdStr, screenSubData]) => {
          const sourceScreenId = parseInt(screenIdStr);
          
          screenSubData.subTables.forEach((subTable) => {
            // rightPanelRelation (필터 관계)이고, 해당 테이블이 존재하는 경우
            // 메인 테이블이든 서브 테이블이든 상관없이 연결선 추가
            if (subTable.relationType === 'rightPanelRelation') {
              // 테이블 노드 ID 결정: 메인 테이블 영역 또는 서브 테이블 영역
              const isFilterTargetMainTable = mainTableSet.has(subTable.tableName);
              const isFilterTargetSubTable = subTableSet.has(subTable.tableName);
              
              if (!isFilterTargetMainTable && !isFilterTargetSubTable) return; // 노드가 없으면 스킵
              
              const targetNodeId = isFilterTargetMainTable 
                ? `table-${subTable.tableName}` 
                : `subtable-${subTable.tableName}`;
              
              // 화면 → 필터 대상 테이블 연결선
              newEdges.push({
                id: `edge-screen-filter-${sourceScreenId}-${subTable.tableName}`,
                source: `screen-${sourceScreenId}`,
                target: targetNodeId,
                sourceHandle: "bottom",
                targetHandle: "top",
                type: "animatedFlow",
                animated: true,
                style: { 
                  stroke: "hsl(var(--primary))", 
                  strokeWidth: 2,
                  strokeDasharray: "5,5", // 점선으로 필터 관계 표시
                },
                data: {
                  sourceScreenId,
                  edgeCategory: 'filter' as EdgeCategory,
                },
              });
              
              // 필터 대상 테이블의 조인 관계 (joinColumnRefs)도 조인선으로 표시
              // 예: customer_item_mapping → item_info (품목 ID가 item_info.item_number 참조)
              if (subTable.joinColumnRefs && subTable.joinColumnRefs.length > 0) {
                subTable.joinColumnRefs.forEach((joinRef) => {
                  const refTable = joinRef.refTable;
                  if (!refTable) return;
                  
                  // 참조 테이블이 메인 테이블 또는 서브 테이블에 있는지 확인
                  const isRefMainTable = mainTableSet.has(refTable);
                  const isRefSubTable = subTableSet.has(refTable);
                  
                  if (!isRefMainTable && !isRefSubTable) return;
                  
                  // 중복 체크 (같은 화면에서 같은 조인 관계 중복 방지)
                  const joinKey = `${sourceScreenId}-${subTable.tableName}-${refTable}`;
                  if (filterJoinEdgeSet.has(joinKey)) return;
                  filterJoinEdgeSet.add(joinKey);
                  
                  // 소스/타겟 노드 ID 결정
                  const sourceNodeId = isFilterTargetMainTable 
                    ? `table-${subTable.tableName}` 
                    : `subtable-${subTable.tableName}`;
                  const refTargetNodeId = isRefMainTable 
                    ? `table-${refTable}` 
                    : `subtable-${refTable}`;
                  
                  // 조인선 추가 (초기 스타일 - styledEdges에서 포커싱에 따라 스타일 결정)
                  newEdges.push({
                    id: `edge-filter-join-${sourceScreenId}-${subTable.tableName}-${refTable}`,
                    source: sourceNodeId,
                    target: refTargetNodeId,
                    sourceHandle: "bottom",
                    targetHandle: "bottom_target",
                    type: "animatedFlow",
                    animated: false,
                    style: { 
                      stroke: RELATION_COLORS.join.strokeLight, // 초기값 (연한색)
                      strokeWidth: 1.5, 
                      strokeDasharray: "6,4",
                      opacity: 0.3,
                    },
                    markerEnd: { 
                      type: MarkerType.ArrowClosed, 
                      color: RELATION_COLORS.join.strokeLight
                    },
                    data: { 
                      sourceScreenId, 
                      isFilterJoin: true,
                      visualRelationType: 'join',
                      edgeCategory: 'join' as EdgeCategory,
                    },
                  });
                });
              }
            }
          });
        });

        // 메인 테이블 → 서브 테이블 연결선 생성 (점선)
        // 메인 테이블 → 메인 테이블 연결선도 생성 (점선, 연한 주황색)
        // 화면별 서브 테이블 연결을 추적하기 위해 screenId 정보도 엣지 ID에 포함
        const mainToMainEdgeSet = new Set<string>(); // 중복 방지용
        
        Object.entries(subTablesData).forEach(([screenIdStr, screenSubData]) => {
          const sourceScreenId = parseInt(screenIdStr);
          const mainTable = screenSubData.mainTable;
          if (!mainTable || !mainTableSet.has(mainTable)) return;
          
          screenSubData.subTables.forEach((subTable) => {
            const isTargetSubTable = subTableSet.has(subTable.tableName);
            const isTargetMainTable = mainTableSet.has(subTable.tableName);
            
            // 자기 자신 연결 방지
            if (mainTable === subTable.tableName) return;
            
            // 메인 테이블 → 메인 테이블 연결 (서브테이블 구간을 통해 연결)
            // 규격: bottom → bottom_target (참조하는 테이블에서 참조당하는 테이블로)
            // 
            // 방향 결정 로직 (범용성):
            // 1. parentMapping (fieldMappings에 sourceTable 있음): mainTable → sourceTable
            //    - mainTable이 sourceTable을 참조하는 관계
            //    - 예: customer_item_mapping.customer_id → customer_mng.customer_code
            // 2. rightPanelRelation (foreignKey 있음): subTable → mainTable  
            //    - subTable이 mainTable을 참조하는 관계
            //    - 예: customer_item_mapping.customer_id → customer_mng.customer_code
            // 3. reference (column_labels): mainTable → subTable
            //    - mainTable이 subTable을 참조하는 관계
            if (isTargetMainTable) {
              // 실제 참조 방향 결정
              let referrerTable: string;  // 참조하는 테이블 (source)
              let referencedTable: string; // 참조당하는 테이블 (target)
              
              // fieldMappings에서 sourceTable 확인
              const hasSourceTable = subTable.fieldMappings?.some(
                (fm: any) => fm.sourceTable && fm.sourceTable === subTable.tableName
              );
              
              if (subTable.relationType === 'parentMapping' || hasSourceTable) {
                // parentMapping: mainTable이 sourceTable(=subTable.tableName)을 참조
                // 방향: mainTable → subTable.tableName
                referrerTable = mainTable;
                referencedTable = subTable.tableName;
              } else if (subTable.relationType === 'rightPanelRelation') {
                // rightPanelRelation: split-panel-layout의 마스터-디테일 관계
                // mainTable(leftPanel, 마스터)이 subTable(rightPanel, 디테일)을 필터링
                // 방향: mainTable(마스터) → subTable(디테일)
                referrerTable = mainTable;
                referencedTable = subTable.tableName;
              } else if (subTable.relationType === 'reference') {
                // reference (column_labels): mainTable이 subTable을 참조
                // 방향: mainTable → subTable.tableName
                referrerTable = mainTable;
                referencedTable = subTable.tableName;
              } else {
                // 기본: subTable이 mainTable을 참조한다고 가정
                referrerTable = subTable.tableName;
                referencedTable = mainTable;
              }
              
              // 화면별로 고유한 키 생성 (같은 테이블 쌍이라도 다른 화면에서는 별도 엣지)
              const pairKey = `${sourceScreenId}-${[mainTable, subTable.tableName].sort().join('-')}`;
              if (!mainToMainEdgeSet.has(pairKey)) {
                mainToMainEdgeSet.add(pairKey);
                
                // 관계 유형 추론 및 색상 결정
                const visualRelationType = inferVisualRelationType(subTable as SubTableInfo);
                
                // 방안 C: 필터 관계는 선 없이 뱃지로만 표시 (겹침 방지)
                if (visualRelationType === 'filter') {
                  return; // 필터선 생성 건너뛰기
                }
                
                const relationColor = RELATION_COLORS[visualRelationType];
                
                // 화면별로 고유한 엣지 ID
                const edgeId = `edge-main-main-${sourceScreenId}-${referrerTable}-${referencedTable}`;
            newEdges.push({
                  id: edgeId,
                  source: `table-${referrerTable}`,     // 참조하는 테이블
                  target: `table-${referencedTable}`,   // 참조당하는 테이블
                  sourceHandle: "bottom",         // 하단에서 나감 (서브테이블 구간으로)
                  targetHandle: "bottom_target",  // 하단으로 들어감
              type: "animatedFlow",
                  animated: false,
                  style: { 
                    stroke: relationColor.strokeLight, // 관계 유형별 연한 색상
                    strokeWidth: 1.5, 
                    strokeDasharray: "8,4",
                    opacity: 0.5,
                  },
                  markerEnd: { 
                    type: MarkerType.ArrowClosed, 
                    color: relationColor.strokeLight
                  },
                  data: { 
                    sourceScreenId, 
                    isMainToMain: true, 
                    referrerTable, 
                    referencedTable,
                    visualRelationType,  // 관계 유형 저장
                    edgeCategory: (visualRelationType === 'lookup' ? 'lookup' : 'join') as EdgeCategory,
                  },
                });
              }
              return; // 메인-메인은 위에서 처리했으므로 스킵
            }
            
            // 서브 테이블이 아니면 스킵
            if (!isTargetSubTable) return;
            
            // 화면별로 고유한 엣지 ID (같은 서브 테이블이라도 다른 화면에서 사용하면 별도 엣지)
            const edgeId = `edge-main-sub-${sourceScreenId}-${mainTable}-${subTable.tableName}`;
            const exists = newEdges.some((e) => e.id === edgeId);
            if (exists) return;
            
            // 관계 유형 결정 (스타일링용)
            const visualRelationType = inferVisualRelationType(subTable);
            const relationColor = RELATION_COLORS[visualRelationType];
            
            // 메인-서브 조인선 (메인-메인과 동일한 스타일, 라벨 없음)
            newEdges.push({
              id: edgeId,
              source: `table-${mainTable}`,
              target: `subtable-${subTable.tableName}`,
              sourceHandle: "bottom",
              targetHandle: "top",
              type: "animatedFlow",
              markerEnd: { 
                type: MarkerType.ArrowClosed, 
                color: relationColor.strokeLight
              },
              animated: false,
              style: { 
                stroke: relationColor.strokeLight,
                strokeWidth: 1.5, 
                strokeDasharray: "8,4",
                opacity: 0.5,
              },
              data: { 
                sourceScreenId,
                visualRelationType,
                edgeCategory: (visualRelationType === 'lookup' ? 'lookup' : 'join') as EdgeCategory,
              },
            });
          });
        });

        // 조인 관계 엣지 (screen_field_joins 기반 - 라벨 없이 통일된 스타일)
        joins.forEach((join: any, idx: number) => {
          if (join.save_table && join.join_table && join.save_table !== join.join_table) {
            newEdges.push({
              id: `edge-join-db-${idx}`,
              source: `table-${join.save_table}`,
              target: `table-${join.join_table}`,
              sourceHandle: "bottom",
              targetHandle: "bottom_target",
              type: "animatedFlow",
              markerEnd: { 
                type: MarkerType.ArrowClosed, 
                color: RELATION_COLORS.join.strokeLight 
              },
              animated: false,
              style: { 
                stroke: RELATION_COLORS.join.strokeLight, 
                strokeWidth: 1.5, 
                strokeDasharray: "8,4",
                opacity: 0.5,
              },
              data: { visualRelationType: 'join', edgeCategory: 'join' as EdgeCategory },
            });
          }
        });

        // 테이블 관계 엣지 (추가 관계) - 참조용 화면(개별 모드: screen, 그룹 모드: screenList[0])
        const refScreen = screen ?? screenList[0];
        relations.forEach((rel: any, idx: number) => {
          if (rel.table_name && rel.table_name !== refScreen.tableName) {
            // 화면 → 연결 테이블
            const edgeExists = newEdges.some(
              (e) => e.source === `screen-${refScreen.screenId}` && e.target === `table-${rel.table_name}`
            );
            if (!edgeExists) {
            newEdges.push({
                id: `edge-rel-${idx}`,
                source: `screen-${refScreen.screenId}`,
                target: `table-${rel.table_name}`,
              sourceHandle: "bottom",
              targetHandle: "top",
              type: "animatedFlow",
                label: rel.relation_type === "join" ? "조인" : rel.crud_operations || "",
                labelStyle: { fontSize: 9, fill: "hsl(var(--success))" },
                labelBgStyle: { fill: "hsl(var(--card))", stroke: "hsl(var(--border))", strokeWidth: 1 },
                labelBgPadding: [3, 2] as [number, number],
              style: { stroke: "hsl(var(--success))", strokeWidth: 1.5 },
                data: { edgeCategory: (rel.relation_type === 'lookup' ? 'lookup' : 'join') as EdgeCategory },
            });
          }
          }
        });

        // 데이터 흐름 엣지 (화면 간)
        flows
          .filter((flow: any) => flow.source_screen_id === refScreen.screenId)
          .forEach((flow: any, idx: number) => {
            if (flow.target_screen_id) {
            newEdges.push({
                id: `edge-flow-${idx}`,
                source: `screen-${refScreen.screenId}`,
                target: `screen-${flow.target_screen_id}`,
              sourceHandle: "right",
              targetHandle: "left",
              type: "animatedFlow",
                animated: true,
                label: flow.flow_label || flow.flow_type || "이동",
                labelStyle: { fontSize: 10, fill: "hsl(var(--primary))", fontWeight: 500 },
                labelBgStyle: { fill: "hsl(var(--card))", stroke: "hsl(var(--border))", strokeWidth: 1 },
                labelBgPadding: [4, 2] as [number, number],
                markerEnd: { type: MarkerType.ArrowClosed, color: "hsl(var(--primary))" },
                style: { stroke: "hsl(var(--primary))", strokeWidth: 2 },
                data: { edgeCategory: 'flow' as EdgeCategory },
            });
          }
        });

        // 최종 노드 배열 합치기
        const allNodes: AllNodeType[] = [...screenNodes, ...tableNodes];

        // 테이블이 없으면 안내 노드 추가
        if (tableNodes.length === 0) {
          allNodes.push({
            id: "hint-table",
            type: "tableNode",
            position: { x: 50, y: TABLE_Y },
            data: {
              label: "연결된 테이블 없음",
              subLabel: "화면에 테이블을 설정하세요",
              isMain: false,
              columns: [],
            },
          });
        }

        setNodes(allNodes);
        setEdges(newEdges);
        
        // 데이터 로드 완료 후 버전 증가 (fitView 트리거용)
        setDataLoadVersion((prev) => prev + 1);
      } catch (error) {
        console.error("관계 데이터 로드 실패:", error);
      } finally {
        setLoading(false);
      }
    };

    loadRelations();
  // focusedScreenId는 스타일링에만 영향을 미치므로 의존성에서 제외
  // refreshKey: 설정 저장 후 강제 새로고침용
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [screen, selectedGroup, setNodes, setEdges, loadTableColumns, refreshKey]);
  
  // 데이터 로드 완료 시 fitView 호출 (초기 로드 시에만)
  useEffect(() => {
    if (dataLoadVersion > 0 && nodes.length > 0) {
      // setTimeout으로 노드 렌더링 완료 후 fitView 호출
      const timer = setTimeout(() => {
        // duration: 0으로 설정하여 애니메이션 없이 즉시 이동
        reactFlowInstance.fitView({ padding: 0.2, duration: 0 });
        // fitView 완료 후 뷰 표시
        setIsViewReady(true);
      }, 50);
      return () => clearTimeout(timer);
    }
  }, [dataLoadVersion, reactFlowInstance, nodes.length]);

  // 노드 클릭 핸들러 (그룹 모드에서 화면 포커스) - 조건부 return 전에 선언해야 함
  const handleNodeClick = useCallback((_event: React.MouseEvent, node: Node) => {
    // 그룹 모드가 아니면 무시
    if (!selectedGroup) return;
    
    // 화면 노드만 처리
    if (node.id.startsWith("screen-")) {
      const screenId = parseInt(node.id.replace("screen-", ""));
      // 이미 포커스된 화면을 다시 클릭하면 포커스 해제
      setFocusedScreenId((prev) => (prev === screenId ? null : screenId));
    }
  }, [selectedGroup]);

  // 테이블 정보에서 조인/필터 정보 추출 (더블클릭 핸들러용)
  const getTableExistingConfig = useCallback((tableName: string) => {
    // subTablesDataMap에서 서브 테이블 정보 찾기
    for (const screenId in subTablesDataMap) {
      const screenSubTables = subTablesDataMap[parseInt(screenId)];
      if (screenSubTables?.subTables) {
        const subTable = screenSubTables.subTables.find(st => st.tableName === tableName);
        if (subTable) {
          return {
            joinColumnRefs: subTable.joinColumnRefs,
            filterColumns: subTable.filterColumns,
            fieldMappings: subTable.fieldMappings?.map(m => ({
              targetField: m.targetField,
              sourceField: m.sourceField,
              sourceTable: m.sourceTable,
              sourceDisplayName: m.sourceDisplayName,
            })),
            columns: [], // 컬럼 정보는 노드에서 가져옴
          };
        }
      }
    }
    return undefined;
  }, [subTablesDataMap]);

  // 노드 우클릭 핸들러 (설정 모달 열기)
  const handleNodeContextMenu = useCallback((event: React.MouseEvent, node: Node) => {
    // 기본 컨텍스트 메뉴 방지
    event.preventDefault();
    
    // 화면 노드 우클릭
    if (node.id.startsWith("screen-")) {
      const screenId = parseInt(node.id.replace("screen-", ""));
      const nodeData = node.data as unknown as ScreenNodeData;
      const mainTable = screenTableMap[screenId];
      
      // 해당 화면의 서브 테이블 (필터 테이블) 정보
      // 1. screenSubTableMap에서 가져오기
      const screenSubTables = screenSubTableMap[screenId] || [];
      
      // 2. edges에서 필터 테이블 찾기 (edge-screen-filter-{screenId}-{tableName})
      const filterTableNamesFromEdges = edges
        .filter(e => e.id.startsWith(`edge-screen-filter-${screenId}-`))
        .map(e => {
          const match = e.id.match(/edge-screen-filter-\d+-(.+)/);
          return match ? match[1] : null;
        })
        .filter((name): name is string => name !== null);
      
      // 모든 필터 테이블 합치기 (중복 제거)
      const allFilterTableNames = [...new Set([...screenSubTables, ...filterTableNamesFromEdges])];
      
      const filterTables = allFilterTableNames.map(tableName => {
        // subTablesDataMap에서 해당 테이블 정보 찾기
        const subTableData = subTablesDataMap[screenId]?.subTables?.find(
          st => st.tableName === tableName
        );
        
        // 또는 nodes에서 테이블 노드 정보 찾기
        const tableNode = nodes.find(n => 
          n.id === `table-${tableName}` || n.id === `subtable-${tableName}`
        );
        const tableNodeData = tableNode?.data as TableNodeData | undefined;
        
        // 필터 키 매핑 정보 추출 (leftColumn → foreignKey)
        let filterKeyMapping: {
          mainTableColumn: string;
          mainTableColumnLabel?: string;
          filterTableColumn: string;
          filterTableColumnLabel?: string;
        } | undefined = undefined;
        
        if (subTableData?.leftColumn && subTableData?.foreignKey) {
          // 메인 테이블 컬럼 한글명 조회
          const mainTable = subTablesDataMap[screenId]?.mainTable;
          const mainTableCols = mainTable ? tableColumns[mainTable] : [];
          const mainColInfo = mainTableCols?.find(c => c.columnName === subTableData.leftColumn);
          
          // 필터 테이블 컬럼 한글명 조회
          const filterTableCols = tableColumns[tableName] || [];
          const filterColInfo = filterTableCols?.find(c => c.columnName === subTableData.foreignKey);
          
          filterKeyMapping = {
            mainTableColumn: subTableData.leftColumn,
            mainTableColumnLabel: mainColInfo?.displayName,
            filterTableColumn: subTableData.foreignKey,
            filterTableColumnLabel: filterColInfo?.displayName,
          };
        }
        
        return {
          tableName,
          tableLabel: subTableData?.tableLabel || tableNodeData?.label || tableName,
          filterColumns: subTableData?.filterColumns || tableNodeData?.filterColumns || [],
          filterKeyMapping,
          joinColumnRefs: subTableData?.joinColumnRefs || tableNodeData?.joinColumnRefs || [],
        };
      });
      
      // 화면의 모든 서브 테이블에서 fieldMappings 추출
      const screenSubTablesData = subTablesDataMap[screenId];
      const allFieldMappings: Array<{
        targetField: string;
        sourceField: string;
        sourceTable?: string;
        sourceDisplayName?: string;
        componentType?: string;
      }> = [];
      
      if (screenSubTablesData?.subTables) {
        screenSubTablesData.subTables.forEach((subTable) => {
          if (subTable.fieldMappings) {
            subTable.fieldMappings.forEach((mapping) => {
              allFieldMappings.push({
                targetField: mapping.targetField,
                sourceField: mapping.sourceField,
                sourceTable: mapping.sourceTable || subTable.tableName,
                sourceDisplayName: mapping.sourceDisplayName,
                componentType: subTable.relationType,
              });
            });
          }
        });
      }
      
      setSettingModalNode({
        nodeType: "screen",
        nodeId: node.id,
        screenId: screenId,
        screenName: nodeData.label || `화면 ${screenId}`,
        tableName: mainTable,
        tableLabel: nodeData.subLabel,
        companyCode: selectedGroup?.company_code, // 프리뷰용 회사 코드
        // 화면의 테이블 정보 전달
        existingConfig: {
          mainTable: mainTable,
          filterTables: filterTables,
          fieldMappings: allFieldMappings,
        },
      });
      setIsSettingModalOpen(true);
      return;
    }
    
    // 메인 테이블 노드 더블클릭
    if (node.id.startsWith("table-") && !node.id.startsWith("table-sub-")) {
      const tableName = node.id.replace("table-", "");
      const nodeData = node.data as unknown as TableNodeData;
      
      // 이 테이블을 사용하는 화면 찾기
      const screenId = Object.entries(screenTableMap).find(
        ([_, tbl]) => tbl === tableName
      )?.[0];
      
      // 백엔드에서 받은 데이터에서 기존 설정 정보 추출
      const existingConfigFromData = getTableExistingConfig(tableName);
      
      setSettingModalNode({
        nodeType: "table",
        nodeId: node.id,
        screenId: screenId ? parseInt(screenId) : 0,
        screenName: nodeData.subLabel || tableName,
        tableName: tableName,
        tableLabel: nodeData.label,
        // 기존 설정 정보 전달
        existingConfig: existingConfigFromData || {
          joinColumnRefs: nodeData.joinColumnRefs,
          filterColumns: nodeData.filterColumns,
          fieldMappings: nodeData.fieldMappings?.map(m => ({
            targetField: m.targetField,
            sourceField: m.sourceField,
            sourceTable: m.sourceTable,
            sourceDisplayName: m.sourceDisplayName,
          })),
          referencedBy: nodeData.referencedBy?.map(r => ({
            fromTable: r.fromTable,
            fromTableLabel: r.fromTableLabel,
            fromColumn: r.fromColumn,
            toColumn: r.toColumn,
            toColumnLabel: r.toColumnLabel,
            relationType: r.relationType,
          })),
          columns: nodeData.columns,
        },
      });
      setIsSettingModalOpen(true);
      return;
    }
    
    // 서브 테이블 노드 더블클릭
    if (node.id.startsWith("subtable-")) {
      const tableName = node.id.replace("subtable-", "");
      const nodeData = node.data as unknown as TableNodeData;
      
      // 이 서브 테이블을 사용하는 화면 찾기
      const screenId = Object.entries(screenSubTableMap).find(
        ([_, tables]) => tables.includes(tableName)
      )?.[0];
      
      // 백엔드에서 받은 데이터에서 기존 설정 정보 추출
      const existingConfigFromData = getTableExistingConfig(tableName);
      
      setSettingModalNode({
        nodeType: "table",
        nodeId: node.id,
        screenId: screenId ? parseInt(screenId) : 0,
        screenName: nodeData.subLabel || tableName,
        tableName: tableName,
        tableLabel: nodeData.label,
        // 기존 설정 정보 전달
        existingConfig: existingConfigFromData || {
          joinColumnRefs: nodeData.joinColumnRefs,
          filterColumns: nodeData.filterColumns,
          fieldMappings: nodeData.fieldMappings?.map(m => ({
            targetField: m.targetField,
            sourceField: m.sourceField,
            sourceTable: m.sourceTable,
            sourceDisplayName: m.sourceDisplayName,
          })),
          referencedBy: nodeData.referencedBy?.map(r => ({
            fromTable: r.fromTable,
            fromTableLabel: r.fromTableLabel,
            fromColumn: r.fromColumn,
            toColumn: r.toColumn,
            toColumnLabel: r.toColumnLabel,
            relationType: r.relationType,
          })),
          columns: nodeData.columns,
        },
      });
      setIsSettingModalOpen(true);
      return;
    }
  }, [screenTableMap, screenSubTableMap, subTablesDataMap, edges, nodes, getTableExistingConfig]);

  // 설정 모달 닫기 및 새로고침
  const handleSettingModalClose = useCallback(() => {
    setIsSettingModalOpen(false);
    setSettingModalNode(null);
  }, []);

  // 시각화 새로고침 (설정 저장 후 호출)
  const handleRefreshVisualization = useCallback(() => {
    // 강제 새로고침: refreshKey 증가로 useEffect 재실행
    setRefreshKey(prev => prev + 1);
  }, []);
  
  // 포커스에 따른 노드 스타일링 (그룹 모드에서 화면 클릭 시)
  const styledNodes = React.useMemo(() => {
    // 그룹 모드가 아니면 원본 반환
    if (!selectedGroup) return nodes;
    
    // 포커스된 화면의 서브 테이블 목록 (포커스가 없으면 빈 배열)
    const focusedSubTables = focusedScreenId !== null 
      ? (screenSubTableMap[focusedScreenId] || []) 
      : [];
    
    // 포커스된 화면의 서브 테이블 데이터 (조인 컬럼 정보 포함)
    const focusedSubTablesData = focusedScreenId !== null 
      ? subTablesDataMap[focusedScreenId] 
      : null;
    
    // 포커스된 화면의 사용 컬럼 정보
    const focusedUsedColumns = focusedScreenId !== null
      ? screenUsedColumnsMap[focusedScreenId]
      : null;
    
    // 연관 테이블 정보 추출 (parentDataMapping, rightPanelRelation 등으로 연결된 테이블)
    // { tableName: { columns: [sourceField1, sourceField2], displayNames: [displayName1, displayName2] } }
    const relatedTablesMap: Record<string, { columns: string[], displayNames: string[] }> = {};
    if (focusedSubTablesData) {
      focusedSubTablesData.subTables.forEach((subTable) => {
        // parentDataMapping, rightPanelRelation 타입의 연결에서 sourceTable 추출
        if ((subTable.relationType === 'parentMapping' || subTable.relationType === 'rightPanelRelation') 
            && subTable.fieldMappings) {
          subTable.fieldMappings.forEach((mapping) => {
            // sourceTable이 명시되어 있으면 그 테이블에 sourceField 추가
            // sourceTable이 없으면 subTable.tableName을 sourceTable로 간주하지 않음
            // mapping에서 sourceTable 정보가 필요함 - 현재는 tableName에서 추론
            // parentDataMapping의 경우: tableName이 targetTable이고, sourceTable은 별도 정보 필요
            
            // 임시: subTables의 tableName과 다른 메인 테이블들을 확인
            // fieldMappings에서 sourceField가 다른 테이블에 있다면 그 테이블이 연관 테이블
          });
        }
      });
    }
    
    // 모든 화면의 메인 테이블과 그에 연결된 조인 정보 매핑
    // 포커스된 화면이 다른 메인 테이블을 참조하는 경우 해당 테이블도 강조
    const relatedMainTables: Record<string, { columns: string[], displayNames: string[] }> = {};
    
    // 모든 화면의 메인 테이블 Set (빠른 조회용)
    const allMainTableSet = new Set(Object.values(screenTableMap));
    
    if (focusedSubTablesData) {
      // 포커스된 화면의 subTables 순회
      focusedSubTablesData.subTables.forEach((subTable) => {
        // 1. subTable.tableName 자체가 다른 화면의 메인 테이블인 경우
        if (allMainTableSet.has(subTable.tableName) && subTable.tableName !== focusedSubTablesData.mainTable) {
          if (!relatedMainTables[subTable.tableName]) {
            relatedMainTables[subTable.tableName] = { columns: [], displayNames: [] };
          }
          
          // fieldMappings가 있으면 조인 컬럼 정보 추출
          if (subTable.fieldMappings) {
            subTable.fieldMappings.forEach((mapping: any) => {
              // reference, source 타입: targetField가 서브(연관) 테이블 컬럼
              // parentMapping 등: sourceField가 연관 테이블 컬럼
              const relatedColumn = mapping.sourceTable 
                ? mapping.sourceField  // parentMapping 스타일
                : mapping.targetField; // reference/source 스타일
              const displayName = mapping.sourceTable
                ? (mapping.sourceDisplayName || mapping.sourceField)
                : (mapping.targetDisplayName || mapping.targetField);
              
              if (relatedColumn && !relatedMainTables[subTable.tableName].columns.includes(relatedColumn)) {
                relatedMainTables[subTable.tableName].columns.push(relatedColumn);
                relatedMainTables[subTable.tableName].displayNames.push(displayName);
              }
            });
          }
        }
        
        // 2. fieldMappings.sourceTable이 다른 화면의 메인 테이블인 경우
        if (subTable.fieldMappings) {
          subTable.fieldMappings.forEach((mapping: any) => {
            if (mapping.sourceTable && allMainTableSet.has(mapping.sourceTable) && mapping.sourceTable !== focusedSubTablesData.mainTable) {
              if (!relatedMainTables[mapping.sourceTable]) {
                relatedMainTables[mapping.sourceTable] = { columns: [], displayNames: [] };
              }
              if (mapping.sourceField && !relatedMainTables[mapping.sourceTable].columns.includes(mapping.sourceField)) {
                relatedMainTables[mapping.sourceTable].columns.push(mapping.sourceField);
                relatedMainTables[mapping.sourceTable].displayNames.push(mapping.sourceDisplayName || mapping.sourceField);
              }
            }
          });
        }
        
        // 3. 필터 대상 테이블의 joinColumnRefs가 있으면 해당 참조 테이블도 활성화
        // 예: customer_item_mapping → item_info (품목 ID → item_info.item_number)
        if (subTable.relationType === 'rightPanelRelation' && subTable.joinColumnRefs) {
          subTable.joinColumnRefs.forEach((joinRef) => {
            const refTable = joinRef.refTable;
            if (refTable && allMainTableSet.has(refTable) && refTable !== focusedSubTablesData.mainTable) {
              if (!relatedMainTables[refTable]) {
                relatedMainTables[refTable] = { columns: [], displayNames: [] };
              }
              // 참조 테이블의 컬럼도 추가 (조인 관계 표시용)
              if (joinRef.refColumn && !relatedMainTables[refTable].columns.includes(joinRef.refColumn)) {
                relatedMainTables[refTable].columns.push(joinRef.refColumn);
                relatedMainTables[refTable].displayNames.push(joinRef.columnLabel || joinRef.refColumn);
              }
            }
          });
        }
      });
    }
    
    // lookup 필터 OFF일 때: lookup 연결만 있는 테이블 노드를 dim 처리
    const lookupOnlyNodes = new Set<string>();
    if (!edgeFilterState.lookup) {
      const nodeEdgeCategories = new Map<string, Set<EdgeCategory>>();
      edges.forEach((edge) => {
        const category = (edge.data as any)?.edgeCategory as EdgeCategory | undefined;
        if (!category) return;
        [edge.source, edge.target].forEach((nodeId) => {
          if (!nodeEdgeCategories.has(nodeId)) {
            nodeEdgeCategories.set(nodeId, new Set());
          }
          nodeEdgeCategories.get(nodeId)!.add(category);
        });
      });
      nodeEdgeCategories.forEach((categories, nodeId) => {
        if (nodeId.startsWith("table-") || nodeId.startsWith("subtable-")) {
          const hasVisibleCategory = Array.from(categories).some(
            (cat) => cat !== "lookup" && edgeFilterState[cat]
          );
          if (!hasVisibleCategory) {
            lookupOnlyNodes.add(nodeId);
          }
        }
      });
    }
    
    return nodes.map((node) => {
      // 화면 노드 스타일링 (포커스가 있을 때만)
      if (node.id.startsWith("screen-")) {
        if (focusedScreenId === null) {
          // 포커스 없음: 모든 화면 정상 표시
          return {
            ...node,
            data: {
              ...node.data,
              isFocused: false,
              isFaded: false,
            },
          };
        }
        
        const screenId = parseInt(node.id.replace("screen-", ""));
        const isFocused = screenId === focusedScreenId;
        const isFaded = !isFocused;
        
        return {
          ...node,
          data: {
            ...node.data,
            isFocused,
            isFaded,
          },
        };
      }
      
      // 메인 테이블 노드 스타일링
      if (node.id.startsWith("table-")) {
        const tableName = node.id.replace("table-", "");
        
        // 포커스된 화면의 메인 테이블인지 확인
        const isFocusedTable = focusedScreenId !== null && screenTableMap[focusedScreenId] === tableName;
        
        // 연관 테이블인지 확인 (다른 화면의 메인 테이블이지만 포커스된 화면에서 참조하는 테이블)
        const isRelatedTable = relatedMainTables[tableName] !== undefined;
        const relatedTableInfo = relatedMainTables[tableName];
        
        // 조인 컬럼 추출
        // 1. columns_config에서 isEntityJoin=true인 컬럼 (__join 키)
        // 2. 서브 테이블 연결 시 fieldMappings에서 메인테이블 컬럼 추출
        let joinColumns: string[] = [...(focusedUsedColumns?.[`${tableName}__join`] || [])];
        
        // 서브 테이블 연결 정보에서도 추가 (포커스된 화면의 메인 테이블인 경우)
        if (focusedSubTablesData && focusedSubTablesData.mainTable === tableName) {
          focusedSubTablesData.subTables.forEach((subTable) => {
            if (subTable.fieldMappings) {
              subTable.fieldMappings.forEach((mapping) => {
                const hasSourceTable = 'sourceTable' in mapping && mapping.sourceTable;
                
                if (hasSourceTable) {
                  if (mapping.targetField && !joinColumns.includes(mapping.targetField)) {
                    joinColumns.push(mapping.targetField);
                  }
                } else if (subTable.relationType === 'reference' || subTable.relationType === 'source') {
                  if (mapping.sourceField && !joinColumns.includes(mapping.sourceField)) {
                    joinColumns.push(mapping.sourceField);
                  }
                } else if (subTable.relationType === 'parentMapping' || subTable.relationType === 'rightPanelRelation') {
                  if (mapping.targetField && !joinColumns.includes(mapping.targetField)) {
                    joinColumns.push(mapping.targetField);
                  }
                } else {
                  if (mapping.targetField && !joinColumns.includes(mapping.targetField)) {
                    joinColumns.push(mapping.targetField);
                  }
                }
              });
            }
          });
        }
        
        // 연관 테이블인 경우: 참조되는 컬럼을 조인 컬럼으로 추가
        if (isRelatedTable && relatedTableInfo) {
          relatedTableInfo.columns.forEach((col) => {
            if (!joinColumns.includes(col)) {
              joinColumns.push(col);
            }
          });
        }
        
        // 사용 컬럼 추출 (조인 컬럼 제외)
        const allUsedColumns = focusedUsedColumns?.[tableName] || [];
        const highlightedColumns = allUsedColumns.filter(col => !joinColumns.includes(col));
        
        // 테이블 활성화 여부: 포커스된 화면의 메인 테이블 OR 연관 테이블
        const isActiveTable = isFocusedTable || isRelatedTable;
        
        // 메인테이블용 fieldMappings 생성 (조인 컬럼 옆에 연관 테이블 컬럼명 표시)
        // 예: 거래처 ID ← 거래처 코드 (메인테이블 컬럼 ← 연관테이블 컬럼)
        let mainTableFieldMappings: Array<{ sourceField: string; targetField: string; sourceDisplayName?: string; targetDisplayName?: string }> = [];
        if (isFocusedTable && focusedSubTablesData) {
          focusedSubTablesData.subTables.forEach((subTable) => {
            if (subTable.fieldMappings) {
              subTable.fieldMappings.forEach((mapping) => {
                // 메인테이블에서는:
                // - sourceField/targetField 중 메인테이블 컬럼이 targetField (표시할 컬럼)
                // - 연관테이블 컬럼이 sourceField (← 뒤에 표시할 참조 컬럼)
                if (subTable.relationType === 'source' && mapping.sourceTable) {
                  // parentDataMapping 스타일: targetField = 메인테이블, sourceField = 연관테이블
                  mainTableFieldMappings.push({
                    sourceField: mapping.sourceField,           // 연관 테이블 컬럼
                    targetField: mapping.targetField,           // 메인 테이블 컬럼
                    sourceDisplayName: mapping.sourceDisplayName || mapping.sourceField,
                    targetDisplayName: mapping.targetDisplayName || mapping.targetField,
                  });
                } else if (subTable.relationType === 'parentMapping' || subTable.relationType === 'rightPanelRelation') {
                  mainTableFieldMappings.push({
                    sourceField: mapping.sourceField,
                    targetField: mapping.targetField,
                    sourceDisplayName: mapping.sourceDisplayName || mapping.sourceField,
                    targetDisplayName: mapping.targetDisplayName || mapping.targetField,
                  });
                } else if (subTable.relationType === 'reference' || subTable.relationType === 'source') {
                  // reference/source: sourceField = 메인테이블, targetField = 서브테이블
                  // 메인테이블 표시: sourceField ← targetDisplayName
                  mainTableFieldMappings.push({
                    sourceField: mapping.targetField,           // 연관 테이블 컬럼 (표시용)
                    targetField: mapping.sourceField,           // 메인 테이블 컬럼
                    sourceDisplayName: mapping.targetDisplayName || mapping.targetField,
                    targetDisplayName: mapping.sourceDisplayName || mapping.sourceField,
                  });
                } else {
                  // lookup: targetField = 메인테이블, sourceField = 서브테이블
                  mainTableFieldMappings.push({
                    sourceField: mapping.sourceField,
                    targetField: mapping.targetField,
                    sourceDisplayName: mapping.sourceDisplayName || mapping.sourceField,
                    targetDisplayName: mapping.targetDisplayName || mapping.targetField,
                  });
                }
              });
            }
          });
        }
        
        // 연관 테이블용 fieldMappings 생성
        let relatedTableFieldMappings: Array<{ sourceField: string; targetField: string; sourceDisplayName?: string; targetDisplayName?: string }> = [];
        if (isRelatedTable && focusedSubTablesData) {
          focusedSubTablesData.subTables.forEach((subTable) => {
            // 1. subTable.tableName === tableName인 경우 (메인-메인 조인)
            if (subTable.tableName === tableName && subTable.fieldMappings) {
              subTable.fieldMappings.forEach((mapping) => {
                // reference/source 타입: sourceField가 메인테이블, targetField가 이 연관테이블
                // 연관 테이블 표시: targetField ← sourceDisplayName (메인테이블 컬럼 한글명)
                if (subTable.relationType === 'reference' || subTable.relationType === 'source') {
                  relatedTableFieldMappings.push({
                    sourceField: mapping.sourceField,           // 메인 테이블 컬럼 (참조 표시용)
                    targetField: mapping.targetField,           // 연관 테이블 컬럼 (표시할 컬럼)
                    sourceDisplayName: mapping.sourceDisplayName || mapping.sourceField,
                    targetDisplayName: mapping.targetDisplayName || mapping.targetField,
                  });
                } else {
                  // lookup, parentMapping 등: targetField가 메인테이블, sourceField가 연관테이블
                  relatedTableFieldMappings.push({
                    sourceField: mapping.targetField,           // 메인 테이블 컬럼 (참조 표시용)
                    targetField: mapping.sourceField,           // 연관 테이블 컬럼 (표시할 컬럼)
                    sourceDisplayName: mapping.targetDisplayName || mapping.targetField,
                    targetDisplayName: mapping.sourceDisplayName || mapping.sourceField,
                  });
                }
              });
            }
            
            // 2. mapping.sourceTable === tableName인 경우 (parentDataMapping 등)
            if (subTable.fieldMappings) {
              subTable.fieldMappings.forEach((mapping) => {
                if (mapping.sourceTable === tableName) {
                  // 이 테이블이 sourceTable인 경우: sourceField가 이 테이블의 컬럼
                  relatedTableFieldMappings.push({
                    sourceField: mapping.targetField,           // 메인 테이블 컬럼 (참조 표시용)
                    targetField: mapping.sourceField,           // 연관 테이블 컬럼 (표시할 컬럼)
                    sourceDisplayName: mapping.targetDisplayName || mapping.targetField,
                    targetDisplayName: mapping.sourceDisplayName || mapping.sourceField,
                  });
                }
              });
            }
          });
          
          // 중복 제거
          const seen = new Set<string>();
          relatedTableFieldMappings = relatedTableFieldMappings.filter(fm => {
            const key = `${fm.sourceField}-${fm.targetField}`;
            if (seen.has(key)) return false;
            seen.add(key);
            return true;
          });
        }
        
        // 포커스된 화면에서 이 테이블이 필터링 대상인지 확인
        // (rightPanelRelation의 서브테이블인 경우)
        let focusedFilterColumns: string[] = [];
        let focusedReferencedBy: ReferenceInfo[] = [];
        
        // 조인 컬럼 참조 정보 수집
        let focusedJoinColumnRefs: Array<{ column: string; refTable: string; refColumn: string }> = [];
        
        // 포커싱된 화면 기준 저장 정보
        let focusedSaveInfos: Array<{ saveType: string; componentType: string; isMainTable: boolean; sourceScreenId?: number }> = [];
        
        if (focusedScreenId !== null && focusedSubTablesData) {
          // 포커스된 화면에서 이 테이블이 rightPanelRelation의 서브테이블인 경우
          focusedSubTablesData.subTables.forEach((subTable) => {
            if (subTable.tableName === tableName && subTable.relationType === 'rightPanelRelation') {
              // FK 컬럼 추출 (필터링 기준)
              if (subTable.foreignKey) {
                focusedFilterColumns.push(subTable.foreignKey);
              }
              // 조인 컬럼 추가 (rightPanel.columns에서 외부 테이블 참조하는 FK)
              // 예: customer_mng.customer_name 표시를 위해 customer_id 사용
              // 조인 컬럼은 주황색으로 표시되어야 하므로 joinColumns에 추가
              if (subTable.joinColumns && Array.isArray(subTable.joinColumns)) {
                subTable.joinColumns.forEach((col) => {
                  if (!joinColumns.includes(col)) {
                    joinColumns.push(col);
                  }
                });
              }
              // 조인 컬럼 참조 정보 추가
              if (subTable.joinColumnRefs && Array.isArray(subTable.joinColumnRefs)) {
                focusedJoinColumnRefs = [...focusedJoinColumnRefs, ...subTable.joinColumnRefs];
              }
              // 참조 정보 생성 (한글명 포함) - 서브 테이블용
              const mainTableCols = tableColumns[focusedSubTablesData.mainTable] || [];
              const fromColInfo = mainTableCols.find(c => c.columnName === (subTable.leftColumn || 'id'));
              const fromColumnLabel = fromColInfo?.displayName || subTable.leftColumn || 'id';
              
              const subTableCols = tableColumns[subTable.tableName] || [];
              const toColInfo = subTableCols.find(c => c.columnName === subTable.foreignKey);
              const toColumnLabel = toColInfo?.displayName || subTable.foreignKey || '';
              
              focusedReferencedBy.push({
                fromTable: focusedSubTablesData.mainTable,
                fromTableLabel: focusedSubTablesData.mainTable,
                fromColumn: subTable.leftColumn || 'id',
                fromColumnLabel: fromColumnLabel,
                toColumn: subTable.foreignKey || '',
                toColumnLabel: toColumnLabel,
                relationType: 'filter',
              });
            }
          });
          
          // 메인 테이블인 경우: 이 테이블이 필터 소스로 사용되는 정보 추가
          if (isFocusedTable) {
            focusedSubTablesData.subTables.forEach((subTable) => {
              if (subTable.relationType === 'rightPanelRelation' && subTable.leftColumn) {
                // 메인 테이블의 컬럼 한글명 조회
                const mainTableCols = tableColumns[focusedSubTablesData.mainTable] || [];
                const fromColInfo = mainTableCols.find(c => c.columnName === subTable.leftColumn);
                const fromColumnLabel = fromColInfo?.displayName || subTable.leftColumn || 'id';
                
                // 서브 테이블의 컬럼 한글명 조회
                const subTableCols = tableColumns[subTable.tableName] || [];
                const toColInfo = subTableCols.find(c => c.columnName === subTable.foreignKey);
                const toColumnLabel = toColInfo?.displayName || subTable.foreignKey || '';
                
                // 메인 테이블 입장: "내 컬럼이 서브 테이블의 필터 소스로 사용됨"
                focusedReferencedBy.push({
                  fromTable: focusedSubTablesData.mainTable,
                  fromTableLabel: focusedSubTablesData.mainTable,
                  fromColumn: subTable.leftColumn,
                  fromColumnLabel: fromColumnLabel,
                  toColumn: subTable.foreignKey || '',
                  toColumnLabel: toColumnLabel,
                  relationType: 'filter',
                });
              }
            });
          }
          
          // 포커싱된 화면 기준 저장 정보 추출
          if (focusedSubTablesData.saveTables) {
            focusedSubTablesData.saveTables.forEach((st) => {
              if (st.tableName === tableName) {
                focusedSaveInfos.push({
                  saveType: st.saveType,
                  componentType: st.componentType,
                  isMainTable: st.isMainTable,
                  sourceScreenId: focusedSubTablesData.screenId,
                });
              }
            });
          }
        }
        
        return {
          ...node,
          data: {
            ...node.data,
            isFocused: isFocusedTable,
            isRelated: isRelatedTable,
            isFaded: (focusedScreenId !== null && !isActiveTable) || lookupOnlyNodes.has(node.id),
            highlightedColumns: isActiveTable ? highlightedColumns : [],
            joinColumns: isActiveTable ? joinColumns : [],
            joinColumnRefs: focusedJoinColumnRefs.length > 0 ? focusedJoinColumnRefs : undefined,  // 조인 컬럼 참조 정보
            filterColumns: focusedFilterColumns,  // 포커스 상태에서만 표시
            referencedBy: focusedReferencedBy.length > 0 ? focusedReferencedBy : undefined,  // 포커스 상태에서만 표시
            saveInfos: focusedSaveInfos.length > 0 ? focusedSaveInfos : undefined,  // 포커스 상태에서만 표시
            fieldMappings: isFocusedTable ? mainTableFieldMappings : (isRelatedTable ? relatedTableFieldMappings : []),
          },
        };
      }
      
      // 서브 테이블 노드 스타일링 
      // 기본: 흐리게, 포커스된 화면의 서브 테이블만 활성화
      if (node.id.startsWith("subtable-")) {
        const subTableName = node.id.replace("subtable-", "");
        const isActiveSubTable = focusedSubTables.includes(subTableName);
        
        // 조인 컬럼 추출 (서브 테이블 측의 컬럼)
        // relationType에 따라 다름:
        // - reference: targetField가 서브테이블 컬럼 (예: manager_id -> user_id)
        // - lookup 등: sourceField가 서브테이블 컬럼
        let subTableJoinColumns: string[] = [];
        if (isActiveSubTable && focusedSubTablesData) {
          const subTableInfo = focusedSubTablesData.subTables.find(st => st.tableName === subTableName);
          if (subTableInfo?.fieldMappings) {
            subTableInfo.fieldMappings.forEach((mapping) => {
              // reference, source, parentMapping, rightPanelRelation 타입: targetField가 서브테이블의 컬럼 (조인 키)
              // lookup 타입: sourceField가 서브테이블의 컬럼
              if (subTableInfo.relationType === 'reference' || subTableInfo.relationType === 'source' ||
                  subTableInfo.relationType === 'parentMapping' || subTableInfo.relationType === 'rightPanelRelation') {
                // reference, source, parentMapping, rightPanelRelation: 메인테이블 컬럼(sourceField) -> 서브테이블 컬럼(targetField)
                if (mapping.targetField && !subTableJoinColumns.includes(mapping.targetField)) {
                  subTableJoinColumns.push(mapping.targetField);
                }
              } else {
                // lookup 등: sourceField가 서브테이블 컬럼
                if (mapping.sourceField && !subTableJoinColumns.includes(mapping.sourceField)) {
                  subTableJoinColumns.push(mapping.sourceField);
                }
              }
            });
          }
        }
        
        // 서브 테이블의 highlightedColumns도 추가 (화면에서 서브테이블 컬럼을 직접 사용하는 경우)
        // joinColumns와 별개로 표시 (조인 키 외 사용 컬럼)
        const subTableHighlightedColumns: string[] = [];
        
        // 필드 매핑 정보 추출 (조인 관계 표시용)
        // reference, source, parentMapping, rightPanelRelation 타입: sourceField = 메인테이블 컬럼, targetField = 서브테이블 컬럼
        // lookup 타입: sourceField = 서브테이블 컬럼, targetField = 메인테이블 컬럼 (swap 필요)
        let displayFieldMappings: Array<{ sourceField: string; targetField: string; sourceDisplayName?: string; targetDisplayName?: string }> = [];
        
        // 포커싱된 화면 기준 저장 정보 (서브 테이블)
        let subTableSaveInfos: Array<{ saveType: string; componentType: string; isMainTable: boolean; sourceScreenId?: number }> = [];
        
        if (isActiveSubTable && focusedSubTablesData) {
          const subTableInfo = focusedSubTablesData.subTables.find(st => st.tableName === subTableName);
          if (subTableInfo?.fieldMappings) {
            displayFieldMappings = subTableInfo.fieldMappings.map((mapping) => {
              if (subTableInfo.relationType === 'reference' || subTableInfo.relationType === 'source' ||
                  subTableInfo.relationType === 'parentMapping' || subTableInfo.relationType === 'rightPanelRelation') {
                // reference, source, parentMapping, rightPanelRelation: 백엔드에서 sourceField = 메인테이블, targetField = 서브테이블
                // 표시: 서브테이블 컬럼(targetField) ← 메인테이블 한글명(sourceDisplayName)
                return {
                  sourceField: mapping.sourceField,          // 메인 테이블 컬럼 (참조)
                  targetField: mapping.targetField,          // 서브 테이블 컬럼 (표시)
                  sourceDisplayName: mapping.sourceDisplayName || mapping.sourceField, // 메인 테이블 한글명
                  targetDisplayName: mapping.targetDisplayName || mapping.targetField,
                };
              } else {
                // lookup 등: 백엔드 fieldMappings가 reference/source와 반대
                // 백엔드: sourceField = 서브테이블 컬럼, targetField = 메인테이블 컬럼
                // 프론트엔드 표시: 서브테이블 컬럼(sourceField) ← 메인테이블 컬럼(targetField)
                // 그래서 swap 필요!
                return {
                  sourceField: mapping.targetField,          // 메인 테이블 컬럼 (참조)
                  targetField: mapping.sourceField,          // 서브 테이블 컬럼 (표시)
                  sourceDisplayName: mapping.targetDisplayName || mapping.targetField, // 메인 테이블 한글명
                  targetDisplayName: mapping.sourceDisplayName || mapping.sourceField, // 서브 테이블 한글명
                };
              }
            });
          }
          
          // 서브 테이블에 대한 저장 정보 추출
          if (focusedSubTablesData.saveTables) {
            focusedSubTablesData.saveTables.forEach((st) => {
              if (st.tableName === subTableName) {
                subTableSaveInfos.push({
                  saveType: st.saveType,
                  componentType: st.componentType,
                  isMainTable: st.isMainTable,
                  sourceScreenId: focusedSubTablesData.screenId,
                });
              }
            });
          }
        }
        
        return {
          ...node,
          style: {
            ...node.style,
            opacity: isActiveSubTable ? 1 : 0.3,
            filter: isActiveSubTable ? "none" : "grayscale(80%)",
          },
          data: {
            ...node.data,
            isFocused: isActiveSubTable,
            isFaded: !isActiveSubTable || lookupOnlyNodes.has(node.id),
            highlightedColumns: isActiveSubTable ? subTableHighlightedColumns : [],
            joinColumns: isActiveSubTable ? subTableJoinColumns : [],
            fieldMappings: isActiveSubTable ? displayFieldMappings : [],
            saveInfos: subTableSaveInfos.length > 0 ? subTableSaveInfos : undefined,  // 포커스 상태에서만 표시
          },
        };
      }
      
      return node;
    });
  }, [nodes, selectedGroup, focusedScreenId, screenSubTableMap, subTablesDataMap, screenUsedColumnsMap, screenTableMap, tableColumns, edgeFilterState, edges]);
  
  // 포커스에 따른 엣지 스타일링 (그룹 모드 & 개별 화면 모드)
  const styledEdges = React.useMemo(() => {
    // 개별 화면 모드: 메인 화면의 연결선만 강조
    if (!selectedGroup && screen) {
      const mainScreenId = screen.screenId;
      
      return edges.map((edge) => {
        // 화면 간 연결선
        if (edge.source.startsWith("screen-") && edge.target.startsWith("screen-")) {
          const sourceId = parseInt(edge.source.replace("screen-", ""));
          const targetId = parseInt(edge.target.replace("screen-", ""));
          const isConnected = sourceId === mainScreenId || targetId === mainScreenId;
          
          return {
            ...edge,
            animated: isConnected,
            style: {
              ...edge.style,
              stroke: isConnected ? "hsl(var(--primary))" : "hsl(var(--border))",
              strokeWidth: isConnected ? 2.5 : 1,
              opacity: isConnected ? 1 : 0.2,
            },
          };
        }
        
        // 화면-테이블 연결선
        if (edge.source.startsWith("screen-") && edge.target.startsWith("table-")) {
          const sourceId = parseInt(edge.source.replace("screen-", ""));
          const isMyConnection = sourceId === mainScreenId;
          
          return {
            ...edge,
            animated: isMyConnection,
            style: {
              ...edge.style,
              stroke: isMyConnection ? "hsl(var(--primary))" : "hsl(var(--border))",
              strokeWidth: isMyConnection ? 2.5 : 1,
              strokeDasharray: isMyConnection ? undefined : "5,5",
              opacity: isMyConnection ? 1 : 0.2,
            },
          };
        }
        
        return edge;
      });
    }
    
    // 그룹 모드가 아니면 원본 반환
    if (!selectedGroup) return edges;
    
    // 연관 테이블 간 조인 엣지 생성 (메인 테이블 간 조인 관계)
    const joinEdges: Edge[] = [];
    
    if (focusedScreenId !== null) {
      const focusedSubTablesData = subTablesDataMap[focusedScreenId];
      const focusedMainTable = screenTableMap[focusedScreenId];
      
      // 모든 화면의 메인 테이블 목록 (메인-메인 조인 판단용)
      const allMainTables = new Set(Object.values(screenTableMap));
      
      // 이미 추가된 테이블 쌍 추적 (중복 방지)
      const addedPairs = new Set<string>();
      
      if (focusedSubTablesData) {
        focusedSubTablesData.subTables.forEach((subTable) => {
          // 1. subTable.tableName이 다른 화면의 메인 테이블인 경우 (메인-메인 조인)
          const isTargetMainTable = allMainTables.has(subTable.tableName) && subTable.tableName !== focusedMainTable;
          
          if (isTargetMainTable) {
            const pairKey = `${subTable.tableName}-${focusedMainTable}`;
            if (addedPairs.has(pairKey)) return;
            addedPairs.add(pairKey);
            
            // 메인 테이블 간 조인 연결선 - edge-main-main 스타일 업데이트만 수행
            // 별도의 edge-main-join을 생성하지 않고, styledEdges에서 edge-main-main을 강조 처리
          }
          
          // 2. fieldMappings.sourceTable이 있는 경우 (parentMapping, rightPanelRelation 등)
          if (subTable.fieldMappings) {
            subTable.fieldMappings.forEach((mapping: any) => {
              const sourceTable = mapping.sourceTable;
              if (!sourceTable) return;
              
              // sourceTable이 메인 테이블인 경우만 메인-메인 조인선 추가
              if (!allMainTables.has(sourceTable) || sourceTable === focusedMainTable) return;
              
              const pairKey = `${sourceTable}-${focusedMainTable}`;
              if (addedPairs.has(pairKey)) return;
              addedPairs.add(pairKey);
              
              const edgeId = `edge-join-relation-${focusedScreenId}-${sourceTable}-${focusedMainTable}`;
              const sourceNodeId = `table-${sourceTable}`;
              const targetNodeId = `table-${focusedMainTable}`;
              
              // 이미 존재하는 엣지인지 확인
              if (joinEdges.some(e => e.id === edgeId)) return;
              
              // 관계 유형 추론 및 색상 결정
              const visualRelationType = inferVisualRelationType(subTable as SubTableInfo);
              
              // 방안 C: 필터 관계는 선 없이 뱃지로만 표시 (겹침 방지)
              if (visualRelationType === 'filter') {
                return; // 필터선 생성 건너뛰기
              }
              
              const relationColor = RELATION_COLORS[visualRelationType];
              
              joinEdges.push({
                id: edgeId,
                source: sourceNodeId,
                target: targetNodeId,
                sourceHandle: 'bottom',         // 고정: 서브테이블 구간 통과
                targetHandle: 'bottom_target',  // 고정: 서브테이블 구간 통과
                type: "animatedFlow",
                animated: true,
                style: {
                  stroke: relationColor.stroke,  // 관계 유형별 색상
                  strokeWidth: 2.5,
                  strokeDasharray: '8,4',
                },
                markerEnd: {
                  type: MarkerType.ArrowClosed,
                  color: relationColor.stroke,
                  width: 15,
                  height: 15,
                },
                data: {
                  visualRelationType,
                },
              });
            });
          }
        });
      }
    }
    
    // 기존 엣지 스타일링 + 조인 엣지 추가
    const styledOriginalEdges = edges.map((edge) => {
      // 화면 간 연결선 (1, 2, 3 라벨)
      if (edge.source.startsWith("screen-") && edge.target.startsWith("screen-")) {
        // 포커스가 없으면 모든 화면 간 연결선 정상 표시
        if (focusedScreenId === null) {
          return edge; // 원본 그대로
        }
        
        // 포커스된 화면과 연결된 화면 간 선만 활성화
        const sourceId = parseInt(edge.source.replace("screen-", ""));
        const targetId = parseInt(edge.target.replace("screen-", ""));
        const isConnected = sourceId === focusedScreenId || targetId === focusedScreenId;
        
        return {
          ...edge,
          animated: isConnected,
          style: {
            ...edge.style,
            stroke: isConnected ? "hsl(var(--primary))" : "hsl(var(--border))",
            strokeWidth: isConnected ? 2.5 : 1,
            opacity: isConnected ? 1 : 0.2,
          },
        };
      }
      
      // 화면-테이블 연결선
      if (edge.source.startsWith("screen-") && edge.target.startsWith("table-")) {
        const sourceId = parseInt(edge.source.replace("screen-", ""));
        const isMyConnection = sourceId === focusedScreenId;
        
        // 필터 연결선 (edge-screen-filter-)은 포커싱 시에만 표시
        const isFilterEdge = edge.id.startsWith("edge-screen-filter-");
        
        if (isFilterEdge) {
          // 포커스가 없거나 다른 화면 포커스 시 숨김
          if (focusedScreenId === null || !isMyConnection) {
            return {
              ...edge,
              animated: false,
              style: {
                ...edge.style,
                stroke: "transparent",
                strokeWidth: 0,
                opacity: 0,
              },
            };
          }
          
          // 포커싱된 화면의 필터 연결선은 표시
          return {
            ...edge,
            animated: true,
            style: {
              ...edge.style,
              stroke: "hsl(var(--primary))",
              strokeWidth: 2.5,
              strokeDasharray: "5,5",
              opacity: 1,
            },
          };
        }
        
        // 메인 테이블 연결선 (edge-screen-table-)은 기존 로직
        // 포커스가 없으면 모든 화면-테이블 연결선 정상 표시
        if (focusedScreenId === null) {
          return edge; // 원본 그대로
        }
        
        return {
          ...edge,
          animated: isMyConnection,
          style: {
            ...edge.style,
            stroke: isMyConnection ? "hsl(var(--primary))" : "hsl(var(--border))",
            strokeWidth: isMyConnection ? 2.5 : 1,
            strokeDasharray: isMyConnection ? undefined : "5,5",
            opacity: isMyConnection ? 1 : 0.2,
          },
        };
      }
      
      // 메인 테이블 → 서브 테이블 연결선 (메인-메인과 동일한 스타일)
      // 규격: bottom → top 고정 (아래로 문어발처럼 뻗어나감)
      if (edge.source.startsWith("table-") && edge.target.startsWith("subtable-")) {
        // 관계 유형별 색상 결정
        const visualRelationType = (edge.data as any)?.visualRelationType as VisualRelationType || 'join';
        const relationColor = RELATION_COLORS[visualRelationType];
        
        // 포커스가 없으면 모든 서브 테이블 연결선 흐리게 (기본 상태)
        if (focusedScreenId === null) {
          return {
            ...edge,
            sourceHandle: "bottom",
            targetHandle: "top",
            animated: false,
            style: {
              ...edge.style,
              stroke: relationColor.strokeLight,
              strokeWidth: 1.5,
              strokeDasharray: "8,4",
              opacity: 0.4,
            },
            markerEnd: {
              type: MarkerType.ArrowClosed,
              color: relationColor.strokeLight,
            },
          };
        }
        
        // 엣지 ID에서 화면 ID 추출: edge-main-sub-{screenId}-{mainTable}-{subTable}
        const idParts = edge.id.split("-");
        const edgeScreenId = idParts.length >= 4 ? parseInt(idParts[3]) : null;
        
        // 포커스된 화면의 서브 테이블 연결인지 확인
        const isMySubTable = edgeScreenId === focusedScreenId;
        
        // 대체 방법: screenSubTableMap 사용
        const focusedSubTables = focusedScreenId ? screenSubTableMap[focusedScreenId] || [] : [];
        const subTableName = edge.target.replace("subtable-", "");
        const isMySubTableByMap = focusedSubTables.includes(subTableName);
        
        const isActive = isMySubTable || isMySubTableByMap;
        
        return {
          ...edge,
          sourceHandle: "bottom",
          targetHandle: "top",
          animated: isActive,
          style: {
            ...edge.style,
            stroke: isActive ? relationColor.stroke : relationColor.strokeLight,
            strokeWidth: isActive ? 2.5 : 1.5,
            strokeDasharray: "8,4",
            opacity: isActive ? 1 : 0.2,
          },
          markerEnd: {
            type: MarkerType.ArrowClosed,
            color: isActive ? relationColor.stroke : relationColor.strokeLight,
          },
        };
      }
      
      // 필터 조인 엣지 (필터 대상 테이블 → 조인 참조 테이블)
      // 규격: 해당 화면이 포커싱됐을 때만 활성화
      if (edge.id.startsWith("edge-filter-join-")) {
        const edgeSourceScreenId = (edge.data as any)?.sourceScreenId;
        
        // 포커스가 없으면 흐리게 표시
        if (focusedScreenId === null) {
          return {
            ...edge,
            animated: false,
            style: {
              ...edge.style,
              stroke: RELATION_COLORS.join.strokeLight,
              strokeWidth: 1.5,
              strokeDasharray: "6,4",
              opacity: 0.2,
            },
            markerEnd: {
              type: MarkerType.ArrowClosed,
              color: RELATION_COLORS.join.strokeLight,
            },
          };
        }
        
        // 포커스된 화면과 일치하는지 확인
        const isMyConnection = edgeSourceScreenId === focusedScreenId;
        
        if (!isMyConnection) {
          // 다른 화면의 필터 조인 엣지는 숨김
          return {
            ...edge,
            hidden: true,
          };
        }
        
        // 내 화면의 필터 조인 엣지는 활성화
        return {
          ...edge,
          animated: true,
          style: {
            ...edge.style,
            stroke: RELATION_COLORS.join.stroke,
            strokeWidth: 2.5,
            strokeDasharray: "6,4",
            opacity: 1,
          },
          markerEnd: {
            type: MarkerType.ArrowClosed,
            color: RELATION_COLORS.join.stroke,
          },
        };
      }
      
      // 메인 테이블 → 메인 테이블 연결선 (서브테이블 구간 통과)
      // 규격: bottom → bottom_target 고정 (아래쪽 서브테이블 선 구간을 통해 연결)
      // edge-main-main-*, edge-join-db-* 모두 동일한 스타일 적용
      const isMainToMainJoin = edge.source.startsWith("table-") && 
                               edge.target.startsWith("table-") && 
                               (edge.id.startsWith("edge-main-main-") || edge.id.startsWith("edge-join-db-"));
      if (isMainToMainJoin) {
        // 관계 유형별 색상 결정
        const visualRelationType = (edge.data as any)?.visualRelationType as VisualRelationType || 'join';
        const relationColor = RELATION_COLORS[visualRelationType];
        
        if (focusedScreenId === null) {
          // 포커스 없으면 관계 유형별 연한 색상으로 표시
          return {
            ...edge,
            sourceHandle: "bottom",
            targetHandle: "bottom_target",
            animated: false,
            style: {
              ...edge.style,
              stroke: relationColor.strokeLight,
              strokeWidth: 1.5,
              strokeDasharray: "8,4",
              opacity: 0.4,
            },
          };
        }
        
        // 포커스된 화면에서 생성된 연결선만 표시
        const edgeSourceScreenId = (edge.data as any)?.sourceScreenId;
        const isMyConnection = edgeSourceScreenId === focusedScreenId;
        
        // 포커스된 화면과 관련 없는 메인-메인 엣지는 숨김
        if (!isMyConnection) {
          return {
            ...edge,
            hidden: true,
          };
        }
        
        return {
          ...edge,
          sourceHandle: "bottom",          // 고정: 서브테이블 구간 통과
          targetHandle: "bottom_target",   // 고정: 서브테이블 구간 통과
          animated: true,
          style: {
            ...edge.style,
            stroke: relationColor.stroke,  // 관계 유형별 진한 색상
            strokeWidth: 2.5,
            strokeDasharray: "8,4",
            opacity: 1,
          },
          markerEnd: {
            type: MarkerType.ArrowClosed,
            color: relationColor.stroke,
            width: 15,
            height: 15,
          },
        };
      }
      
      return edge;
    });
    
    // 기존 엣지 + 조인 관계 엣지 합치기
    const allEdges = [...styledOriginalEdges, ...joinEdges];
    // 엣지 필터 적용 (edgeFilterState에 따라 숨김)
    return allEdges.map((edge) => {
      const category = (edge.data as any)?.edgeCategory as EdgeCategory | undefined;
      if (category && !edgeFilterState[category]) {
        return {
          ...edge,
          hidden: true,
        };
      }
      return edge;
    });
  }, [edges, nodes, selectedGroup, focusedScreenId, screen, screenSubTableMap, subTablesDataMap, screenTableMap, edgeFilterState]);

  // 그룹의 화면 목록 (데이터 흐름 설정용) - 모든 조건부 return 전에 선언해야 함
  const groupScreensList = React.useMemo(() => {
    if (!selectedGroup) return [];
    // nodes에서 screen- 으로 시작하는 노드들 추출
    return nodes
      .filter(n => n.id.startsWith("screen-"))
      .map(n => ({
        screen_id: parseInt(n.id.replace("screen-", "")),
        screen_name: (n.data as ScreenNodeData).label || `화면 ${n.id}`,
      }));
  }, [selectedGroup, nodes]);

  // 조건부 렌더링 (모든 훅 선언 후에 위치해야 함)
  if (!screen && !selectedGroup) {
    return (
      <div className="flex h-full flex-col items-center justify-center gap-6 p-8">
        <div className="relative">
          <div className="flex items-center gap-4 opacity-30">
            <div className="h-16 w-24 rounded-lg border-2 border-dashed border-primary/40 flex items-center justify-center">
              <Monitor className="h-6 w-6 text-primary/60" />
            </div>
            <div className="h-px w-12 border-t-2 border-dashed border-border" />
            <div className="h-12 w-20 rounded-lg border-2 border-dashed border-info/40 flex items-center justify-center">
              <Database className="h-5 w-5 text-info/60" />
            </div>
          </div>
        </div>
        <div className="text-center max-w-sm">
          <h3 className="text-lg font-semibold mb-2">화면 관계 시각화</h3>
          <p className="text-sm text-muted-foreground leading-relaxed">
            좌측에서 그룹 또는 화면을 선택하면<br/>
            테이블 관계가 자동으로 시각화됩니다.
          </p>
        </div>
        <div className="flex gap-8 text-xs text-muted-foreground">
          <div className="flex items-center gap-2">
            <span className="flex h-5 w-5 items-center justify-center rounded-full bg-primary/10 text-primary text-[10px] font-bold">1</span>
            <span>그룹 선택</span>
          </div>
          <div className="flex items-center gap-2">
            <span className="flex h-5 w-5 items-center justify-center rounded-full bg-primary/10 text-primary text-[10px] font-bold">2</span>
            <span>관계 확인</span>
          </div>
          <div className="flex items-center gap-2">
            <span className="flex h-5 w-5 items-center justify-center rounded-full bg-primary/10 text-primary text-[10px] font-bold">3</span>
            <span>화면 편집</span>
          </div>
        </div>
      </div>
    );
  }

  if (loading) {
    return (
      <div className="flex h-full items-center justify-center">
        <div className="text-sm text-muted-foreground">로딩 중...</div>
      </div>
    );
  }

  return (
    <div className="relative h-full w-full">
      {/* 선택 정보 바 (캔버스 상단) */}
      {(screen || selectedGroup) && (
        <div className="absolute top-0 left-0 right-0 z-10 flex items-center gap-3 border-b bg-card dark:bg-card/80 backdrop-blur-sm px-4 py-2">
          {selectedGroup && (
            <>
              <FolderOpen className="h-4 w-4 text-warning" />
              <span className="text-sm font-medium">{selectedGroup.name}</span>
            </>
          )}
          {screen && !selectedGroup && (
            <>
              <Monitor className="h-4 w-4 text-primary" />
              <span className="text-sm font-medium">{screen.screenName}</span>
              <span className="text-xs text-muted-foreground/80 dark:text-muted-foreground/50 font-mono">{screen.screenCode}</span>
            </>
          )}

          <div className="h-4 w-px bg-border/50 dark:bg-border/30 mx-1" />
          <span className="text-[10px] font-medium text-muted-foreground/80 dark:text-muted-foreground/50">연결</span>

          {(
            [
              { key: "main" as EdgeCategory, label: "메인", color: "bg-primary", defaultOn: true },
              { key: "filter" as EdgeCategory, label: "마스터-디테일", color: "bg-[hsl(var(--info))]", defaultOn: true },
              { key: "join" as EdgeCategory, label: "엔티티 조인", color: "bg-amber-400", defaultOn: true },
              { key: "lookup" as EdgeCategory, label: "코드 참조", color: "bg-warning", defaultOn: false },
            ] as const
          ).map(({ key, label, color, defaultOn }) => {
            const isOn = edgeFilterState[key];
            const count = edges.filter((e) => (e.data as any)?.edgeCategory === key).length;
            return (
              <button
                key={key}
                type="button"
                onClick={() => setEdgeFilterState((prev) => ({ ...prev, [key]: !prev[key] }))}
                className={`inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full text-[10px] font-medium transition-all duration-200 ${
                  isOn
                    ? "bg-foreground/[0.08] dark:bg-foreground/5 border border-border/40 dark:border-border/20 text-foreground/80"
                    : `border text-muted-foreground/70 dark:text-muted-foreground/40 ${!defaultOn ? "border-dashed border-border/40 dark:border-border/20" : "border-border/40 dark:border-border/10"}`
                }`}
              >
                <span className={`w-1.5 h-1.5 rounded-full ${color} transition-opacity ${isOn ? "opacity-100 shadow-sm" : "opacity-50 dark:opacity-30"}`} />
                {label}
                <span className="text-[9px] text-muted-foreground/70 dark:text-muted-foreground/40 font-mono">{count}</span>
              </button>
            );
          })}
        </div>
      )}
      {/* isViewReady가 false면 숨김 처리하여 깜빡임 방지 */}
      <div className={`h-full w-full transition-opacity duration-0 ${isViewReady ? "opacity-100" : "opacity-0"}`}>
      <ReactFlow
          className="[&_.react-flow__node]:transition-all [&_.react-flow__node]:duration-300"
          nodes={styledNodes}
          edges={styledEdges}
        onNodesChange={onNodesChange}
        onEdgesChange={onEdgesChange}
          onNodeClick={handleNodeClick}
          onNodeContextMenu={handleNodeContextMenu}
        nodeTypes={nodeTypes}
        edgeTypes={edgeTypes}
        minZoom={0.3}
        maxZoom={1.5}
        proOptions={{ hideAttribution: true }}
      >
          <svg style={{ position: "absolute", width: 0, height: 0 }}>
            <defs>
              <filter id="edge-glow" x="-50%" y="-50%" width="200%" height="200%">
                <feGaussianBlur stdDeviation="3" result="blur" />
                <feMerge>
                  <feMergeNode in="blur" />
                  <feMergeNode in="SourceGraphic" />
                </feMerge>
              </filter>
            </defs>
          </svg>
          <Background id="bg-dots" variant={BackgroundVariant.Dots} gap={16} size={0.5} color="hsl(var(--border) / 0.3)" />
          <Background id="bg-lines" variant={BackgroundVariant.Lines} gap={120} color="hsl(var(--border) / 0.08)" />
        <Controls position="top-right" />
        <MiniMap
          position="bottom-right"
          nodeColor={(node) => {
            if (node.type === "screenNode") return "hsl(var(--primary))";
            if (node.type === "tableNode") return "hsl(var(--warning))";
            return "hsl(var(--muted-foreground))";
          }}
          nodeStrokeWidth={2}
          zoomable
          pannable
          style={{
            background: "hsl(var(--card) / 0.8)",
            border: "1px solid hsl(var(--border) / 0.5)",
            borderRadius: "8px",
            marginBottom: "8px",
          }}
        />
      </ReactFlow>
      </div>
      
      {/* 화면 노드 설정 모달 */}
      {settingModalNode && settingModalNode.nodeType === "screen" && (
        <ScreenSettingModal
          isOpen={isSettingModalOpen}
          onClose={handleSettingModalClose}
          screenId={settingModalNode.screenId}
          screenName={settingModalNode.screenName}
          groupId={selectedGroup?.id}
          companyCode={settingModalNode.companyCode}
          mainTable={settingModalNode.existingConfig?.mainTable}
          mainTableLabel={settingModalNode.tableLabel}
          filterTables={settingModalNode.existingConfig?.filterTables}
          fieldMappings={settingModalNode.existingConfig?.fieldMappings}
          componentCount={0}
          onSaveSuccess={handleRefreshVisualization}
        />
      )}
      
      {/* 테이블 노드 설정 모달 */}
      {settingModalNode && settingModalNode.nodeType === "table" && (
        <TableSettingModal
          isOpen={isSettingModalOpen}
          onClose={handleSettingModalClose}
          tableName={settingModalNode.tableName || ""}
          tableLabel={settingModalNode.tableLabel}
          screenId={settingModalNode.screenId}
          joinColumnRefs={settingModalNode.existingConfig?.joinColumnRefs}
          referencedBy={settingModalNode.existingConfig?.referencedBy}
          columns={settingModalNode.existingConfig?.columns?.map((col) => ({
            column: col.originalName ?? col.name,
            label: col.name,
            type: col.type,
            isPK: col.isPrimaryKey,
            isFK: col.isForeignKey,
          }))}
          filterColumns={settingModalNode.existingConfig?.filterColumns}
          onSaveSuccess={handleRefreshVisualization}
        />
      )}
    </div>
  );
}

// 외부 래퍼 컴포넌트 (ReactFlowProvider 포함)
export function ScreenRelationFlow(props: ScreenRelationFlowProps) {
  return (
    <ReactFlowProvider>
      <ScreenRelationFlowInner {...props} />
    </ReactFlowProvider>
  );
}
