"use client";

import React from "react";
import { ComponentData } from "@/types/screen";
import { DynamicLayoutRenderer } from "./DynamicLayoutRenderer";
import { ComponentRegistry } from "./ComponentRegistry";
import { filterDOMProps } from "@/lib/utils/domPropsFilter";

// 통합 폼 시스템 import
import { useV2FormOptional } from "@/components/v2/V2FormContext";
import { apiClient } from "@/lib/api/client";

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

// 컬럼 메타데이터 캐시 (테이블명 → 컬럼 설정 맵)
export const columnMetaCache: Record<string, Record<string, any>> = {};
const columnMetaLoading: Record<string, Promise<void>> = {};
const columnMetaTimestamp: Record<string, number> = {};
const CACHE_TTL_MS = 5000;

export function invalidateColumnMetaCache(tableName?: string): void {
  if (tableName) {
    delete columnMetaCache[tableName];
    delete columnMetaLoading[tableName];
    delete columnMetaTimestamp[tableName];
  } else {
    for (const key of Object.keys(columnMetaCache)) delete columnMetaCache[key];
    for (const key of Object.keys(columnMetaLoading)) delete columnMetaLoading[key];
    for (const key of Object.keys(columnMetaTimestamp)) delete columnMetaTimestamp[key];
  }
}

async function loadColumnMeta(tableName: string, forceReload = false): Promise<void> {
  const now = Date.now();
  const isStale = columnMetaTimestamp[tableName] && (now - columnMetaTimestamp[tableName] > CACHE_TTL_MS);

  if (!forceReload && !isStale && columnMetaCache[tableName]) return;

  if (forceReload || isStale) {
    delete columnMetaCache[tableName];
    delete columnMetaLoading[tableName];
  }

  if (columnMetaLoading[tableName]) {
    await columnMetaLoading[tableName];
    return;
  }

  columnMetaLoading[tableName] = (async () => {
    try {
      const response = await apiClient.get(`/table-management/tables/${tableName}/columns?size=1000`);
      const data = response.data.data || response.data;
      const columns = data.columns || data || [];
      const map: Record<string, any> = {};
      for (const col of columns) {
        const name = col.column_name || col.columnName;
        if (name) map[name] = col;
      }
      columnMetaCache[tableName] = map;
      columnMetaTimestamp[tableName] = Date.now();
    } catch (e) {
      console.error(`[columnMeta] ${tableName} 로드 실패:`, e);
      columnMetaCache[tableName] = {};
    } finally {
      delete columnMetaLoading[tableName];
    }
  })();

  await columnMetaLoading[tableName];
}

// 테이블 타입관리 NOT NULL 기반 필수 여부 판단
export function isColumnRequiredByMeta(tableName?: string, columnName?: string): boolean {
  if (!tableName || !columnName) return false;
  const meta = columnMetaCache[tableName]?.[columnName];
  if (!meta) return false;
  const nullable = meta.is_nullable || meta.isNullable;
  return nullable === "NO" || nullable === "N";
}

// table_type_columns 기반 componentConfig 병합 (DB input_type 우선 적용)
function mergeColumnMeta(tableName: string | undefined, columnName: string | undefined, componentConfig: any): any {
  if (!tableName || !columnName) return componentConfig;

  const meta = columnMetaCache[tableName]?.[columnName];
  if (!meta) return componentConfig;

  const rawType = meta.input_type || meta.inputType;
  const dbInputType = rawType === "direct" || rawType === "auto" ? undefined : rawType;
  if (!dbInputType) return componentConfig;

  const merged = { ...componentConfig };
  const savedFieldType = merged.fieldType;

  // savedFieldType이 있고 DB와 같으면 변경 불필요
  if (savedFieldType && savedFieldType === dbInputType) return merged;
  // savedFieldType이 있고 DB와 다르면 — 사용자가 V2FieldConfigPanel에서 설정한 값 존중
  if (savedFieldType) return merged;

  // savedFieldType이 없으면: DB input_type 기준으로 동기화
  // 기존 overrides의 source/inputType이 DB와 불일치하면 덮어씀
  if (dbInputType === "entity") {
    const refTable = meta.reference_table || meta.referenceTable;
    const refColumn = meta.reference_column || meta.referenceColumn;
    const rawDisplayCol = meta.display_column || meta.displayColumn;
    const displayCol = rawDisplayCol && rawDisplayCol !== "none" && rawDisplayCol !== "" ? rawDisplayCol : undefined;
    if (refTable) {
      merged.source = "entity";
      merged.entityTable = refTable;
      merged.entityValueColumn = refColumn || "id";
      // 화면 설정에 이미 entityLabelColumn이 있으면 유지, 없으면 DB 값 또는 기본값 사용
      if (!merged.entityLabelColumn) {
        merged.entityLabelColumn = displayCol || "name";
      }
      merged.fieldType = "entity";
      merged.inputType = "entity";
    }
  } else if (dbInputType === "category") {
    merged.source = "category";
    merged.fieldType = "category";
    merged.inputType = "category";
  } else if (dbInputType === "select") {
    if (!merged.source || merged.source === "category" || merged.source === "entity") {
      merged.source = "static";
    }
    const detail =
      typeof meta.detail_settings === "string" ? JSON.parse(meta.detail_settings || "{}") : meta.detail_settings || {};
    if (detail.options && !merged.options?.length) {
      merged.options = detail.options;
    }
    merged.fieldType = "select";
    merged.inputType = "select";
  } else {
    // text, number, textarea 등 input 계열 — 카테고리 잔류 속성 제거
    merged.fieldType = dbInputType;
    merged.inputType = dbInputType;
    delete merged.source;
  }

  return merged;
}

// 컴포넌트 렌더러 인터페이스
export interface ComponentRenderer {
  (props: {
    component: ComponentData;
    isSelected?: boolean;
    isInteractive?: boolean;
    formData?: Record<string, any>;
    originalData?: Record<string, any>; // 부분 업데이트용 원본 데이터
    onFormDataChange?: (fieldName: string, value: any) => void;
    onClick?: (e?: React.MouseEvent) => void;
    onDragStart?: (e: React.DragEvent) => void;
    onDragEnd?: () => void;
    children?: React.ReactNode;
    onZoneComponentDrop?: (e: React.DragEvent, zoneId: string, layoutId: string) => void;
    onZoneClick?: (zoneId: string) => void;
    // 버튼 액션을 위한 추가 props
    screenId?: number;
    tableName?: string;
    onRefresh?: () => void;
    onClose?: () => void;
    // 테이블 선택된 행 정보 (다중 선택 액션용)
    selectedRows?: any[];
    selectedRowsData?: any[];
    onSelectedRowsChange?: (
      selectedRows: any[],
      selectedRowsData: any[],
      sortBy?: string,
      sortOrder?: "asc" | "desc",
      columnOrder?: string[],
      tableDisplayData?: any[],
    ) => void;
    // 테이블 정렬 정보 (엑셀 다운로드용)
    sortBy?: string;
    sortOrder?: "asc" | "desc";
    tableDisplayData?: any[]; // 🆕 화면 표시 데이터
    // 플로우 선택된 데이터 정보 (플로우 위젯 선택 액션용)
    flowSelectedData?: any[];
    flowSelectedStepId?: number | null;
    onFlowSelectedDataChange?: (selectedData: any[], stepId: number | null) => void;
    // 테이블 새로고침 키
    refreshKey?: number;
    // 편집 모드
    mode?: "view" | "edit";
    // 설정 변경 핸들러 (상세설정과 연동)
    onConfigChange?: (config: any) => void;
    [key: string]: any;
  }): React.ReactElement;
}

// 레거시 렌더러 레지스트리 (기존 컴포넌트들용)
class LegacyComponentRegistry {
  private renderers: Map<string, ComponentRenderer> = new Map();

  // 컴포넌트 렌더러 등록
  register(componentType: string, renderer: ComponentRenderer) {
    this.renderers.set(componentType, renderer);
  }

  // 컴포넌트 렌더러 조회
  get(componentType: string): ComponentRenderer | undefined {
    return this.renderers.get(componentType);
  }

  // 등록된 모든 컴포넌트 타입 조회
  getRegisteredTypes(): string[] {
    return Array.from(this.renderers.keys());
  }

  // 컴포넌트 타입이 등록되어 있는지 확인
  has(componentType: string): boolean {
    const result = this.renderers.has(componentType);
    return result;
  }
}

// 전역 레거시 레지스트리 인스턴스
export const legacyComponentRegistry = new LegacyComponentRegistry();

// 하위 호환성을 위한 기존 이름 유지
export const componentRegistry = legacyComponentRegistry;

// 동적 컴포넌트 렌더러 컴포넌트
export interface DynamicComponentRendererProps {
  component: ComponentData;
  isSelected?: boolean;
  isPreview?: boolean; // 반응형 모드 플래그
  isDesignMode?: boolean; // 디자인 모드 여부 (false일 때 데이터 로드)
  onClick?: (e?: React.MouseEvent) => void;
  onDragStart?: (e: React.DragEvent) => void;
  onDragEnd?: () => void;
  children?: React.ReactNode;
  // 폼 데이터 관련
  formData?: Record<string, any>;
  originalData?: Record<string, any>; // 부분 업데이트용 원본 데이터
  onFormDataChange?: (fieldName: string, value: any) => void;
  // 버튼 액션을 위한 추가 props
  screenId?: number;
  tableName?: string;
  menuId?: number; // 🆕 메뉴 ID (카테고리 관리 등에 필요)
  menuObjid?: number; // 🆕 메뉴 OBJID (메뉴 스코프 - 카테고리/채번)
  selectedScreen?: any; // 🆕 화면 정보 전체 (menuId 등 추출용)
  userId?: string; // 🆕 현재 사용자 ID
  userName?: string; // 🆕 현재 사용자 이름
  companyCode?: string; // 🆕 현재 사용자의 회사 코드
  onRefresh?: () => void;
  onClose?: () => void;
  onSave?: () => Promise<void>; // 🆕 EditModal의 handleSave 콜백
  // 테이블 선택된 행 정보 (다중 선택 액션용)
  selectedRows?: any[];
  // 🆕 그룹 데이터 (EditModal → ModalRepeaterTable)
  groupedData?: Record<string, any>[];
  // 🆕 비활성화할 필드 목록 (EditModal → 각 컴포넌트)
  disabledFields?: string[];
  selectedRowsData?: any[];
  onSelectedRowsChange?: (
    selectedRows: any[],
    selectedRowsData: any[],
    sortBy?: string,
    sortOrder?: "asc" | "desc",
    columnOrder?: string[],
    tableDisplayData?: any[],
  ) => void;
  // 테이블 정렬 정보 (엑셀 다운로드용)
  sortBy?: string;
  sortOrder?: "asc" | "desc";
  columnOrder?: string[];
  tableDisplayData?: any[]; // 🆕 화면 표시 데이터
  // 플로우 선택된 데이터 정보 (플로우 위젯 선택 액션용)
  flowSelectedData?: any[];
  // 🆕 컴포넌트 업데이트 콜백 (탭 내부 컴포넌트 위치 조정 등)
  onUpdateComponent?: (updatedComponent: any) => void;
  // 🆕 탭 내부 컴포넌트 선택 콜백
  onSelectTabComponent?: (tabId: string, compId: string, comp: any) => void;
  selectedTabComponentId?: string;
  // 🆕 분할 패널 내부 컴포넌트 선택 콜백
  onSelectPanelComponent?: (panelSide: "left" | "right", compId: string, comp: any) => void;
  selectedPanelComponentId?: string;
  // 중첩된 분할패널 내부 컴포넌트 선택 콜백 (탭 안의 분할패널)
  onNestedPanelSelect?: (splitPanelId: string, panelSide: "left" | "right", compId: string, comp: any) => void;
  flowSelectedStepId?: number | null;
  onFlowSelectedDataChange?: (selectedData: any[], stepId: number | null) => void;
  // 테이블 새로고침 키
  refreshKey?: number;
  // 플로우 새로고침 키
  flowRefreshKey?: number;
  onFlowRefresh?: () => void;
  // 편집 모드
  mode?: "view" | "edit";
  // 모달 내에서 렌더링 여부
  isInModal?: boolean;
  // 탭 관련 정보 (탭 내부의 컴포넌트에서 사용)
  parentTabId?: string; // 부모 탭 ID
  parentTabsComponentId?: string; // 부모 탭 컴포넌트 ID
  // 🆕 조건부 비활성화 상태
  conditionalDisabled?: boolean;
  [key: string]: any;
}

export const DynamicComponentRenderer: React.FC<DynamicComponentRendererProps> = ({
  component,
  isSelected = false,
  isPreview = false,
  onClick,
  onDragStart,
  onDragEnd,
  children,
  ...props
}) => {
  // 컬럼 메타데이터 로드 트리거 (TTL 기반 자동 갱신)
  const screenTableName = props.tableName || (component as any).tableName;
  const [metaVersion, forceUpdate] = React.useState(0);
  React.useEffect(() => {
    if (screenTableName) {
      loadColumnMeta(screenTableName).then(() => forceUpdate((v) => v + 1));
    }
  }, [screenTableName]);

  // table-columns-refresh 이벤트 수신 시 캐시 무효화 후 최신 메타 다시 로드
  React.useEffect(() => {
    const handler = () => {
      if (screenTableName) {
        invalidateColumnMetaCache(screenTableName);
        loadColumnMeta(screenTableName, true).then(() => forceUpdate((v) => v + 1));
      }
    };
    window.addEventListener("table-columns-refresh", handler);
    return () => window.removeEventListener("table-columns-refresh", handler);
  }, [screenTableName]);

  // 컴포넌트 타입 추출 - 새 시스템에서는 componentType 속성 사용, 레거시는 type 사용
  // 🆕 V2 레이아웃의 경우 url에서 컴포넌트 타입 추출 (예: "@/lib/registry/components/v2-input" → "v2-input")
  const extractTypeFromUrl = (url: string | undefined): string | undefined => {
    if (!url) return undefined;
    // url의 마지막 세그먼트를 컴포넌트 타입으로 사용
    const segments = url.split("/");
    return segments[segments.length - 1];
  };

  const rawComponentType =
    (component as any).componentType || component.type || extractTypeFromUrl((component as any).url);

  // 레거시 타입을 v2 컴포넌트로 매핑 (v2 컴포넌트가 없으면 원본 유지)
  const mapToV2ComponentType = (type: string | undefined): string | undefined => {
    if (!type) return type;

    // 이미 v2- 접두사가 있으면 그대로 반환
    if (type.startsWith("v2-")) return type;

    // 레거시 타입을 v2로 매핑 시도
    const v2Type = `v2-${type}`;
    // v2 버전이 등록되어 있는지 확인
    if (ComponentRegistry.hasComponent(v2Type)) {
      return v2Type;
    }
    // v2 버전이 없으면 원본 유지
    return type;
  };

  const mappedComponentType = mapToV2ComponentType(rawComponentType);

  // fieldType 기반 동적 컴포넌트 전환 (사용자 설정 > DB input_type > 기본값)
  const componentType = (() => {
    const configFieldType = (component as any).componentConfig?.fieldType;
    const fieldName = (component as any).columnName || (component as any).componentConfig?.fieldKey || (component as any).componentConfig?.columnName;
    const isEntityJoin = fieldName?.includes(".");
    const baseCol = isEntityJoin ? undefined : fieldName;
    const rawDbType = baseCol && screenTableName
      ? (columnMetaCache[screenTableName]?.[baseCol]?.input_type || columnMetaCache[screenTableName]?.[baseCol]?.inputType)
      : undefined;
    const dbInputType = rawDbType === "direct" || rawDbType === "auto" ? undefined : rawDbType;

    // 디버그 (division, unit 필드만) - 문제 확인 후 제거
    if (baseCol && (baseCol === "division" || baseCol === "unit")) {
      const result = configFieldType 
        ? (["text","number","password","textarea","slider","color","numbering"].includes(configFieldType) ? "v2-input" : "v2-select")
        : dbInputType 
          ? (["text","number","password","textarea","slider","color","numbering"].includes(dbInputType) ? "v2-input" : "v2-select")
          : mappedComponentType;
      const skipCat = dbInputType && !["category", "entity", "select"].includes(dbInputType);
      console.log(`[DCR] ${baseCol}: dbInputType=${dbInputType}, RESULT=${result}, skipCat=${skipCat}`);
    }

    // 사용자가 V2FieldConfigPanel에서 명시적으로 설정한 fieldType 최우선
    if (configFieldType) {
      if (["text", "number", "password", "textarea", "slider", "color", "numbering"].includes(configFieldType)) return "v2-input";
      if (["select", "category", "entity"].includes(configFieldType)) return "v2-select";
    }

    // componentConfig.fieldType 없으면 DB input_type 참조 (초기 로드 시)
    if (dbInputType) {
      if (["text", "number", "password", "textarea", "slider", "color", "numbering"].includes(dbInputType)) return "v2-input";
      if (["select", "category", "entity"].includes(dbInputType)) return "v2-select";
    }

    return mappedComponentType;
  })();

  // 🆕 조건부 렌더링 체크 (conditionalConfig)
  // componentConfig 또는 overrides에서 conditionalConfig를 가져와서 formData와 비교
  const conditionalConfig =
    (component as any).componentConfig?.conditionalConfig || (component as any).overrides?.conditionalConfig;

  // 조건부 렌더링 처리
  if (conditionalConfig?.enabled && props.formData) {
    const { field, operator, value, action } = conditionalConfig;
    const fieldValue = props.formData[field];

    // 조건 평가
    let conditionMet = false;
    switch (operator) {
      case "=":
      case "==":
      case "===":
        conditionMet = fieldValue === value;
        break;
      case "!=":
      case "!==":
        conditionMet = fieldValue !== value;
        break;
      case ">":
        conditionMet = Number(fieldValue) > Number(value);
        break;
      case "<":
        conditionMet = Number(fieldValue) < Number(value);
        break;
      case ">=":
        conditionMet = Number(fieldValue) >= Number(value);
        break;
      case "<=":
        conditionMet = Number(fieldValue) <= Number(value);
        break;
      case "contains":
        conditionMet = String(fieldValue || "").includes(String(value));
        break;
      case "empty":
        conditionMet = !fieldValue || fieldValue === "";
        break;
      case "notEmpty":
        conditionMet = !!fieldValue && fieldValue !== "";
        break;
      default:
        conditionMet = fieldValue === value;
    }

    // 액션에 따라 렌더링 결정
    if (action === "show" && !conditionMet) {
      return null;
    }
    if (action === "hide" && conditionMet) {
      return null;
    }
    // "enable"/"disable" 액션은 conditionalDisabled props로 전달
  }

  // 🆕 모든 v2- 컴포넌트는 ComponentRegistry에서 통합 처리
  // (v2-input, v2-select, v2-repeat-container 등 모두 동일하게 처리)

  // 🎯 카테고리 타입 우선 처리 (inputType 또는 webType 확인)
  // DB input_type이 "text" 등 비-카테고리로 변경된 경우 이 분기를 건너뜀
  const savedInputType = (component as any).componentConfig?.inputType || (component as any).inputType;
  const webType = (component as any).componentConfig?.webType;
  const tableName = (component as any).tableName;
  const columnName = (component as any).columnName;

  // DB input_type 확인: 데이터타입관리에서 변경한 최신 값이 레이아웃 저장값보다 우선
  const dbMetaForField = columnName && screenTableName && !columnName.includes(".")
    ? columnMetaCache[screenTableName]?.[columnName]
    : undefined;
  const dbFieldInputType = dbMetaForField
    ? (() => { const raw = dbMetaForField.input_type || dbMetaForField.inputType; return raw === "direct" || raw === "auto" ? undefined : raw; })()
    : undefined;
  // DB에서 확인된 타입이 있으면 그걸 사용, 없으면 저장된 값 사용
  const inputType = dbFieldInputType || savedInputType;
  // webType도 DB 값으로 대체 (레이아웃에 webType: "category" 하드코딩되어 있을 수 있음)
  const effectiveWebType = dbFieldInputType || webType;

  const componentMode = (component as any).componentConfig?.mode || (component as any).config?.mode;
  const isMultipleSelect = (component as any).componentConfig?.multiple;
  const nonDropdownModes = ["radio", "check", "checkbox", "tag", "tagbox", "toggle", "swap", "combobox"];
  const isNonDropdownMode = componentMode && nonDropdownModes.includes(componentMode);
  const shouldUseV2Select =
    componentType === "select-basic" || componentType === "v2-select" || isNonDropdownMode || isMultipleSelect;

  // DB input_type이 비-카테고리(text 등)로 확인된 경우, 레이아웃에 category가 남아있어도 카테고리 분기 강제 스킵
  // dbFieldInputType이 있으면(캐시 로드됨) 그 값으로 판단, 없으면 기존 로직 유지
  const isDbConfirmedNonCategory = dbFieldInputType && !["category", "entity", "select"].includes(dbFieldInputType);

  if (!isDbConfirmedNonCategory && (inputType === "category" || effectiveWebType === "category") && tableName && columnName && shouldUseV2Select) {
    // V2SelectRenderer로 직접 렌더링 (카테고리 + 고급 모드)
    try {
      const { V2SelectRenderer } = require("@/lib/registry/components/v2-select/V2SelectRenderer");
      const fieldName = columnName || component.id;

      // 수평 라벨 감지
      const catLabelDisplay = component.style?.labelDisplay ?? (component as any).labelDisplay;
      const catLabelPosition = component.style?.labelPosition;
      const catLabelText =
        catLabelDisplay === true || catLabelDisplay === "true"
          ? component.style?.labelText || (component as any).label || component.componentConfig?.label
          : undefined;
      const catNeedsExternalHorizLabel = !!(
        catLabelText &&
        (catLabelPosition === "left" || catLabelPosition === "right")
      );

      const selectComponent = {
        ...component,
        componentConfig: {
          ...component.componentConfig,
          mode: componentMode || "dropdown",
          source: "category",
          categoryTable: tableName,
          categoryColumn: columnName,
        },
        tableName,
        columnName,
        inputType: "category",
        webType: "category",
      };

      const catStyle = catNeedsExternalHorizLabel
        ? {
            ...(component as any).style,
            labelDisplay: false,
            labelPosition: "top" as const,
            width: "100%",
            height: "100%",
            borderWidth: undefined,
            borderColor: undefined,
            borderStyle: undefined,
            border: undefined,
            borderRadius: undefined,
          }
        : (component as any).style;
      const catSize = catNeedsExternalHorizLabel
        ? { ...(component as any).size, width: undefined }
        : (component as any).size;

      const rendererProps = {
        component: selectComponent,
        formData: props.formData,
        onFormDataChange: props.onFormDataChange,
        isDesignMode: props.isDesignMode,
        isInteractive: props.isInteractive ?? !props.isDesignMode,
        tableName,
        style: catStyle,
        size: catSize,
      };

      const rendererInstance = new V2SelectRenderer(rendererProps);
      const renderedCatSelect = rendererInstance.render();

      if (catNeedsExternalHorizLabel) {
        const labelGap = component.style?.labelGap || "8px";
        const labelFontSize = component.style?.labelFontSize || "14px";
        const labelColor = getAdaptiveLabelColor(component.style?.labelColor);
        const labelFontWeight = component.style?.labelFontWeight || "500";
        const isRequired =
          component.required || (component as any).required || isColumnRequiredByMeta(tableName, columnName);
        const isLeft = catLabelPosition === "left";
        return (
          <div style={{ position: "relative", width: "100%", height: "100%" }}>
            <label
              style={{
                position: "absolute",
                top: "50%",
                transform: "translateY(-50%)",
                ...(isLeft ? { right: "100%", marginRight: labelGap } : { left: "100%", marginLeft: labelGap }),
                fontSize: labelFontSize,
                color: labelColor,
                fontWeight: labelFontWeight,
                whiteSpace: "nowrap",
              }}
              className="text-sm font-medium"
            >
              {catLabelText}
              {isRequired && <span className="ml-0.5 text-amber-500">*</span>}
            </label>
            <div style={{ width: "100%", height: "100%" }}>{renderedCatSelect}</div>
          </div>
        );
      }
      return renderedCatSelect;
    } catch (error) {
      console.error("❌ V2SelectRenderer 로드 실패:", error);
    }
  } else if (!isDbConfirmedNonCategory && (inputType === "category" || effectiveWebType === "category") && tableName && columnName) {
    try {
      const { CategorySelectComponent } = require("@/lib/registry/components/category-select/CategorySelectComponent");
      const fieldName = columnName || component.id;
      const currentValue = props.formData?.[fieldName] || "";

      const handleChange = (value: any) => {
        if (props.onFormDataChange) {
          props.onFormDataChange(fieldName, value);
        }
      };

      // 🆕 disabledFields 체크 + readonly 체크
      const isFieldDisabled = props.disabledFields?.includes(columnName) || (component as any).disabled;
      const isFieldReadonly = (component as any).readonly || (component as any).componentConfig?.readonly;

      // 🔧 높이 계산: component.size에서 height 추출
      const categorySize = (component as any).size;
      const categoryStyle = (component as any).style;
      const categoryLabel = (component as any).label;
      const categoryId = component.id;

      return (
        <CategorySelectComponent
          tableName={tableName}
          columnName={columnName}
          value={currentValue}
          onChange={handleChange}
          placeholder={component.componentConfig?.placeholder || "선택하세요"}
          required={(component as any).required}
          disabled={isFieldDisabled}
          readonly={isFieldReadonly}
          className="w-full"
          size={categorySize}
          style={categoryStyle}
          label={categoryLabel}
          id={categoryId}
          isDesignMode={props.isDesignMode}
        />
      );
    } catch (error) {
      console.error("❌ CategorySelectComponent 로드 실패:", error);
    }
  }

  // 레이아웃 컴포넌트 처리
  if (componentType === "layout") {
    // DOM 안전한 props만 전달
    const safeLayoutProps = filterDOMProps(props);

    return (
      <DynamicLayoutRenderer
        layout={component as any}
        allComponents={props.allComponents || []}
        isSelected={isSelected}
        onClick={onClick}
        onDragStart={onDragStart}
        onDragEnd={onDragEnd}
        onUpdateLayout={props.onUpdateLayout}
        // onComponentDrop 제거 - 일반 캔버스 드롭만 사용
        onZoneClick={props.onZoneClick}
        isInteractive={props.isInteractive}
        formData={props.formData}
        onFormDataChange={props.onFormDataChange}
        screenId={props.screenId}
        tableName={props.tableName}
        onRefresh={props.onRefresh}
        onClose={props.onClose}
        mode={props.mode}
        isInModal={props.isInModal}
        originalData={props.originalData}
        {...safeLayoutProps}
      />
    );
  }

  // 1. 새 컴포넌트 시스템에서 먼저 조회
  const newComponent = ComponentRegistry.getComponent(componentType);

  if (newComponent) {
    // 새 컴포넌트 시스템으로 렌더링
    try {
      const NewComponentRenderer = newComponent.component;
      if (NewComponentRenderer) {
        // React 전용 props들을 명시적으로 분리하고 DOM 안전한 props만 전달
        const {
          isInteractive,
          formData,
          onFormDataChange,
          tableName,
          menuId, // 🆕 메뉴 ID
          menuObjid, // 🆕 메뉴 OBJID (메뉴 스코프)
          selectedScreen, // 🆕 화면 정보
          onRefresh,
          onClose,
          onSave, // 🆕 EditModal의 handleSave 콜백
          screenId,
          userId, // 🆕 사용자 ID
          userName, // 🆕 사용자 이름
          companyCode, // 🆕 회사 코드
          mode,
          isInModal,
          originalData,
          allComponents,
          onUpdateLayout,
          onZoneClick,
          selectedRows,
          selectedRowsData,
          onSelectedRowsChange,
          sortBy, // 🆕 정렬 컬럼
          sortOrder, // 🆕 정렬 방향
          tableDisplayData, // 🆕 화면 표시 데이터
          flowSelectedData,
          flowSelectedStepId,
          onFlowSelectedDataChange,
          refreshKey,
          flowRefreshKey, // Added this
          onFlowRefresh, // Added this
          onConfigChange,
          isPreview,
          autoGeneration,
          disabledFields, // 🆕 비활성화 필드 목록
          ...restProps
        } = props;

        // DOM 안전한 props만 필터링
        const safeProps = filterDOMProps(restProps);

        // 컴포넌트의 columnName에 해당하는 formData 값 추출
        const fieldName =
          (component as any).columnName || (component as any).componentConfig?.columnName || component.id;

        // 다중 레코드를 다루는 컴포넌트는 배열 데이터로 초기화
        let currentValue;
        if (
          componentType === "modal-repeater-table" ||
          componentType === "repeat-screen-modal" ||
          componentType === "selected-items-detail-input"
        ) {
          // EditModal/ScreenModal에서 전달된 groupedData가 있으면 우선 사용
          currentValue = props.groupedData || formData?.[fieldName] || [];
        } else if (componentType === "v2-repeater") {
          // V2Repeater는 자체 데이터 관리 (groupedData는 메인 테이블 레코드이므로 사용하지 않음)
          currentValue = formData?.[fieldName] || [];
        } else {
          currentValue = formData?.[fieldName] || "";
        }

        // 🆕 V2 폼 시스템 연동 (Context가 있으면 사용, 없으면 null)
        // eslint-disable-next-line react-hooks/rules-of-hooks
        const v2FormContext = useV2FormOptional();

        // onChange 핸들러 - 컴포넌트 타입에 따라 다르게 처리
        // 🆕 V2 시스템과 레거시 시스템 모두에 전파
        const handleChange = (value: any) => {
          // autocomplete-search-input, entity-search-input은 자체적으로 onFormDataChange를 호출하므로 중복 저장 방지
          if (componentType === "autocomplete-search-input" || componentType === "entity-search-input") {
            return;
          }

          // React 이벤트 객체인 경우 값 추출
          let actualValue = value;
          if (value && typeof value === "object" && value.nativeEvent && value.target) {
            // SyntheticEvent인 경우 target.value 추출
            actualValue = value.target.value;
          }

          // 1. V2 폼 시스템에 전파 (있으면)
          if (v2FormContext) {
            v2FormContext.setValue(fieldName, actualValue);
          }

          // 2. 레거시 onFormDataChange 콜백도 호출 (호환성 유지)
          if (onFormDataChange) {
            // modal-repeater-table은 배열 데이터를 다룸
            if (componentType === "modal-repeater-table") {
              onFormDataChange(fieldName, actualValue);
            }
            // RepeaterInput 같은 복합 컴포넌트는 전체 데이터를 전달
            else if (componentType === "repeater-field-group" || componentType === "repeater") {
              onFormDataChange(fieldName, actualValue);
            } else {
              onFormDataChange(fieldName, actualValue);
            }
          }
        };

        // 렌더러 props 구성
        // 숨김 값 추출
        const hiddenValue = component.hidden || component.componentConfig?.hidden;

        // 숨김 처리: 인터랙티브 모드(실제 뷰)에서만 숨김, 디자인 모드에서는 표시
        if (hiddenValue && isInteractive) {
          return null;
        }

        const isRuntimeMode = !props.isDesignMode;
        const finalStyle: React.CSSProperties = {
          ...component.style,
          width: isRuntimeMode ? "100%" : component.size?.width ? `${component.size.width}px` : component.style?.width,
          height: isRuntimeMode
            ? "100%"
            : component.size?.height
              ? `${component.size.height}px`
              : component.style?.height,
        };

        // 🆕 엔티티 검색 컴포넌트는 componentConfig.tableName을 사용해야 함 (화면 테이블이 아닌 검색 대상 테이블)
        // 🆕 v2-input도 포함 (채번 규칙 조회 시 tableName 필요)
        const useConfigTableName =
          componentType === "entity-search-input" ||
          componentType === "autocomplete-search-input" ||
          componentType === "modal-repeater-table" ||
          componentType === "v2-input";

        // 🆕 v2-input 등의 라벨 표시 로직 (InteractiveScreenViewerDynamic과 동일한 부정형 체크)
        const labelDisplay = component.style?.labelDisplay ?? (component as any).labelDisplay;
        const effectiveLabel =
          labelDisplay !== false && labelDisplay !== "false"
            ? component.style?.labelText || (component as any).label || component.componentConfig?.label
            : undefined;

        // 🔧 수평 라벨(left/right) 감지 → 런타임에서만 외부 flex 컨테이너로 라벨 처리
        // 디자인 모드에서는 V2 컴포넌트가 자체적으로 라벨을 렌더링 (height 체인 문제 방지)
        const labelPosition = component.style?.labelPosition;
        const isV2Component = componentType?.startsWith("v2-");
        const needsExternalHorizLabel = !!(
          !props.isDesignMode &&
          isV2Component &&
          effectiveLabel &&
          (labelPosition === "left" || labelPosition === "right")
        );

        const mergedStyle = {
          ...component.style,
          width: finalStyle.width,
          height: finalStyle.height,
          ...(needsExternalHorizLabel
            ? {
                labelDisplay: false,
                labelPosition: "top" as const,
                width: "100%",
                borderWidth: undefined,
                borderColor: undefined,
                borderStyle: undefined,
                border: undefined,
                borderRadius: undefined,
              }
            : {}),
        };

        // 컬럼 메타데이터 기반 componentConfig 병합 (DB 최신 설정 우선)
        const isEntityJoinColumn = fieldName?.includes(".");
        const baseColumnName = isEntityJoinColumn ? undefined : fieldName;
        const rawMergedConfig = mergeColumnMeta(screenTableName, baseColumnName, component.componentConfig || {});

        // fieldType이 설정된 경우, source/inputType 보조 속성 자동 보완
        const mergedComponentConfig = (() => {
          const ft = rawMergedConfig?.fieldType;
          if (!ft) return rawMergedConfig;
          const patch: Record<string, any> = {};
          if (["select", "category", "entity"].includes(ft) && !rawMergedConfig.source) {
            patch.source = ft === "category" ? "category" : ft === "entity" ? "entity" : "static";
          }
          if (["text", "number", "password", "textarea", "slider", "color", "numbering"].includes(ft) && !rawMergedConfig.inputType) {
            patch.inputType = ft;
          }
          return Object.keys(patch).length > 0 ? { ...rawMergedConfig, ...patch } : rawMergedConfig;
        })();

        // NOT NULL 기반 필수 여부를 component.required에 반영
        const notNullRequired = isColumnRequiredByMeta(screenTableName, baseColumnName);
        const effectiveRequired = component.required || notNullRequired;

        // 엔티티 조인 컬럼은 런타임에서 readonly/disabled 강제 해제
        const effectiveComponent = isEntityJoinColumn
          ? { ...component, componentConfig: mergedComponentConfig, readonly: false, required: effectiveRequired }
          : { ...component, componentConfig: mergedComponentConfig, required: effectiveRequired };

        const rendererProps = {
          component: effectiveComponent,
          isSelected,
          onClick,
          onDragStart,
          onDragEnd,
          config: mergedComponentConfig,
          componentConfig: mergedComponentConfig,
          // componentConfig spread를 먼저 → 이후 명시적 속성이 override
          ...(mergedComponentConfig || {}),
          // size/position/style/label은 componentConfig spread 이후에 설정 (덮어쓰기 방지)
          size: needsExternalHorizLabel
            ? { ...(component.size || newComponent.defaultSize), width: undefined }
            : component.size || newComponent.defaultSize,
          position: component.position,
          style: mergedStyle,
          label: needsExternalHorizLabel ? undefined : effectiveLabel,
          // NOT NULL 메타데이터 포함된 필수 여부 (V2Hierarchy 등 직접 props.required 참조하는 컴포넌트용)
          required: effectiveRequired,
          // 🆕 V2 레이아웃에서 overrides에서 복원된 상위 레벨 속성들도 전달 (DB 메타데이터 우선)
          inputType:
            (baseColumnName && columnMetaCache[screenTableName || ""]?.[baseColumnName]?.input_type) ||
            (component as any).inputType ||
            mergedComponentConfig?.inputType,
          columnName: (component as any).columnName || component.componentConfig?.columnName,
          value: currentValue, // formData에서 추출한 현재 값 전달
          // 새로운 기능들 전달
          // 🆕 webTypeConfig.numberingRuleId가 있으면 autoGeneration으로 변환
          autoGeneration:
            component.autoGeneration ||
            component.componentConfig?.autoGeneration ||
            ((component as any).webTypeConfig?.numberingRuleId
              ? {
                  type: "numbering_rule" as const,
                  enabled: true,
                  options: {
                    numberingRuleId: (component as any).webTypeConfig.numberingRuleId,
                  },
                }
              : undefined),
          hidden: hiddenValue,
          // React 전용 props들은 직접 전달 (DOM에 전달되지 않음)
          isInteractive,
          formData,
          onFormDataChange,
          onChange: handleChange, // 개선된 onChange 핸들러 전달
          // 🆕 엔티티 검색 컴포넌트는 componentConfig.tableName 유지, 그 외는 화면 테이블명 사용
          // 🆕 component.tableName도 확인 (V2 레이아웃에서 overrides.tableName이 복원됨)
          tableName: useConfigTableName
            ? component.componentConfig?.tableName || (component as any).tableName || tableName
            : tableName,
          menuId, // 🆕 메뉴 ID
          menuObjid, // 🆕 메뉴 OBJID (메뉴 스코프)
          selectedScreen, // 🆕 화면 정보
          onRefresh,
          onClose,
          onSave, // 🆕 EditModal의 handleSave 콜백
          screenId,
          userId, // 🆕 사용자 ID
          userName, // 🆕 사용자 이름
          companyCode, // 🆕 회사 코드
          // 🆕 화면 모드 (edit/view)와 컴포넌트 UI 모드 구분
          screenMode: mode,
          // componentConfig.mode가 있으면 유지 (entity-search-input의 UI 모드)
          mode: component.componentConfig?.mode || mode,
          isInModal,
          readonly: isEntityJoinColumn ? false : component.readonly,
          disabled: isEntityJoinColumn ? false : disabledFields?.includes(fieldName) || component.readonly,
          originalData,
          allComponents,
          onUpdateLayout,
          onZoneClick,
          // 테이블 선택된 행 정보 전달
          selectedRows,
          selectedRowsData,
          onSelectedRowsChange,
          // 테이블 정렬 정보 전달
          sortBy,
          sortOrder,
          tableDisplayData, // 🆕 화면 표시 데이터
          // 플로우 선택된 데이터 정보 전달
          flowSelectedData,
          flowSelectedStepId,
          onFlowSelectedDataChange,
          // 설정 변경 핸들러 전달
          onConfigChange,
          refreshKey,
          // 플로우 새로고침 키
          flowRefreshKey,
          onFlowRefresh,
          // 반응형 모드 플래그 전달
          isPreview,
          // 디자인 모드 플래그 전달 - isPreview와 명확히 구분
          isDesignMode: props.isDesignMode !== undefined ? props.isDesignMode : false,
          // 🆕 그룹 데이터 전달 (EditModal → ConditionalContainer → ModalRepeaterTable)
          // Note: 이 props들은 DOM 요소에 전달되면 안 됨
          // 각 컴포넌트에서 명시적으로 destructure하여 사용해야 함
          groupedData: props.groupedData, // ✅ 언더스코어 제거하여 직접 전달
          _groupedData: props.groupedData, // 하위 호환성 유지
          // 🆕 UniversalFormModal용 initialData 전달
          // 우선순위: props.initialData > originalData > formData
          // 조건부 컨테이너에서 전달된 initialData가 있으면 그것을 사용
          _initialData:
            props.initialData || (originalData && Object.keys(originalData).length > 0 ? originalData : formData),
          _originalData: originalData,
          // 🆕 initialData도 직접 전달 (조건부 컨테이너 → 내부 컴포넌트)
          initialData: props.initialData,
          // 🆕 탭 관련 정보 전달 (탭 내부의 테이블 컴포넌트에서 사용)
          parentTabId: props.parentTabId,
          parentTabsComponentId: props.parentTabsComponentId,
          // 🆕 컴포넌트 업데이트 콜백 (탭 내부 컴포넌트 위치 조정 등)
          onUpdateComponent: props.onUpdateComponent,
          // 🆕 탭 내부 컴포넌트 선택 콜백
          onSelectTabComponent: props.onSelectTabComponent,
          selectedTabComponentId: props.selectedTabComponentId,
          // 🆕 분할 패널 내부 컴포넌트 선택 콜백
          onSelectPanelComponent: props.onSelectPanelComponent,
          selectedPanelComponentId: props.selectedPanelComponentId,
          onNestedPanelSelect: props.onNestedPanelSelect,
        };

        // 렌더러가 클래스인지 함수인지 확인
        const isClass =
          typeof NewComponentRenderer === "function" &&
          NewComponentRenderer.prototype &&
          NewComponentRenderer.prototype.render;

        let renderedElement: React.ReactElement;
        if (isClass) {
          const rendererInstance = new NewComponentRenderer(rendererProps);
          renderedElement = rendererInstance.render();
        } else {
          renderedElement = <NewComponentRenderer key={refreshKey} {...rendererProps} />;
        }

        // 수평 라벨 → 라벨을 컴포넌트 영역 바깥에 absolute 배치, 입력은 100% 채움
        if (needsExternalHorizLabel) {
          const labelGap = component.style?.labelGap || "8px";
          const labelFontSize = component.style?.labelFontSize || "14px";
          const labelColor = getAdaptiveLabelColor(component.style?.labelColor);
          const labelFontWeight = component.style?.labelFontWeight || "500";
          const isRequired = effectiveComponent.required || isColumnRequiredByMeta(screenTableName, baseColumnName);
          const isLeft = labelPosition === "left";

          return (
            <div style={{ position: "relative", width: "100%", height: "100%" }}>
              <label
                style={{
                  position: "absolute",
                  top: "50%",
                  transform: "translateY(-50%)",
                  ...(isLeft ? { right: "100%", marginRight: labelGap } : { left: "100%", marginLeft: labelGap }),
                  fontSize: labelFontSize,
                  color: labelColor,
                  fontWeight: labelFontWeight,
                  whiteSpace: "nowrap",
                }}
                className="text-sm font-medium"
              >
                {effectiveLabel}
                {isRequired && <span className="ml-0.5 text-amber-500">*</span>}
              </label>
              <div style={{ width: "100%", height: "100%" }}>{renderedElement}</div>
            </div>
          );
        }

        return renderedElement;
      }
    } catch (error) {
      console.error(`❌ 새 컴포넌트 렌더링 실패 (${componentType}):`, error);
    }
  }

  // 2. 레거시 시스템에서 조회
  const renderer = legacyComponentRegistry.get(componentType);

  if (!renderer) {
    console.error(`⚠️ 등록되지 않은 컴포넌트 타입: ${componentType}`, {
      component: component,
      componentId: component.id,
      componentLabel: component.label,
      componentType: componentType,
      originalType: component.type,
      originalComponentType: (component as any).componentType,
      componentConfig: component.componentConfig,
      webTypeConfig: (component as any).webTypeConfig,
      autoGeneration: (component as any).autoGeneration,
      availableNewComponents: ComponentRegistry.getAllComponents().map((c) => c.id),
      availableLegacyComponents: legacyComponentRegistry.getRegisteredTypes(),
    });

    // 폴백 렌더링 - 기본 플레이스홀더
    return (
      <div className="border-border bg-muted flex h-full w-full items-center justify-center rounded border-2 border-dashed p-4">
        <div className="text-center">
          <div className="text-muted-foreground mb-2 text-sm font-medium">{component.label || component.id}</div>
          <div className="text-muted-foreground/70 text-xs">미구현 컴포넌트: {componentType}</div>
        </div>
      </div>
    );
  }

  // 동적 렌더링 실행
  try {
    // 레거시 시스템에서도 DOM 안전한 props만 전달
    const safeLegacyProps = filterDOMProps(props);

    return renderer({
      component,
      isSelected,
      onClick,
      onDragStart,
      onDragEnd,
      children,
      // React 전용 props들은 명시적으로 전달 (레거시 컴포넌트가 필요한 경우)
      isInteractive: props.isInteractive,
      formData: props.formData,
      onFormDataChange: props.onFormDataChange,
      screenId: props.screenId,
      tableName: props.tableName,
      userId: props.userId, // 🆕 사용자 ID
      userName: props.userName, // 🆕 사용자 이름
      companyCode: props.companyCode, // 🆕 회사 코드
      onRefresh: props.onRefresh,
      onClose: props.onClose,
      mode: props.mode,
      isInModal: props.isInModal,
      originalData: props.originalData,
      onUpdateLayout: props.onUpdateLayout,
      onZoneClick: props.onZoneClick,
      onZoneComponentDrop: props.onZoneComponentDrop,
      allComponents: props.allComponents,
      // 테이블 선택된 행 정보 전달
      selectedRows: props.selectedRows,
      selectedRowsData: props.selectedRowsData,
      onSelectedRowsChange: props.onSelectedRowsChange,
      // 플로우 선택된 데이터 정보 전달
      flowSelectedData: props.flowSelectedData,
      flowSelectedStepId: props.flowSelectedStepId,
      onFlowSelectedDataChange: props.onFlowSelectedDataChange,
      refreshKey: props.refreshKey,
      // DOM 안전한 props들
      ...safeLegacyProps,
    });
  } catch (error) {
    console.error(`❌ 컴포넌트 렌더링 실패 (${componentType}):`, error);

    // 오류 발생 시 폴백 렌더링
    return (
      <div className="border-destructive/30 bg-destructive/10 flex h-full w-full items-center justify-center rounded border-2 p-4">
        <div className="text-center">
          <div className="text-destructive mb-2 text-sm font-medium">렌더링 오류</div>
          <div className="text-destructive/80 text-xs">
            {componentType}: {error instanceof Error ? error.message : "알 수 없는 오류"}
          </div>
        </div>
      </div>
    );
  }
};

export default DynamicComponentRenderer;
