"use client";

import React, { useState, useCallback, useEffect, useSyncExternalStore } from "react";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog";
import { useAuth } from "@/hooks/useAuth";
import { uploadFilesAndCreateData } from "@/lib/api/file";
import { toast } from "sonner";
import { ComponentData, WidgetComponent, DataTableComponent, FileComponent, ButtonTypeConfig } from "@/types/screen";
import { FileUploadComponent } from "@/lib/registry/components/file-upload/FileUploadComponent";
import { InteractiveDataTable } from "./InteractiveDataTable";
import { DynamicWebTypeRenderer } from "@/lib/registry";
import { DynamicComponentRenderer, isColumnRequiredByMeta } from "@/lib/registry/DynamicComponentRenderer";
import { filterDOMProps } from "@/lib/utils/domPropsFilter";
import { isFileComponent, isDataTableComponent, isButtonComponent } from "@/lib/utils/componentTypeUtils";
import { FlowButtonGroup } from "./widgets/FlowButtonGroup";
import { FlowVisibilityConfig } from "@/types/control-management";
import { findAllButtonGroups } from "@/lib/utils/flowButtonGroupUtils";
import { useScreenPreview } from "@/contexts/ScreenPreviewContext";
import { useSplitPanelContext } from "@/contexts/SplitPanelContext";
import { evaluateConditional } from "@/lib/utils/conditionalEvaluator";
import {
  subscribe as canvasSplitSubscribe,
  getSnapshot as canvasSplitGetSnapshot,
  getServerSnapshot as canvasSplitGetServerSnapshot,
  subscribeDom as canvasSplitSubscribeDom,
} from "@/lib/registry/components/v2-split-line/canvasSplitStore";
import { ButtonIconRenderer } from "@/lib/button-icon-map";

import { getAdaptiveLabelColor } from "@/lib/utils/darkModeColor";

// 컴포넌트 렌더러들을 강제로 로드하여 레지스트리에 등록
import "@/lib/registry/components/ButtonRenderer";
import "@/lib/registry/components/CardRenderer";
import "@/lib/registry/components/DashboardRenderer";
import "@/lib/registry/components/WidgetRenderer";
import { dynamicFormApi, DynamicFormData } from "@/lib/api/dynamicForm";
import { useParams } from "next/navigation";
import { screenApi } from "@/lib/api/screen";

interface InteractiveScreenViewerProps {
  component: ComponentData;
  allComponents: ComponentData[];
  formData?: Record<string, any>;
  onFormDataChange?: (fieldName: string, value: any) => void;
  hideLabel?: boolean;
  screenInfo?: {
    id: number;
    tableName?: string;
  };
  menuObjid?: number; // 메뉴 OBJID (코드 스코프용)
  onSave?: () => Promise<void>;
  onRefresh?: () => void;
  onFlowRefresh?: () => void;
  // 외부에서 전달받는 사용자 정보 (ScreenModal 등에서 사용)
  userId?: string;
  userName?: string;
  companyCode?: string;
  // 그룹 데이터 (EditModal에서 전달)
  groupedData?: Record<string, any>[];
  // 비활성화할 필드 목록 (EditModal에서 전달)
  disabledFields?: string[];
  // EditModal 내부인지 여부 (button-primary가 EditModal의 handleSave 사용하도록)
  isInModal?: boolean;
  // 원본 데이터 (수정 모드에서 UPDATE 판단용)
  originalData?: Record<string, any> | null;
  // 탭 관련 정보 (탭 내부의 컴포넌트에서 사용)
  parentTabId?: string; // 부모 탭 ID
  parentTabsComponentId?: string; // 부모 탭 컴포넌트 ID
}

export const InteractiveScreenViewerDynamic: React.FC<InteractiveScreenViewerProps> = ({
  component,
  allComponents,
  formData: externalFormData,
  onFormDataChange,
  hideLabel = false,
  screenInfo,
  menuObjid,
  onSave,
  onRefresh,
  onFlowRefresh,
  userId: externalUserId,
  userName: externalUserName,
  companyCode: externalCompanyCode,
  groupedData,
  disabledFields = [],
  isInModal = false,
  originalData,
  parentTabId,
  parentTabsComponentId,
}) => {
  const { isPreviewMode } = useScreenPreview();
  const { userName: authUserName, user: authUser } = useAuth();
  const splitPanelContext = useSplitPanelContext();

  // 캔버스 분할선 글로벌 스토어 구독
  const canvasSplit = useSyncExternalStore(canvasSplitSubscribe, canvasSplitGetSnapshot, canvasSplitGetServerSnapshot);
  const canvasSplitSideRef = React.useRef<"left" | "right" | null>(null);
  const myScopeIdRef = React.useRef<string | null>(null);

  // 외부에서 전달받은 사용자 정보가 있으면 우선 사용 (ScreenModal 등에서)
  const userName = externalUserName || authUserName;
  const user =
    externalUserId && externalUserId !== authUser?.userId
      ? {
          userId: externalUserId,
          userName: externalUserName || authUserName || "",
          companyCode: externalCompanyCode || authUser?.companyCode || "",
          isAdmin: authUser?.isAdmin || false,
        }
      : authUser;
  const [localFormData, setLocalFormData] = useState<Record<string, any>>({});
  const [dateValues, setDateValues] = useState<Record<string, Date | undefined>>({});

  // 테이블에서 선택된 행 데이터 (버튼 액션에 전달)
  const [selectedRowsData, setSelectedRowsData] = useState<any[]>([]);

  // 플로우에서 선택된 데이터 (버튼 액션에 전달)
  const [flowSelectedData, setFlowSelectedData] = useState<any[]>([]);
  const [flowSelectedStepId, setFlowSelectedStepId] = useState<number | null>(null);

  // 팝업 화면 상태
  const [popupScreen, setPopupScreen] = useState<{
    screenId: number;
    title: string;
    size: string;
  } | null>(null);

  // 팝업 화면 레이아웃 상태
  const [popupLayout, setPopupLayout] = useState<ComponentData[]>([]);
  const [popupLoading, setPopupLoading] = useState(false);
  const [popupScreenResolution, setPopupScreenResolution] = useState<{ width: number; height: number } | null>(null);
  const [popupScreenInfo, setPopupScreenInfo] = useState<{ id: number; tableName?: string } | null>(null);

  // 팝업 전용 formData 상태
  const [popupFormData, setPopupFormData] = useState<Record<string, any>>({});

  // 🆕 분할 패널에서 매핑된 부모 데이터 가져오기
  // disableAutoDataTransfer가 true이면 자동 전달 비활성화 (버튼 클릭으로만 전달)
  const splitPanelMappedData = React.useMemo(() => {
    if (splitPanelContext && !splitPanelContext.disableAutoDataTransfer) {
      return splitPanelContext.getMappedParentData();
    }
    return {};
  }, [splitPanelContext, splitPanelContext?.selectedLeftData, splitPanelContext?.disableAutoDataTransfer]);

  // formData 결정 (외부에서 전달받은 것이 있으면 우선 사용, 분할 패널 데이터도 병합)
  const formData = React.useMemo(() => {
    const baseData = externalFormData || localFormData;
    // 분할 패널 매핑 데이터가 있으면 병합 (기존 값이 없는 경우에만)
    // disableAutoDataTransfer가 true이면 자동 병합 안함
    if (Object.keys(splitPanelMappedData).length > 0) {
      const merged = { ...baseData };
      for (const [key, value] of Object.entries(splitPanelMappedData)) {
        // 기존 값이 없거나 빈 값인 경우에만 매핑 데이터 적용
        if (merged[key] === undefined || merged[key] === null || merged[key] === "") {
          merged[key] = value;
        }
      }
      return merged;
    }
    return baseData;
  }, [externalFormData, localFormData, splitPanelMappedData]);

  // formData 업데이트 함수
  const updateFormData = useCallback(
    (fieldName: string, value: any) => {
      if (onFormDataChange) {
        onFormDataChange(fieldName, value);
      } else {
        setLocalFormData((prev) => ({
          ...prev,
          [fieldName]: value,
        }));
      }
    },
    [onFormDataChange],
  );

  // 자동값 생성 함수
  const generateAutoValue = useCallback(
    (autoValueType: string): string => {
      const now = new Date();
      const pad = (n: number) => String(n).padStart(2, "0");
      const localDate = `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}`;
      const localTime = `${pad(now.getHours())}:${pad(now.getMinutes())}:${pad(now.getSeconds())}`;
      switch (autoValueType) {
        case "current_datetime":
          return `${localDate} ${localTime}`;
        case "current_date":
          return localDate;
        case "current_time":
          return localTime;
        case "current_user":
          return userName || "사용자";
        case "uuid":
          return crypto.randomUUID();
        case "sequence":
          return `SEQ_${Date.now()}`;
        default:
          return "";
      }
    },
    [userName],
  );

  // 🆕 Enter 키로 다음 필드 이동
  useEffect(() => {
    const handleEnterKey = (e: KeyboardEvent) => {
      if (e.key === "Enter" && !e.shiftKey) {
        const target = e.target as HTMLElement;

        // 한글 조합 중이면 무시 (한글 입력 문제 방지)
        if ((e as any).isComposing || e.keyCode === 229) {
          return;
        }

        // textarea는 제외 (여러 줄 입력)
        if (target.tagName === "TEXTAREA") {
          return;
        }

        // input, select 등의 폼 요소에서만 작동
        if (target.tagName === "INPUT" || target.tagName === "SELECT" || target.getAttribute("role") === "combobox") {
          e.preventDefault();

          // 모든 포커스 가능한 요소 찾기
          const focusableElements = document.querySelectorAll<HTMLElement>(
            'input:not([disabled]):not([type="hidden"]), select:not([disabled]), textarea:not([disabled]), [role="combobox"]:not([disabled])',
          );

          // 화면에 보이는 순서(Y 좌표 → X 좌표)대로 정렬
          const focusableArray = Array.from(focusableElements).sort((a, b) => {
            const rectA = a.getBoundingClientRect();
            const rectB = b.getBoundingClientRect();

            // Y 좌표 차이가 10px 이상이면 Y 좌표로 정렬 (위에서 아래로)
            if (Math.abs(rectA.top - rectB.top) > 10) {
              return rectA.top - rectB.top;
            }

            // 같은 줄이면 X 좌표로 정렬 (왼쪽에서 오른쪽으로)
            return rectA.left - rectB.left;
          });

          const currentIndex = focusableArray.indexOf(target);

          if (currentIndex !== -1 && currentIndex < focusableArray.length - 1) {
            // 다음 요소로 포커스 이동
            const nextElement = focusableArray[currentIndex + 1];
            nextElement.focus();

            // select() 제거: 한글 입력 시 이전 필드의 마지막 글자가 복사되는 버그 방지
          }
        }
      }
    };

    document.addEventListener("keydown", handleEnterKey);

    return () => {
      document.removeEventListener("keydown", handleEnterKey);
    };
  }, []);

  // 🆕 autoFill 자동 입력 초기화
  React.useEffect(() => {
    const initAutoInputFields = async () => {
      for (const comp of allComponents) {
        // type: "component" 또는 type: "widget" 모두 처리
        if (comp.type === "widget" || comp.type === "component") {
          const widget = comp as any;
          const fieldName = widget.columnName || widget.id;

          // autoFill 처리 (테이블 조회 기반 자동 입력)
          if (widget.autoFill?.enabled || (comp as any).autoFill?.enabled) {
            const autoFillConfig = widget.autoFill || (comp as any).autoFill;
            const currentValue = formData[fieldName];

            if (currentValue === undefined || currentValue === "") {
              const { sourceTable, filterColumn, userField, displayColumn } = autoFillConfig;

              // 사용자 정보에서 필터 값 가져오기
              const userValue = user?.[userField];

              if (userValue && sourceTable && filterColumn && displayColumn) {
                try {
                  const { tableTypeApi } = await import("@/lib/api/screen");
                  const result = await tableTypeApi.getTableRecord(sourceTable, filterColumn, userValue, displayColumn);

                  updateFormData(fieldName, result.value);
                } catch (error) {
                  console.error(`autoFill 조회 실패: ${fieldName}`, error);
                }
              }
            }
          }
        }
      }
    };

    initAutoInputFields();
  }, [allComponents, user]);

  // 팝업 화면 레이아웃 로드
  React.useEffect(() => {
    if (popupScreen?.screenId) {
      loadPopupScreen(popupScreen.screenId);
    }
  }, [popupScreen?.screenId]);

  const loadPopupScreen = async (screenId: number) => {
    try {
      setPopupLoading(true);
      const response = await screenApi.getScreenLayout(screenId);

      if (response.success && response.data) {
        const screenData = response.data;
        setPopupLayout(screenData.components || []);
        setPopupScreenResolution({
          width: screenData.screenResolution?.width || 1200,
          height: screenData.screenResolution?.height || 800,
        });
        setPopupScreenInfo({
          id: screenData.id,
          tableName: screenData.tableName,
        });
      } else {
        toast.error("팝업 화면을 불러올 수 없습니다.");
        setPopupScreen(null);
      }
    } catch (error) {
      // console.error("팝업 화면 로드 오류:", error);
      toast.error("팝업 화면 로드 중 오류가 발생했습니다.");
      setPopupScreen(null);
    } finally {
      setPopupLoading(false);
    }
  };

  // 폼 데이터 변경 핸들러
  const handleFormDataChange = (fieldName: string | any, value?: any) => {
    // 일반 필드 변경
    if (onFormDataChange) {
      onFormDataChange(fieldName, value);
    } else {
      setLocalFormData((prev) => ({ ...prev, [fieldName]: value }));
    }
  };

  // 동적 대화형 위젯 렌더링
  const renderInteractiveWidget = (comp: ComponentData) => {
    // 조건부 표시 평가 (기존 conditional 시스템)
    const conditionalResult = evaluateConditional(comp.conditional, formData, allComponents);

    // 조건에 따라 숨김 처리
    if (!conditionalResult.visible) {
      return null;
    }
    
    // 🆕 conditionalConfig 시스템 체크 (V2 레이아웃용)
    const conditionalConfig = (comp as any).componentConfig?.conditionalConfig;
    if (conditionalConfig?.enabled && formData) {
      const { field, operator, value, action } = conditionalConfig;
      const fieldValue = formData[field];
      
      let conditionMet = false;
      switch (operator) {
        case "=":
        case "==":
        case "===":
          conditionMet = fieldValue === value;
          break;
        case "!=":
        case "!==":
          conditionMet = fieldValue !== value;
          break;
        default:
          conditionMet = fieldValue === value;
      }
      
      if (action === "show" && !conditionMet) {
        return null;
      }
      if (action === "hide" && conditionMet) {
        return null;
      }
    }

    // 데이터 테이블 컴포넌트 처리
    if (isDataTableComponent(comp)) {
      return (
        <InteractiveDataTable
          component={comp as DataTableComponent}
          className="h-full w-full"
          style={{
            width: "100%",
            height: "100%",
          }}
          onRefresh={() => {
            // 테이블 자체에서 loadData를 호출하므로 여기서는 빈 함수
            console.log("🔄 InteractiveDataTable 새로고침 트리거됨 (Dynamic)");
          }}
        />
      );
    }

    // 파일 컴포넌트 처리
    if (isFileComponent(comp)) {
      return renderFileComponent(comp as FileComponent);
    }

    // 버튼 컴포넌트 또는 위젯이 아닌 경우 DynamicComponentRenderer 사용
    if (comp.type !== "widget") {
      return (
        <DynamicComponentRenderer
          component={comp}
          isInteractive={true}
          formData={formData}
          originalData={originalData || undefined}
          onFormDataChange={handleFormDataChange}
          screenId={screenInfo?.id}
          tableName={screenInfo?.tableName}
          menuObjid={menuObjid}
          userId={user?.userId}
          userName={user?.userName}
          companyCode={user?.companyCode}
          onSave={onSave}
          allComponents={allComponents}
          selectedRowsData={selectedRowsData}
          onSelectedRowsChange={(selectedRows, selectedData) => {
            console.log("테이블에서 선택된 행 데이터:", selectedData);
            setSelectedRowsData(selectedData);
          }}
          groupedData={groupedData}
          disabledFields={disabledFields}
          flowSelectedData={flowSelectedData}
          flowSelectedStepId={flowSelectedStepId}
          onFlowSelectedDataChange={(selectedData, stepId) => {
            console.log("플로우에서 선택된 데이터:", { selectedData, stepId });
            setFlowSelectedData(selectedData);
            setFlowSelectedStepId(stepId);
          }}
          onRefresh={
            onRefresh ||
            (() => {
              console.log("InteractiveScreenViewerDynamic onRefresh 호출");
            })
          }
          onFlowRefresh={onFlowRefresh}
          onClose={() => {
            // buttonActions.ts가 이미 처리함
          }}
          isInModal={isInModal}
          // 탭 관련 정보 전달
          parentTabId={parentTabId}
          parentTabsComponentId={parentTabsComponentId}
        />
      );
    }

    const widget = comp as WidgetComponent;
    const { widgetType, label, placeholder, required, readonly, columnName } = widget;
    const fieldName = columnName || comp.id;
    const currentValue = formData[fieldName] || "";

    // 스타일 적용
    const applyStyles = (element: React.ReactElement) => {
      if (!comp.style) return element;

      // ✅ 격자 시스템 잔재 제거: style.width, style.height는 무시
      // size.width, size.height가 부모 컨테이너에서 적용되므로
      const { width, height, ...styleWithoutSize } = comp.style;

      return React.cloneElement(element, {
        style: {
          ...element.props.style,
          ...styleWithoutSize, // width/height 제외한 스타일만 적용
          width: "100%",
          height: "100%",
          minHeight: "100%",
          maxHeight: "100%",
          boxSizing: "border-box",
        },
      });
    };

    // 조건부 비활성화 적용
    const isConditionallyDisabled = conditionalResult.disabled;

    // 동적 웹타입 렌더링 사용
    if (widgetType) {
      try {
        const dynamicElement = (
          <DynamicWebTypeRenderer
            webType={widgetType}
            props={{
              component: widget,
              value: currentValue,
              onChange: (value: any) => handleFormDataChange(fieldName, value),
              onFormDataChange: handleFormDataChange,
              formData: formData, // 🆕 전체 formData 전달
              isInteractive: true,
              readonly: readonly || isConditionallyDisabled, // 조건부 비활성화 적용
              disabled: isConditionallyDisabled, // 조건부 비활성화 전달
              required: required,
              placeholder: placeholder,
              className: "w-full h-full",
              isInModal: isInModal, // 🆕 EditModal 내부 여부 전달
              onSave: onSave, // 🆕 EditModal의 handleSave 콜백 전달
              groupedData: groupedData, // 🆕 그룹 데이터 전달 (RepeatScreenModal용)
            }}
            config={widget.webTypeConfig}
            onEvent={(event: string, data: any) => {
              // 이벤트 처리
              // console.log(`Widget event: ${event}`, data);
            }}
          />
        );

        return applyStyles(dynamicElement);
      } catch (error) {
        // console.error(`웹타입 "${widgetType}" 대화형 렌더링 실패:`, error);
        // 오류 발생 시 폴백으로 기본 input 렌더링
        const fallbackElement = (
          <Input
            type="text"
            value={currentValue}
            onChange={(e) => handleFormDataChange(fieldName, e.target.value)}
            placeholder={`${widgetType} (렌더링 오류)`}
            disabled={readonly || isConditionallyDisabled}
            required={required}
            className="h-full w-full"
          />
        );
        return applyStyles(fallbackElement);
      }
    }

    // 웹타입이 없는 경우 기본 input 렌더링 (하위 호환성)
    const defaultElement = (
      <Input
        type="text"
        value={currentValue}
        onChange={(e) => handleFormDataChange(fieldName, e.target.value)}
        placeholder={placeholder || "입력하세요"}
        disabled={readonly || isConditionallyDisabled}
        required={required}
        className="h-full w-full"
      />
    );
    return applyStyles(defaultElement);
  };

  // 버튼 렌더링
  const renderButton = (comp: ComponentData) => {
    const config = (comp as any).webTypeConfig as ButtonTypeConfig | undefined;
    const { label } = comp;

    // 버튼 액션 핸들러들
    const handleSaveAction = async () => {
      // EditModal에서 전달된 onSave가 있으면 우선 사용 (수정 모달)
      if (onSave) {
        try {
          await onSave();
        } catch (error: any) {
          console.error("저장 오류:", error);
          const msg =
            error?.response?.data?.message ||
            error?.message ||
            "저장에 실패했습니다.";
          toast.error(msg);
        }
        return;
      }

      // 일반 저장 액션 (신규 생성)
      if (!screenInfo?.tableName) {
        toast.error("테이블명이 설정되지 않았습니다.");
        return;
      }

      // 리피터가 화면과 동일 테이블을 사용하는지 감지 (useCustomTable 미설정 = 동일 테이블)
      const hasRepeaterOnSameTable = allComponents.some((c: any) => {
        const compType = c.componentType || c.overrides?.type;
        if (compType !== "v2-repeater") return false;
        const compConfig = c.componentConfig || c.overrides || {};
        return !compConfig.useCustomTable;
      });

      if (hasRepeaterOnSameTable) {
        // 동일 테이블 리피터: 마스터 저장 스킵, 리피터만 저장
        // 리피터가 mainFormData를 각 행에 병합하여 N건 INSERT 처리
        try {
          window.dispatchEvent(
            new CustomEvent("repeaterSave", {
              detail: {
                parentId: null,
                masterRecordId: null,
                mainFormData: formData,
                tableName: screenInfo.tableName,
              },
            }),
          );

          toast.success("데이터가 성공적으로 저장되었습니다.");
        } catch (error: any) {
          const msg =
            error?.response?.data?.message ||
            error?.message ||
            "저장에 실패했습니다.";
          toast.error(msg);
        }
        return;
      }

      try {
        // 리피터 데이터(배열)를 마스터 저장에서 제외 (V2Repeater가 별도로 저장)
        // 단, 파일 업로드 컴포넌트의 파일 배열(objid 배열)은 포함
        const masterFormData: Record<string, any> = {};
        
        // 파일 업로드 컴포넌트의 columnName 목록 수집 (v2-media, file-upload 모두 포함)
        const mediaColumnNames = new Set(
          allComponents
            .filter((c: any) => 
              c.componentType === "v2-media" || 
              c.componentType === "file-upload" ||
              c.url?.includes("v2-media") ||
              c.url?.includes("file-upload")
            )
            .map((c: any) => c.columnName || c.componentConfig?.columnName)
            .filter(Boolean)
        );
        
        Object.entries(formData).forEach(([key, value]) => {
          if (!Array.isArray(value)) {
            masterFormData[key] = value;
          } else if (mediaColumnNames.has(key)) {
            masterFormData[key] = value.length > 0 ? value[0] : null;
            console.log(`📷 미디어 데이터 저장: ${key}, objid: ${masterFormData[key]}`);
          } else {
            console.log(`🔄 리피터 데이터 제외 (별도 저장): ${key}, ${value.length}개 항목`);
          }
        });
        
        const saveData: DynamicFormData = {
          tableName: screenInfo.tableName,
          data: masterFormData,
        };

        const response = await dynamicFormApi.saveData(saveData);

        if (response.success) {
          const masterRecordId = response.data?.id || formData.id;
          
          // 리피터 데이터 저장 이벤트 발생 (V2Repeater 컴포넌트가 리스닝)
          window.dispatchEvent(
            new CustomEvent("repeaterSave", {
              detail: { 
                parentId: masterRecordId,
                masterRecordId,
                mainFormData: formData,
                tableName: screenInfo.tableName,
              },
            }),
          );

          toast.success("데이터가 성공적으로 저장되었습니다.");
        } else {
          toast.error(response.message || "저장에 실패했습니다.");
        }
      } catch (error: any) {
        const msg =
          error?.response?.data?.message ||
          error?.message ||
          "저장에 실패했습니다.";
        toast.error(msg);
      }
    };

    const handleDeleteAction = async () => {
      if (!config?.confirmationEnabled || window.confirm(config.confirmationMessage || "정말 삭제하시겠습니까?")) {
        // console.log("🗑️ 삭제 액션 실행");
        toast.success("삭제가 완료되었습니다.");
      }
    };

    const handlePopupAction = () => {
      if (config?.popupScreenId) {
        setPopupScreen({
          screenId: config.popupScreenId,
          title: config.popupTitle || "팝업 화면",
          size: config.popupSize || "medium",
        });
      }
    };

    const handleNavigateAction = () => {
      const navigateType = config?.navigateType || "url";

      if (navigateType === "screen" && config?.navigateScreenId) {
        const screenPath = `/screens/${config.navigateScreenId}`;

        if (config.navigateTarget === "_blank") {
          window.open(screenPath, "_blank");
        } else {
          window.location.href = screenPath;
        }
      } else if (navigateType === "url" && config?.navigateUrl) {
        if (config.navigateTarget === "_blank") {
          window.open(config.navigateUrl, "_blank");
        } else {
          window.location.href = config.navigateUrl;
        }
      }
    };

    const handleCustomAction = async () => {
      if (config?.customAction) {
        try {
          const result = eval(config.customAction);
          if (result instanceof Promise) {
            await result;
          }
          // console.log("⚡ 커스텀 액션 실행 완료");
        } catch (error) {
          throw new Error(`커스텀 액션 실행 실패: ${error.message}`);
        }
      }
    };

    // 🆕 즉시 저장(quickInsert) 액션 핸들러
    const handleQuickInsertAction = async () => {
      // componentConfig에서 quickInsertConfig 가져오기
      const quickInsertConfig = (comp as any).componentConfig?.action?.quickInsertConfig;

      if (!quickInsertConfig?.targetTable) {
        toast.error("대상 테이블이 설정되지 않았습니다.");
        return;
      }

      // 1. 대상 테이블의 컬럼 목록 조회 (자동 매핑용)
      let targetTableColumns: string[] = [];
      try {
        const { default: apiClient } = await import("@/lib/api/client");
        const columnsResponse = await apiClient.get(
          `/table-management/tables/${quickInsertConfig.targetTable}/columns`,
        );
        if (columnsResponse.data?.success && columnsResponse.data?.data) {
          const columnsData = columnsResponse.data.data.columns || columnsResponse.data.data;
          targetTableColumns = columnsData.map((col: any) => col.columnName || col.column_name || col.name);
          console.log("📍 대상 테이블 컬럼 목록:", targetTableColumns);
        }
      } catch (error) {
        console.error("대상 테이블 컬럼 조회 실패:", error);
      }

      // 2. 컬럼 매핑에서 값 수집
      const insertData: Record<string, any> = {};
      const columnMappings = quickInsertConfig.columnMappings || [];

      for (const mapping of columnMappings) {
        let value: any;

        switch (mapping.sourceType) {
          case "component":
            // 같은 화면의 컴포넌트에서 값 가져오기
            // 방법1: sourceColumnName 사용
            if (mapping.sourceColumnName && formData[mapping.sourceColumnName] !== undefined) {
              value = formData[mapping.sourceColumnName];
              console.log(`📍 컴포넌트 값 (sourceColumnName): ${mapping.sourceColumnName} = ${value}`);
            }
            // 방법2: sourceComponentId로 컴포넌트 찾아서 columnName 사용
            else if (mapping.sourceComponentId) {
              const sourceComp = allComponents.find((c: any) => c.id === mapping.sourceComponentId);
              if (sourceComp) {
                const fieldName = (sourceComp as any).columnName || sourceComp.id;
                value = formData[fieldName];
                console.log(`📍 컴포넌트 값 (컴포넌트 조회): ${fieldName} = ${value}`);
              }
            }
            break;

          case "leftPanel":
            // 분할 패널 좌측 선택 데이터에서 값 가져오기
            if (mapping.sourceColumn && splitPanelContext?.selectedLeftData) {
              value = splitPanelContext.selectedLeftData[mapping.sourceColumn];
            }
            break;

          case "fixed":
            value = mapping.fixedValue;
            break;

          case "currentUser":
            if (mapping.userField) {
              switch (mapping.userField) {
                case "userId":
                  value = user?.userId;
                  break;
                case "userName":
                  value = userName;
                  break;
                case "companyCode":
                  value = user?.companyCode;
                  break;
                case "deptCode":
                  value = authUser?.deptCode;
                  break;
              }
            }
            break;
        }

        if (value !== undefined && value !== null && value !== "") {
          insertData[mapping.targetColumn] = value;
        }
      }

      // 3. 좌측 패널 선택 데이터에서 자동 매핑 (컬럼명이 같고 대상 테이블에 있는 경우)
      if (splitPanelContext?.selectedLeftData && targetTableColumns.length > 0) {
        const leftData = splitPanelContext.selectedLeftData;
        console.log("📍 좌측 패널 자동 매핑 시작:", leftData);

        for (const [key, val] of Object.entries(leftData)) {
          // 이미 매핑된 컬럼은 스킵
          if (insertData[key] !== undefined) {
            continue;
          }

          // 대상 테이블에 해당 컬럼이 없으면 스킵
          if (!targetTableColumns.includes(key)) {
            continue;
          }

          // 시스템 컬럼 제외
          const systemColumns = ["id", "created_date", "updated_date", "writer", "writer_name"];
          if (systemColumns.includes(key)) {
            continue;
          }

          // _label, _name 으로 끝나는 표시용 컬럼 제외
          if (key.endsWith("_label") || key.endsWith("_name")) {
            continue;
          }

          // 값이 있으면 자동 추가
          if (val !== undefined && val !== null && val !== "") {
            insertData[key] = val;
            console.log(`📍 자동 매핑 추가: ${key} = ${val}`);
          }
        }
      }

      console.log("🚀 quickInsert 최종 데이터:", insertData);

      // 4. 필수값 검증
      if (Object.keys(insertData).length === 0) {
        toast.error("저장할 데이터가 없습니다. 값을 선택해주세요.");
        return;
      }

      // 5. 중복 체크 (설정된 경우)
      if (quickInsertConfig.duplicateCheck?.enabled && quickInsertConfig.duplicateCheck?.columns?.length > 0) {
        try {
          const { default: apiClient } = await import("@/lib/api/client");

          // 중복 체크를 위한 검색 조건 구성
          const searchConditions: Record<string, any> = {};
          for (const col of quickInsertConfig.duplicateCheck.columns) {
            if (insertData[col] !== undefined) {
              searchConditions[col] = { value: insertData[col], operator: "equals" };
            }
          }

          console.log("📍 중복 체크 조건:", searchConditions);

          // 기존 데이터 조회
          const checkResponse = await apiClient.post(`/table-management/tables/${quickInsertConfig.targetTable}/data`, {
            page: 1,
            pageSize: 1,
            search: searchConditions,
          });

          console.log("📍 중복 체크 응답:", checkResponse.data);

          // data 배열이 있고 길이가 0보다 크면 중복
          const existingData = checkResponse.data?.data?.data || checkResponse.data?.data || [];
          if (Array.isArray(existingData) && existingData.length > 0) {
            toast.error(quickInsertConfig.duplicateCheck.errorMessage || "이미 존재하는 데이터입니다.");
            return;
          }
        } catch (error) {
          console.error("중복 체크 오류:", error);
          // 중복 체크 실패 시 계속 진행
        }
      }

      // 6. API 호출
      try {
        const { default: apiClient } = await import("@/lib/api/client");

        const response = await apiClient.post(
          `/table-management/tables/${quickInsertConfig.targetTable}/add`,
          insertData,
        );

        if (response.data?.success) {
          // 7. 성공 후 동작
          if (quickInsertConfig.afterInsert?.showSuccessMessage !== false) {
            toast.success(quickInsertConfig.afterInsert?.successMessage || "저장되었습니다.");
          }

          // 데이터 새로고침 (테이블리스트, 카드 디스플레이)
          if (quickInsertConfig.afterInsert?.refreshData !== false) {
            console.log("📍 데이터 새로고침 이벤트 발송");
            if (typeof window !== "undefined") {
              window.dispatchEvent(new CustomEvent("refreshTable"));
              window.dispatchEvent(new CustomEvent("refreshCardDisplay"));
            }
          }

          // 지정된 컴포넌트 초기화
          if (quickInsertConfig.afterInsert?.clearComponents?.length > 0) {
            for (const componentId of quickInsertConfig.afterInsert.clearComponents) {
              const targetComp = allComponents.find((c: any) => c.id === componentId);
              if (targetComp) {
                const fieldName = (targetComp as any).columnName || targetComp.id;
                onFormDataChange?.(fieldName, "");
              }
            }
          }
        } else {
          toast.error(response.data?.message || "저장에 실패했습니다.");
        }
      } catch (error: any) {
        console.error("quickInsert 오류:", error);
        toast.error(error.response?.data?.message || error.message || "저장 중 오류가 발생했습니다.");
      }
    };

    const handleClick = async () => {
      try {
        const actionType = config?.actionType || "save";

        switch (actionType) {
          case "save":
            await handleSaveAction();
            break;
          case "delete":
            await handleDeleteAction();
            break;
          case "popup":
            handlePopupAction();
            break;
          case "navigate":
            handleNavigateAction();
            break;
          case "custom":
            await handleCustomAction();
            break;
          case "quickInsert":
            await handleQuickInsertAction();
            break;
          default:
          // console.log("🔘 기본 버튼 클릭");
        }
      } catch (error) {
        // console.error("버튼 액션 오류:", error);
        toast.error(error.message || "액션 실행 중 오류가 발생했습니다.");
      }
    };

    // 커스텀 색상이 있으면 Tailwind 클래스 대신 직접 스타일 적용
    const hasCustomColors = config?.backgroundColor || config?.textColor || comp.style?.backgroundColor || comp.style?.color;

    return (
      <button
        onClick={handleClick}
        disabled={config?.disabled}
        className={`w-full rounded-md px-3 py-2 text-sm font-medium transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 ${
          hasCustomColors 
            ? '' 
            : 'bg-background border border-foreground text-foreground shadow-xs hover:bg-muted/50'
        }`}
        style={{
          // 컴포넌트 스타일 적용
          ...comp.style,
          // 설정값이 있으면 우선 적용
          backgroundColor: config?.backgroundColor || comp.style?.backgroundColor,
          color: config?.textColor || comp.style?.color,
          // 부모 컨테이너 크기에 맞춤
          width: "100%",
          height: "100%",
        }}
      >
        <ButtonIconRenderer componentConfig={(comp as any).componentConfig} fallbackLabel={label || "버튼"} />
      </button>
    );
  };

  // 파일 컴포넌트 렌더링
  const renderFileComponent = (comp: FileComponent) => {
    const { label, readonly } = comp;
    const fieldName = comp.columnName || comp.id;

    // 화면 ID 추출 (URL에서)
    const screenId =
      screenInfo?.screenId ||
      (typeof window !== "undefined" && window.location.pathname.includes("/screens/")
        ? parseInt(window.location.pathname.split("/screens/")[1])
        : null);

    return (
      <div className="h-full w-full">
        {/* 실제 FileUploadComponent 사용 */}
        <FileUploadComponent
          component={comp}
          componentConfig={{
            ...comp.fileConfig,
            multiple: comp.fileConfig?.multiple !== false,
            accept: comp.fileConfig?.accept || "*/*",
            maxSize: (comp.fileConfig?.maxSize || 10) * 1024 * 1024, // MB to bytes
            disabled: readonly,
          }}
          componentStyle={{
            width: "100%",
            height: "100%",
          }}
          className="h-full w-full"
          isInteractive={true}
          isDesignMode={false}
          formData={{
            screenId, // 🎯 화면 ID 전달
            // 🎯 백엔드 API가 기대하는 정확한 형식으로 설정
            autoLink: true, // 자동 연결 활성화
            linkedTable: "screen_files", // 연결 테이블
            recordId: screenId, // 레코드 ID
            columnName: fieldName, // 컬럼명 (중요!)
            isVirtualFileColumn: true, // 가상 파일 컬럼
            id: formData.id,
            ...formData,
          }}
          onFormDataChange={(data) => {
            // console.log("📝 실제 화면 파일 업로드 완료:", data);
            if (onFormDataChange) {
              Object.entries(data).forEach(([key, value]) => {
                onFormDataChange(key, value);
              });
            }
          }}
          onUpdate={(updates) => {
            console.log("🔄🔄🔄 실제 화면 파일 컴포넌트 업데이트:", {
              componentId: comp.id,
              hasUploadedFiles: !!updates.uploadedFiles,
              filesCount: updates.uploadedFiles?.length || 0,
              hasLastFileUpdate: !!updates.lastFileUpdate,
              updates,
            });

            // 파일 업로드/삭제 완료 시 formData 업데이터
            if (updates.uploadedFiles && onFormDataChange) {
              onFormDataChange(fieldName, updates.uploadedFiles);
            }

            // 🎯 화면설계 모드와 동기화를 위한 전역 이벤트 발생 (업로드/삭제 모두)
            if (updates.uploadedFiles !== undefined && typeof window !== "undefined") {
              // 업로드인지 삭제인지 판단 (lastFileUpdate가 있으면 변경사항 있음)
              const action = updates.lastFileUpdate ? "update" : "sync";

              const eventDetail = {
                componentId: comp.id,
                files: updates.uploadedFiles,
                fileCount: updates.uploadedFiles.length,
                action: action,
                timestamp: updates.lastFileUpdate || Date.now(),
                source: "realScreen", // 실제 화면에서 온 이벤트임을 표시
              };

              // console.log("🚀🚀🚀 실제 화면 파일 변경 이벤트 발생:", eventDetail);

              const event = new CustomEvent("globalFileStateChanged", {
                detail: eventDetail,
              });
              window.dispatchEvent(event);

              // console.log("✅✅✅ 실제 화면 → 화면설계 모드 동기화 이벤트 발생 완료");

              // 추가 지연 이벤트들 (화면설계 모드가 열려있을 때를 대비)
              setTimeout(() => {
                // console.log("🔄 실제 화면 추가 이벤트 발생 (지연 100ms)");
                window.dispatchEvent(
                  new CustomEvent("globalFileStateChanged", {
                    detail: { ...eventDetail, delayed: true },
                  }),
                );
              }, 100);

              setTimeout(() => {
                // console.log("🔄 실제 화면 추가 이벤트 발생 (지연 500ms)");
                window.dispatchEvent(
                  new CustomEvent("globalFileStateChanged", {
                    detail: { ...eventDetail, delayed: true, attempt: 2 },
                  }),
                );
              }, 500);
            }
          }}
        />
      </div>
    );
  };

  // 메인 렌더링
  const { type, position, size, style = {} } = component;

  // ✅ 격자 시스템 잔재 제거: style.width, style.height 무시
  const { width: styleWidth, height: styleHeight, ...styleWithoutSize } = style;

  // TableSearchWidget의 경우 높이를 자동으로 설정
  const isTableSearchWidget = (component as any).componentId === "table-search-widget";

  // 라벨 표시 여부 확인 (V2 입력 컴포넌트)
  const compType = (component as any).componentType || "";
  const isV2InputComponent = 
    type === "v2-input" || type === "v2-select" || type === "v2-date" ||
    compType === "v2-input" || compType === "v2-select" || compType === "v2-date";
  const hasVisibleLabel = isV2InputComponent && 
    style?.labelDisplay !== false && style?.labelDisplay !== "false" &&
    (style?.labelText || (component as any).label);
  
  // 라벨 위치에 따라 오프셋 계산 (좌/우 배치 시 세로 오프셋 불필요)
  const labelPos = style?.labelPosition || "top";
  const isVerticalLabel = labelPos === "top" || labelPos === "bottom";
  const labelFontSize = style?.labelFontSize ? parseInt(String(style.labelFontSize)) : 14;
  const labelMarginBottom = style?.labelMarginBottom ? parseInt(String(style.labelMarginBottom)) : 4;
  const labelOffset = (hasVisibleLabel && isVerticalLabel) ? (labelFontSize + labelMarginBottom + 2) : 0;

  // 수평 라벨 관련 (componentStyle 계산보다 먼저 선언)
  const needsExternalLabel = hasVisibleLabel && labelPos !== "top";
  const isHorizLabel = labelPos === "left" || labelPos === "right";
  const labelText = style?.labelText || (component as any).label || "";
  const labelGapValue = style?.labelGap || "8px";

  const calculateCanvasSplitX = (): { x: number; w: number } => {
    const compType = (component as any).componentType || "";
    const isSplitLine = type === "component" && compType === "v2-split-line";
    const origX = position?.x || 0;
    const defaultW = size?.width || 200;

    if (isSplitLine) return { x: origX, w: defaultW };

    if (!canvasSplit.active || canvasSplit.canvasWidth <= 0 || !canvasSplit.scopeId) {
      return { x: origX, w: defaultW };
    }

    if (myScopeIdRef.current === null) {
      const el = document.getElementById(`interactive-${component.id}`);
      const container = el?.closest("[data-screen-runtime]");
      myScopeIdRef.current = container?.getAttribute("data-split-scope") || "__none__";
    }
    if (myScopeIdRef.current !== canvasSplit.scopeId) {
      return { x: origX, w: defaultW };
    }

    const { initialDividerX, currentDividerX, canvasWidth } = canvasSplit;
    const delta = currentDividerX - initialDividerX;
    if (Math.abs(delta) < 1) return { x: origX, w: defaultW };

    const origW = defaultW;
    if (canvasSplitSideRef.current === null) {
      const componentCenterX = origX + (origW / 2);
      canvasSplitSideRef.current = componentCenterX < initialDividerX ? "left" : "right";
    }

    // 영역별 비례 스케일링: 스플릿선이 벽 역할 → 절대 넘어가지 않음
    let newX: number;
    let newW: number;
    const GAP = 4; // 스플릿선과의 최소 간격

    if (canvasSplitSideRef.current === "left") {
      // 왼쪽 영역: [0, currentDividerX - GAP]
      const initialZoneWidth = initialDividerX;
      const currentZoneWidth = Math.max(20, currentDividerX - GAP);
      const scale = initialZoneWidth > 0 ? currentZoneWidth / initialZoneWidth : 1;
      newX = origX * scale;
      newW = origW * scale;
      // 안전 클램핑: 왼쪽 영역을 절대 넘지 않음
      if (newX + newW > currentDividerX - GAP) {
        newW = currentDividerX - GAP - newX;
      }
    } else {
      // 오른쪽 영역: [currentDividerX + GAP, canvasWidth]
      const initialRightWidth = canvasWidth - initialDividerX;
      const currentRightWidth = Math.max(20, canvasWidth - currentDividerX - GAP);
      const scale = initialRightWidth > 0 ? currentRightWidth / initialRightWidth : 1;
      const rightOffset = origX - initialDividerX;
      newX = currentDividerX + GAP + rightOffset * scale;
      newW = origW * scale;
      // 안전 클램핑: 오른쪽 영역을 절대 넘지 않음
      if (newX < currentDividerX + GAP) newX = currentDividerX + GAP;
      if (newX + newW > canvasWidth) newW = canvasWidth - newX;
    }

    newX = Math.max(0, newX);
    newW = Math.max(20, newW);

    return { x: newX, w: newW };
  };

  const splitResult = calculateCanvasSplitX();
  const adjustedX = splitResult.x;
  const adjustedW = splitResult.w;
  const origW = size?.width || 200;
  const isSplitActive = canvasSplit.active && canvasSplit.scopeId && myScopeIdRef.current === canvasSplit.scopeId;

  // styleWithoutSize에서 left/top 제거 (캔버스 분할 조정값 덮어쓰기 방지)
  const { left: _styleLeft, top: _styleTop, ...safeStyleWithoutSize } = styleWithoutSize as any;

  // 수평 라벨 컴포넌트: position wrapper에서 border 제거 (내부 V2 컴포넌트가 기본 border 사용)
  const cleanedStyle = (isHorizLabel && needsExternalLabel)
    ? (() => {
        const { borderWidth: _bw, borderColor: _bc, borderStyle: _bs, border: _b, borderRadius: _br, ...rest } = safeStyleWithoutSize;
        return rest;
      })()
    : safeStyleWithoutSize;

  const componentStyle = {
    position: "absolute" as const,
    ...cleanedStyle,
    // left/top은 반드시 마지막에 (styleWithoutSize가 덮어쓰지 못하게)
    left: adjustedX,
    top: position?.y || 0,
    zIndex: position?.z || 1,
    width: isSplitActive ? adjustedW : (size?.width || 200),
    height: isTableSearchWidget ? "auto" : size?.height || 10,
    minHeight: isTableSearchWidget ? "48px" : undefined,
    overflow: (isSplitActive && adjustedW < origW) ? "hidden" : (labelOffset > 0 ? "visible" : undefined),
    willChange: canvasSplit.isDragging && isSplitActive ? "left, width" as const : undefined,
    transition: isSplitActive
      ? (canvasSplit.isDragging ? "none" : "left 0.15s ease-out, width 0.15s ease-out")
      : undefined,
  };

  // 스플릿 조정된 컴포넌트 객체 캐싱 (드래그 끝난 후 최종 렌더링용)
  const splitAdjustedComponent = React.useMemo(() => {
    if (isSplitActive && adjustedW !== origW) {
      return { ...component, size: { ...(component as any).size, width: Math.round(adjustedW) } };
    }
    return component;
  }, [component, isSplitActive, adjustedW, origW]);

  // 드래그 중 DOM 직접 조작 (React 리렌더 없이 매 프레임 업데이트)
  const elRef = React.useRef<HTMLDivElement>(null);
  React.useEffect(() => {
    const compType = (component as any).componentType || "";
    if (type === "component" && compType === "v2-split-line") return;

    const unsubscribe = canvasSplitSubscribeDom((snap) => {
      if (!snap.isDragging || !snap.active || !snap.scopeId) return;
      if (myScopeIdRef.current !== snap.scopeId) return;
      const el = elRef.current;
      if (!el) return;

      const origX = position?.x || 0;
      const oW = size?.width || 200;
      const { initialDividerX, currentDividerX, canvasWidth } = snap;
      const delta = currentDividerX - initialDividerX;
      if (Math.abs(delta) < 1) return;

      if (canvasSplitSideRef.current === null) {
        canvasSplitSideRef.current = (origX + oW / 2) < initialDividerX ? "left" : "right";
      }

      const GAP = 4;
      let nx: number, nw: number;
      if (canvasSplitSideRef.current === "left") {
        const scale = initialDividerX > 0 ? Math.max(20, currentDividerX - GAP) / initialDividerX : 1;
        nx = origX * scale;
        nw = oW * scale;
        if (nx + nw > currentDividerX - GAP) nw = currentDividerX - GAP - nx;
      } else {
        const irw = canvasWidth - initialDividerX;
        const crw = Math.max(20, canvasWidth - currentDividerX - GAP);
        const scale = irw > 0 ? crw / irw : 1;
        nx = currentDividerX + GAP + (origX - initialDividerX) * scale;
        nw = oW * scale;
        if (nx < currentDividerX + GAP) nx = currentDividerX + GAP;
        if (nx + nw > canvasWidth) nw = canvasWidth - nx;
      }
      nx = Math.max(0, nx);
      nw = Math.max(20, nw);

      el.style.left = `${nx}px`;
      el.style.width = `${Math.round(nw)}px`;
      el.style.overflow = nw < oW ? "hidden" : "";
    });
    return unsubscribe;
  }, [component.id, position?.x, size?.width, type]);

  // needsExternalLabel, isHorizLabel, labelText, labelGapValue는 위에서 선언됨

  const externalLabelComponent = needsExternalLabel ? (
    <label
      htmlFor={component.id}
      className="text-sm font-medium leading-none"
      style={{
        fontSize: style?.labelFontSize || "14px",
        color: getAdaptiveLabelColor(style?.labelColor),
        fontWeight: style?.labelFontWeight || "500",
        ...(isHorizLabel ? { whiteSpace: "nowrap" as const, display: "flex", alignItems: "center" } : {}),
        ...(labelPos === "bottom" ? { marginTop: style?.labelMarginBottom || "4px" } : {}),
      }}
    >
      {labelText}{((component as any).required || (component as any).componentConfig?.required || isColumnRequiredByMeta((component as any).tableName, (component as any).columnName)) && (
        <span className="text-orange-500">*</span>
      )}
    </label>
  ) : null;

  const componentToRender = needsExternalLabel
    ? {
        ...splitAdjustedComponent,
        style: {
          ...splitAdjustedComponent.style,
          labelDisplay: false,
          labelPosition: "top" as const,
          ...(isHorizLabel ? { 
            width: "100%", 
            height: "100%",
            borderWidth: undefined,
            borderColor: undefined,
            borderStyle: undefined,
            border: undefined,
            borderRadius: undefined,
          } : {}),
        },
        ...(isHorizLabel ? {
          size: {
            ...splitAdjustedComponent.size,
            width: undefined as unknown as number,
          },
        } : {}),
      }
    : splitAdjustedComponent;

  return (
    <>
      <div ref={elRef} id={`interactive-${component.id}`} className="absolute" style={componentStyle}>
        {needsExternalLabel ? (
          isHorizLabel ? (
            <div style={{ position: "relative", width: "100%", height: "100%" }}>
              <label
                htmlFor={component.id}
                className="text-sm font-medium leading-none"
                style={{
                  position: "absolute",
                  top: "50%",
                  transform: "translateY(-50%)",
                  ...(labelPos === "left"
                    ? { right: "100%", marginRight: labelGapValue }
                    : { left: "100%", marginLeft: labelGapValue }),
                  fontSize: style?.labelFontSize || "14px",
                  color: getAdaptiveLabelColor(style?.labelColor),
                  fontWeight: style?.labelFontWeight || "500",
                  whiteSpace: "nowrap",
                }}
              >
                {labelText}{((component as any).required || (component as any).componentConfig?.required || isColumnRequiredByMeta((component as any).tableName, (component as any).columnName)) && (
                  <span className="text-orange-500">*</span>
                )}
              </label>
              <div style={{ width: "100%", height: "100%" }}>
                {renderInteractiveWidget(componentToRender)}
              </div>
            </div>
          ) : (
            <div
              style={{
                display: "flex",
                flexDirection: "column-reverse",
                width: "100%",
                height: "100%",
              }}
            >
              {externalLabelComponent}
              <div style={{ flex: 1, minWidth: 0 }}>
                {renderInteractiveWidget(componentToRender)}
              </div>
            </div>
          )
        ) : (
          renderInteractiveWidget(componentToRender)
        )}
      </div>

      {/* 팝업 화면 렌더링 */}
      {popupScreen && (
        <Dialog open={!!popupScreen} onOpenChange={() => setPopupScreen(null)}>
          <DialogContent
            className="max-w-none overflow-hidden p-0"
            style={{
              width: popupScreen.size === "small" ? "600px" : popupScreen.size === "large" ? "1400px" : "1000px",
              height: "800px",
              maxWidth: "95vw",
              maxHeight: "90vh",
            }}
          >
            <DialogHeader>
              <DialogTitle>{popupScreen.title}</DialogTitle>
            </DialogHeader>

            {popupLoading ? (
              <div className="flex items-center justify-center p-8">
                <div className="text-muted-foreground">로딩 중...</div>
              </div>
            ) : (
              <div
                className="relative overflow-auto"
                style={{
                  width: popupScreenResolution?.width || 1200,
                  height: popupScreenResolution?.height || 600,
                  maxWidth: "100%",
                  maxHeight: "70vh",
                }}
              >
                {popupLayout.map((popupComponent) => (
                  <InteractiveScreenViewerDynamic
                    key={popupComponent.id}
                    component={popupComponent}
                    allComponents={popupLayout}
                    formData={popupFormData}
                    onFormDataChange={(fieldName, value) => {
                      setPopupFormData((prev) => ({ ...prev, [fieldName]: value }));
                    }}
                    screenInfo={popupScreenInfo}
                  />
                ))}
              </div>
            )}
          </DialogContent>
        </Dialog>
      )}
    </>
  );
};

// 기존 InteractiveScreenViewer와의 호환성을 위한 export
export { InteractiveScreenViewerDynamic as InteractiveScreenViewer };

InteractiveScreenViewerDynamic.displayName = "InteractiveScreenViewerDynamic";
