"use client";

import React, { useState, useEffect, useMemo, useCallback, useRef } from "react";
import { useSearchParams } from "next/navigation";
import { ComponentRendererProps } from "@/types/component";
import { SelectedItemsDetailInputConfig, AdditionalFieldDefinition, ItemData, GroupEntry, DisplayItem } from "./types";
import { useModalDataStore, ModalDataItem } from "@/stores/modalDataStore";
import { Input } from "@/components/ui/input";
import { Textarea } from "@/components/ui/textarea";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Checkbox } from "@/components/ui/checkbox";
import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { formatNumber as centralFormatNumber } from "@/lib/formatting";
import {
  X,
  Check,
  Plus,
  Minus,
  Edit,
  Trash2,
  Search,
  Save,
  RefreshCw,
  AlertCircle,
  Info,
  Settings,
  ChevronDown,
  ChevronUp,
  ChevronRight,
  Copy,
  Download,
  Upload,
  ExternalLink,
  type LucideIcon,
} from "lucide-react";

const LUCIDE_ICON_MAP: Record<string, LucideIcon> = {
  X, Check, Plus, Minus, Edit, Trash2, Search, Save, RefreshCw,
  AlertCircle, Info, Settings, ChevronDown, ChevronUp, ChevronRight,
  Copy, Download, Upload, ExternalLink,
};
import { commonCodeApi } from "@/lib/api/commonCode";
import { cn } from "@/lib/utils";
import { isColumnRequiredByMeta } from "@/lib/registry/DynamicComponentRenderer";

export interface SelectedItemsDetailInputComponentProps extends ComponentRendererProps {
  config?: SelectedItemsDetailInputConfig;
}

/**
 * SelectedItemsDetailInput 컴포넌트
 * 선택된 항목들의 상세 정보를 입력하는 컴포넌트
 */
export const SelectedItemsDetailInputComponent: React.FC<SelectedItemsDetailInputComponentProps> = ({
  component,
  isDesignMode = false,
  isSelected = false,
  isInteractive = false,
  onClick,
  onDragStart,
  onDragEnd,
  config,
  className,
  style,
  formData,
  onFormDataChange,
  screenId,
  ...props
}) => {
  // 🆕 groupedData 추출 (DynamicComponentRenderer에서 전달)
  const groupedData = (props as any).groupedData || (props as any)._groupedData;
  // 🆕 URL 파라미터에서 dataSourceId 읽기
  const searchParams = useSearchParams();
  const urlDataSourceId = searchParams?.get("dataSourceId") || undefined;

  // 컴포넌트 설정
  const componentConfig = useMemo(
    () =>
      ({
        dataSourceId: component.id || "default",
        displayColumns: [],
        additionalFields: [],
        layout: "grid",
        inputMode: "inline", // 🆕 기본값
        showIndex: true,
        allowRemove: false,
        emptyMessage: "전달받은 데이터가 없습니다.",
        targetTable: "",
        ...config,
        ...component.config,
      }) as SelectedItemsDetailInputConfig,
    [config, component.config, component.id],
  );

  // 소스 테이블의 키 필드명
  // 우선순위: 1) config에서 명시적 설정 → 2) additionalFields에서 autoFillFrom:"id" 필드 감지 → 3) 하위 호환 "item_id"
  const sourceKeyField = useMemo(() => {
    // sourceKeyField는 config에서 직접 지정 (ConfigPanel 자동 감지에서 설정됨)
    return componentConfig.sourceKeyField || "item_id";
  }, [componentConfig.sourceKeyField]);

  // 🆕 dataSourceId 우선순위: URL 파라미터 > 컴포넌트 설정 > component.id
  const dataSourceId = useMemo(
    () => urlDataSourceId || componentConfig.dataSourceId || component.id || "default",
    [urlDataSourceId, componentConfig.dataSourceId, component.id],
  );

  // 중복 저장 방지 가드
  const isSavingRef = useRef(false);

  // 전체 레지스트리를 가져와서 컴포넌트 내부에서 필터링 (캐싱 문제 회피)
  const dataRegistry = useModalDataStore((state) => state.dataRegistry);
  const modalData = useMemo(() => dataRegistry[dataSourceId] || [], [dataRegistry, dataSourceId]);

  // 전체 dataRegistry를 사용 (모든 누적 데이터에 접근 가능)

  const updateItemData = useModalDataStore((state) => state.updateItemData);

  // 🆕 새로운 데이터 구조: 품목별로 여러 개의 상세 데이터
  const [items, setItems] = useState<ItemData[]>([]);

  // 🆕 입력 모드 상태 (modal 모드일 때 사용)
  const [isEditing, setIsEditing] = useState(false);
  const [editingItemId, setEditingItemId] = useState<string | null>(null); // 현재 편집 중인 품목 ID
  const [editingGroupId, setEditingGroupId] = useState<string | null>(null); // 현재 편집 중인 그룹 ID (레거시 호환)
  const [editingDetailId, setEditingDetailId] = useState<string | null>(null); // 현재 편집 중인 항목 ID (레거시 호환)

  // 🆕 그룹별 독립 편집 상태: { [groupId]: entryId }
  const [editingEntries, setEditingEntries] = useState<Record<string, string | null>>({});

  // 🆕 코드 카테고리별 옵션 캐싱
  const [codeOptions, setCodeOptions] = useState<Record<string, Array<{ label: string; value: string }>>>({});

  // 디버깅 로그 (제거됨)

  // 🆕 필드에 codeCategory가 있으면 자동으로 옵션 로드
  useEffect(() => {
    const loadCodeOptions = async () => {
      // code/category 타입 필드 + codeCategory가 있는 필드 모두 처리
      const codeFields = componentConfig.additionalFields?.filter(
        (field) => field.inputType === "code" || field.inputType === "category",
      );

      if (!codeFields || codeFields.length === 0) {
        return;
      }

      const newOptions: Record<string, Array<{ label: string; value: string }>> = { ...codeOptions };

      // 🆕 그룹별 sourceTable 매핑 구성
      const groups = componentConfig.fieldGroups || [];
      const groupSourceTableMap: Record<string, string> = {};
      groups.forEach((g) => {
        if (g.sourceTable) {
          groupSourceTableMap[g.id] = g.sourceTable;
        }
      });
      const defaultTargetTable = componentConfig.targetTable;

      // 테이블별 컬럼 메타데이터 캐시
      const tableColumnsCache: Record<string, any[]> = {};
      const getTableColumns = async (tableName: string) => {
        if (tableColumnsCache[tableName]) return tableColumnsCache[tableName];
        try {
          const { tableTypeApi } = await import("@/lib/api/screen");
          const columnsResponse = await tableTypeApi.getColumns(tableName);
          tableColumnsCache[tableName] = columnsResponse || [];
          return tableColumnsCache[tableName];
        } catch (error) {
          console.error(`❌ 테이블 컬럼 조회 실패 (${tableName}):`, error);
          return [];
        }
      };

      for (const field of codeFields) {
        if (newOptions[field.name]) {
          continue;
        }

        // 🆕 필드의 그룹 sourceTable 결정
        const fieldSourceTable = (field.groupId && groupSourceTableMap[field.groupId]) || defaultTargetTable;

        try {
          if (field.inputType === "category" && fieldSourceTable) {
            const { getCategoryValues } = await import("@/lib/api/tableCategoryValue");
            const response = await getCategoryValues(fieldSourceTable, field.name, false);

            if (response.success && response.data && response.data.length > 0) {
              newOptions[field.name] = response.data.map((item: any) => ({
                label: item.value_label || item.valueLabel,
                value: item.value_code || item.valueCode,
              }));
            } else {
            }
          } else if (field.inputType === "code") {
            let codeCategory = field.codeCategory;

            if (!codeCategory && fieldSourceTable) {
              const targetTableColumns = await getTableColumns(fieldSourceTable);
              if (targetTableColumns.length > 0) {
                const columnMeta = targetTableColumns.find(
                  (col: any) => (col.columnName || col.column_name) === field.name,
                );
                if (columnMeta) {
                  codeCategory = columnMeta.codeCategory || columnMeta.code_category;
                }
              }
            }

            if (!codeCategory) {
              continue;
            }

            const response = await commonCodeApi.options.getOptions(codeCategory);
            if (response.success && response.data) {
              newOptions[field.name] = response.data.map((opt) => ({
                label: opt.label,
                value: opt.value,
              }));
            }
          }
        } catch (error) {
          console.error(`❌ 옵션 로드 실패 (${field.name}):`, error);
        }
      }

      setCodeOptions(newOptions);
    };

    loadCodeOptions();
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [componentConfig.additionalFields, componentConfig.targetTable]);

  // 🆕 모달 데이터를 ItemData 구조로 변환 (그룹별 구조)
  useEffect(() => {
    // 🆕 수정 모드: groupedData 또는 formData에서 데이터 로드 (URL에 mode=edit이 있으면)
    const urlParams = new URLSearchParams(window.location.search);
    const mode = urlParams.get("mode");

    // 🔧 데이터 소스 우선순위: groupedData > formData (배열) > formData (객체)
    const sourceData = groupedData && Array.isArray(groupedData) && groupedData.length > 0
      ? groupedData
      : formData;

    if (mode === "edit" && sourceData) {
      const loadEditData = async () => {
        const isArray = Array.isArray(sourceData);
        const dataArray = isArray ? sourceData : [sourceData];

        if (dataArray.length === 0 || (dataArray.length === 1 && Object.keys(dataArray[0]).length === 0)) {
          return;
        }

        const groups = componentConfig.fieldGroups || [];
        const additionalFields = componentConfig.additionalFields || [];
        const firstRecord = dataArray[0];

        // 수정 모드: 모든 관련 테이블의 데이터를 API로 전체 로드
        // sourceData는 클릭한 1개 레코드만 포함할 수 있으므로, API로 전체를 다시 가져옴
        const editTableName = new URLSearchParams(window.location.search).get("tableName");
        const allTableData: Record<string, Record<string, any>[]> = {};

        // 동적 필터 구성: parentDataMapping의 targetField + sourceKeyField
        const editFilters: Record<string, any> = {};
        const parentMappings = componentConfig.parentDataMapping || [];
        parentMappings.forEach((mapping: any) => {
          if (mapping.targetField && firstRecord[mapping.targetField]) {
            editFilters[mapping.targetField] = firstRecord[mapping.targetField];
          }
        });
        if (firstRecord[sourceKeyField]) {
          editFilters[sourceKeyField] = firstRecord[sourceKeyField];
        }

        const hasRequiredKeys = Object.keys(editFilters).length >= 2;

        if (hasRequiredKeys) {
          try {
            const { dataApi } = await import("@/lib/api/data");
            // 모든 sourceTable의 데이터를 API로 전체 로드 (중복 테이블 제거)
            const allTables = groups
              .map((g) => g.sourceTable || editTableName)
              .filter((v, i, a) => v && a.indexOf(v) === i) as string[];

            for (const table of allTables) {
              const response = await dataApi.getTableData(table, {
                filters: editFilters,
                sortBy: "created_date",
                sortOrder: "desc",
              });
              if (response.data && response.data.length > 0) {
                allTableData[table] = response.data;
              }
            }
          } catch (err) {
            console.error("❌ 편집 데이터 전체 로드 실패:", err);
          }
        }

        const mainFieldGroups: Record<string, GroupEntry[]> = {};

        groups.forEach((group) => {
          const groupFields = additionalFields.filter((field: any) => field.groupId === group.id);

          if (groupFields.length === 0) {
            mainFieldGroups[group.id] = [];
            return;
          }

          // 이 그룹의 sourceTable 결정 → API에서 가져온 전체 데이터 사용
          const groupTable = group.sourceTable || editTableName || "";
          // 현재 테이블만 sourceData fallback 허용 (다른 테이블은 빈 배열 → id 크로스오염 방지)
          const isCurrentTable = !group.sourceTable || group.sourceTable === editTableName;
          const groupDataList = allTableData[groupTable] || (isCurrentTable ? dataArray : []);

          {
            // 모든 테이블 그룹: API에서 가져온 전체 레코드를 entry로 변환
            const entriesMap = new Map<string, GroupEntry>();

            groupDataList.forEach((record) => {
              const entryData: Record<string, any> = {};

              groupFields.forEach((field: any) => {
                let fieldValue = record[field.name];

                // 값이 없으면 autoFillFrom 로직 적용
                if ((fieldValue === undefined || fieldValue === null) && field.autoFillFrom) {
                  let src: any = null;

                  if (field.autoFillFromTable) {
                    const tableData = dataRegistry[field.autoFillFromTable];
                    if (tableData && tableData.length > 0) {
                      src = tableData[0].originalData || tableData[0];
                    } else {
                      src = record;
                    }
                  } else {
                    src = record;
                  }

                  if (src && src[field.autoFillFrom] !== undefined) {
                    fieldValue = src[field.autoFillFrom];
                  } else {
                    const possibleKeys = Object.keys(src || {}).filter((key) =>
                      key.endsWith(`_${field.autoFillFrom}`),
                    );
                    if (possibleKeys.length > 0) {
                      fieldValue = src[possibleKeys[0]];
                    }
                  }
                }

                if (fieldValue === undefined || fieldValue === null) {
                  if (field.defaultValue !== undefined) {
                    fieldValue = field.defaultValue;
                  } else if (field.type === "checkbox") {
                    fieldValue = false;
                  } else {
                    return;
                  }
                }

                // 날짜 타입이면 YYYY-MM-DD 형식으로 변환
                if (field.type === "date" || field.type === "datetime") {
                  const dateStr = String(fieldValue);
                  const match = dateStr.match(/^(\d{4})-(\d{2})-(\d{2})/);
                  if (match) {
                    const [, year, month, day] = match;
                    fieldValue = `${year}-${month}-${day}`;
                  }
                }

                entryData[field.name] = fieldValue;
              });

              const entryKey = JSON.stringify(entryData);
              if (!entriesMap.has(entryKey)) {
                entriesMap.set(entryKey, {
                  id: `${group.id}_entry_${entriesMap.size + 1}`,
                  // DB 레코드의 고유 id(UUID PK) 보존 → 수정 시 이 id로 UPDATE
                  _dbRecordId: record.id || null,
                  ...entryData,
                });
              }
            });

            mainFieldGroups[group.id] = Array.from(entriesMap.values());
          }
        });

        if (groups.length === 0) {
          mainFieldGroups["default"] = [];
        }

        const newItem: ItemData = {
          // 수정 모드: sourceKeyField를 우선 사용 (id는 가격레코드의 PK일 수 있음)
          id: String(firstRecord[sourceKeyField] || firstRecord.id || "edit"),
          originalData: firstRecord,
          fieldGroups: mainFieldGroups,
        };

        setItems([newItem]);
      };

      loadEditData();
      return;
    }

    // 생성 모드: modalData에서 데이터 로드
    if (modalData && modalData.length > 0) {

      // 🆕 각 품목마다 빈 fieldGroups 객체를 가진 ItemData 생성
      const groups = componentConfig.fieldGroups || [];
      const newItems: ItemData[] = modalData.map((item) => {
        const fieldGroups: Record<string, GroupEntry[]> = {};

        // 각 그룹에 대해 초기화 (maxEntries === 1이면 자동 1개 생성)
        groups.forEach((group) => {
          if (group.maxEntries === 1) {
            // 1:1 관계: 빈 entry 1개 자동 생성
            fieldGroups[group.id] = [{ id: `${group.id}_auto_1` }];
          } else {
            fieldGroups[group.id] = [];
          }
        });

        // 그룹이 없으면 기본 그룹 생성
        if (groups.length === 0) {
          fieldGroups["default"] = [];
        }

        // 🔧 modalData의 구조 확인: item.originalData가 있으면 그것을 사용, 없으면 item 자체를 사용
        const actualData = (item as any).originalData || item;

        return {
          id: String(item.id),
          originalData: actualData, // 🔧 실제 데이터 추출
          fieldGroups,
        };
      });

      setItems(newItems);
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [modalData, component.id, componentConfig.fieldGroups, formData, groupedData]); // groupedData 의존성 추가

  // 🆕 Cartesian Product 생성 함수 (items에서 모든 그룹의 조합을 생성)
  const generateCartesianProduct = useCallback(
    (itemsList: ItemData[]): Record<string, any>[] => {
      const allRecords: Record<string, any>[] = [];
      const groups = componentConfig.fieldGroups || [];
      const additionalFields = componentConfig.additionalFields || [];

      itemsList.forEach((item, itemIndex) => {
        // 각 그룹의 엔트리 배열들을 준비
        // 🔧 빈 엔트리 필터링: id만 있고 실제 필드 값이 없는 엔트리는 제외
        const groupEntriesArrays: GroupEntry[][] = groups.map((group) => {
          const entries = item.fieldGroups[group.id] || [];
          const groupFields = additionalFields.filter((f) => f.groupId === group.id);
          
          // 실제 필드 값이 하나라도 있는 엔트리만 포함
          return entries.filter((entry) => {
            const hasAnyFieldValue = groupFields.some((field) => {
              const value = entry[field.name];
              return value !== undefined && value !== null && value !== "";
            });
            
            return hasAnyFieldValue;
          });
        });

        // 🆕 모든 그룹이 비어있는지 확인
        const allGroupsEmpty = groupEntriesArrays.every((arr) => arr.length === 0);

        if (allGroupsEmpty) {
          // 디테일 데이터가 없어도 기본 레코드 생성 (품목-거래처 매핑 유지)
          const baseRecord: Record<string, any> = {};

          // sourceKeyField 자동 매핑 (item_id = originalData.id)
          if (sourceKeyField && item.originalData?.id) {
            baseRecord[sourceKeyField] = item.originalData.id;
          }

          // 나머지 autoFillFrom 필드 (sourceKeyField 제외)
          additionalFields.forEach((f) => {
            if (f.name !== sourceKeyField && f.autoFillFrom && item.originalData) {
              const value = item.originalData[f.autoFillFrom];
              if (value !== undefined && value !== null) {
                baseRecord[f.name] = value;
              }
            }
          });

          allRecords.push(baseRecord);
          return;
        }

        // Cartesian Product 재귀 함수
        const cartesian = (arrays: GroupEntry[][], currentIndex: number, currentCombination: Record<string, any>) => {
          if (currentIndex === arrays.length) {
            // 모든 그룹을 순회했으면 조합 완성
            allRecords.push({ ...currentCombination });
            return;
          }

          const currentGroupEntries = arrays[currentIndex];
          if (currentGroupEntries.length === 0) {
            // 🆕 현재 그룹에 데이터가 없으면 빈 조합으로 다음 그룹 진행
            // (그룹이 비어있어도 다른 그룹의 데이터로 레코드 생성)
            cartesian(arrays, currentIndex + 1, currentCombination);
            return;
          }

          // 현재 그룹의 각 엔트리마다 재귀
          currentGroupEntries.forEach((entry) => {
            const newCombination = { ...currentCombination };

            // 🆕 기존 레코드의 id가 있으면 포함 (UPDATE를 위해)
            if (entry.id) {
              newCombination.id = entry.id;
            }

            // 현재 그룹의 필드들을 조합에 추가
            const groupFields = additionalFields.filter((f) => f.groupId === groups[currentIndex].id);
            groupFields.forEach((field) => {
              if (entry[field.name] !== undefined) {
                newCombination[field.name] = entry[field.name];
              }
            });

            cartesian(arrays, currentIndex + 1, newCombination);
          });
        };

        // 재귀 시작
        cartesian(groupEntriesArrays, 0, {});
      });

      return allRecords;
    },
    [componentConfig.fieldGroups, componentConfig.additionalFields, sourceKeyField],
  );

  // 🆕 저장 요청 시에만 데이터 전달 (이벤트 리스너 방식)
  useEffect(() => {
    const handleSaveRequest = async (event: Event) => {
      // 중복 저장 방지
      // 항상 skipDefaultSave 설정 (buttonActions.ts의 이중 저장 방지)
      if (event instanceof CustomEvent && event.detail) {
        (event.detail as any).skipDefaultSave = true;
      }

      if (isSavingRef.current) return;
      isSavingRef.current = true;

      // component.id를 문자열로 안전하게 변환
      const componentKey = String(component.id || "selected_items");

      if (items.length === 0) {
        isSavingRef.current = false;
        return;
      }

      // parentDataMapping이 있으면 UPSERT API로 직접 저장
      const hasParentMapping = componentConfig.parentDataMapping && componentConfig.parentDataMapping.length > 0;

      if (hasParentMapping) {
        try {

          // 수정 모드 감지 (parentKeys 구성 전에 필요)
          const urlParams = typeof window !== "undefined" ? new URLSearchParams(window.location.search) : null;
          const urlEditMode = urlParams?.get("mode") === "edit";
          const dataHasDbId = items.some(item => !!item.originalData?.id);
          const isEditMode = urlEditMode || dataHasDbId;

          // 부모 키 추출 (parentDataMapping에서)
          const parentKeys: Record<string, any> = {};

          // formData 또는 items[0].originalData에서 부모 데이터 가져오기
          // formData가 배열이면 첫 번째 항목 사용
          let sourceData: any = formData;
          if (Array.isArray(formData) && formData.length > 0) {
            sourceData = formData[0];
          } else if (!formData) {
            sourceData = items[0]?.originalData || {};
          }

          componentConfig.parentDataMapping.forEach((mapping) => {
            let value: any;

            // 수정 모드: originalData의 targetField 값 우선 사용
            // 로드(editFilters)와 동일한 방식으로 FK 값을 가져와야
            // 백엔드에서 기존 레코드를 정확히 매칭하여 UPDATE 수행 가능
            if (isEditMode && items.length > 0 && items[0].originalData) {
              value = items[0].originalData[mapping.targetField];
            }

            // 신규 모드 또는 originalData에 값 없으면 기존 로직
            if (value === undefined || value === null) {
              value = getFieldValue(sourceData, mapping.sourceField);

              if ((value === undefined || value === null) && mapping.sourceTable) {
                const registryData = dataRegistry[mapping.sourceTable];
                if (registryData && registryData.length > 0) {
                  const registryItem = registryData[0].originalData || registryData[0];
                  value = registryItem[mapping.sourceField];
                }
              }
            }

            if (value !== undefined && value !== null) {
              parentKeys[mapping.targetField] = value;
            } else {
              console.warn(`⚠️ 부모 키 누락: ${mapping.sourceField} → ${mapping.targetField}`);
            }
          });

          // 🔒 parentKeys 유효성 검증 - 빈 값이 있으면 저장 중단
          const parentKeyValues = Object.values(parentKeys);
          const hasEmptyParentKey = parentKeyValues.length === 0 || 
            parentKeyValues.some(v => v === null || v === undefined || v === "");
          
          if (hasEmptyParentKey) {
            console.error("❌ parentKeys 비어있음:", parentKeys);
            window.dispatchEvent(
              new CustomEvent("formSaveError", {
                detail: { message: "부모 키 값이 비어있어 저장할 수 없습니다. 먼저 상위 데이터를 선택해주세요." },
              }),
            );

            // 🔧 기본 저장 건너뛰기 - event.detail 객체 직접 수정
            if (event instanceof CustomEvent && event.detail) {
              (event.detail as any).skipDefaultSave = true;
            }
            isSavingRef.current = false;
            return;
          }

          // targetTable 검증
          if (!componentConfig.targetTable) {
            window.dispatchEvent(
              new CustomEvent("formSaveError", {
                detail: { message: "대상 테이블이 설정되지 않았습니다." },
              }),
            );
            if (event instanceof CustomEvent && event.detail) {
              (event.detail as any).skipDefaultSave = true;
            }
            isSavingRef.current = false;
            return;
          }

          // 🔧 기본 저장 건너뛰기 설정 (UPSERT 전에!)
          if (event instanceof CustomEvent && event.detail) {
            (event.detail as any).skipDefaultSave = true;
          }

          const { dataApi } = await import("@/lib/api/data");
          const groups = componentConfig.fieldGroups || [];
          const additionalFields = componentConfig.additionalFields || [];
          const mainTable = componentConfig.targetTable!;

          console.log("[SelectedItemsDetailInput] 수정 모드 감지:", {
            urlEditMode,
            dataHasDbId,
            isEditMode,
            itemCount: items.length,
            firstItemId: items[0]?.originalData?.id,
          });

          // fieldGroup별 sourceTable 분류
          const groupsByTable = new Map<string, typeof groups>();
          groups.forEach((group) => {
            const table = group.sourceTable || mainTable;
            if (!groupsByTable.has(table)) {
              groupsByTable.set(table, []);
            }
            groupsByTable.get(table)!.push(group);
          });

          // 디테일 테이블이 있는지 확인 (mainTable과 다른 sourceTable)
          const detailTables = [...groupsByTable.keys()].filter((t) => t !== mainTable);
          const hasDetailTable = detailTables.length > 0;

          if (hasDetailTable) {
            // ============================================================
            // 2단계 저장: 메인 테이블 + 디테일 테이블 분리 저장
            // upsertGroupedRecords를 양쪽 모두 사용 (정확한 매칭 보장)
            // ============================================================
            const mainGroups = groupsByTable.get(mainTable) || [];

            for (const item of items) {
              // sourceKeyField 값 추출 (예: item_id 또는 customer_id)
              let sourceKeyValue: string | null = null;

              // 1순위: originalData에 sourceKeyField가 직접 있으면 사용 (수정 모드)
              if (item.originalData && item.originalData[sourceKeyField]) {
                sourceKeyValue = item.originalData[sourceKeyField];
              }

              // 2순위: 원본 데이터의 id를 sourceKeyField 값으로 사용 (신규 등록 모드)
              if (!sourceKeyValue && item.originalData) {
                sourceKeyValue = item.originalData.id || null;
              }

              if (!sourceKeyValue) {
                console.error(`❌ [2단계 저장] ${sourceKeyField}를 찾을 수 없음:`, item);
                continue;
              }

              // upsert 공통 parentKeys: parentMapping 키 + sourceKeyField (정확한 매칭)
              const itemParentKeys = { ...parentKeys, [sourceKeyField]: sourceKeyValue };

              // === Step 1: 메인 테이블(customer_item_mapping) 저장 ===
              // 여러 개의 매핑 레코드 지원 (거래처 품번/품명이 다중일 수 있음)
              const mappingRecords: Record<string, any>[] = [];
              mainGroups.forEach((group) => {
                const entries = item.fieldGroups[group.id] || [];
                const groupFields = additionalFields.filter((f) => f.groupId === group.id);

                entries.forEach((entry) => {
                  const record: Record<string, any> = {};
                  groupFields.forEach((field) => {
                    const val = entry[field.name];
                    if (val !== undefined && val !== null && val !== "") {
                      record[field.name] = val;
                    }
                  });
                  // 기존 DB 레코드의 고유 id(PK)가 있으면 포함 → 백엔드에서 이 id로 UPDATE
                  if (entry._dbRecordId) {
                    record.id = entry._dbRecordId;
                  }
                  // sourceKeyField는 정확한 sourceKeyValue 변수 사용 (autoFillFrom:"id" 오작동 방지)
                  record[sourceKeyField] = sourceKeyValue;
                  // 나머지 autoFillFrom 필드 처리
                  groupFields.forEach((field) => {
                    if (field.name !== sourceKeyField && field.autoFillFrom && item.originalData) {
                      const value = item.originalData[field.autoFillFrom];
                      if (value !== undefined && value !== null && !record[field.name]) {
                        record[field.name] = value;
                      }
                    }
                  });
                  mappingRecords.push(record);
                });
              });

              // 수정 모드이거나 레코드에 id(기존 DB PK)가 있으면 → 고아 삭제 (기존 레코드 교체)
              // 신규 등록이고 id 없으면 → 기존 레코드 건드리지 않음
              const mappingHasDbIds = mappingRecords.some((r) => !!r.id);
              const shouldDeleteOrphans = isEditMode || mappingHasDbIds;

              console.log(`[SelectedItemsDetailInput] ${mainTable} 저장:`, {
                isEditMode,
                mappingHasDbIds,
                shouldDeleteOrphans,
                recordCount: mappingRecords.length,
                recordIds: mappingRecords.map(r => r.id || "NEW"),
                parentKeys: itemParentKeys,
              });

              // 저장된 매핑 ID를 추적 (디테일 테이블에 mapping_id 주입용)
              let savedMappingIds: string[] = [];
              try {
                const mappingResult = await dataApi.upsertGroupedRecords(
                  mainTable,
                  itemParentKeys,
                  mappingRecords,
                  { deleteOrphans: shouldDeleteOrphans },
                );
                // 백엔드에서 반환된 저장된 레코드 ID 목록
                if (mappingResult.success && mappingResult.savedIds) {
                  savedMappingIds = mappingResult.savedIds;
                  console.log(`✅ ${mainTable} 저장 완료, savedIds:`, savedMappingIds);
                }
              } catch (err) {
                console.error(`❌ ${mainTable} 저장 실패:`, err);
              }

              // === Step 2: 디테일 테이블(customer_item_prices) 저장 ===
              for (const detailTable of detailTables) {
                const detailGroups = groupsByTable.get(detailTable) || [];
                const priceRecords: Record<string, any>[] = [];

                detailGroups.forEach((group) => {
                  const entries = item.fieldGroups[group.id] || [];
                  const groupFields = additionalFields.filter((f) => f.groupId === group.id);

                  entries.forEach((entry) => {
                    // 사용자가 실제 입력한 값이 있는지 확인
                    // select/category 필드는 항상 기본값이 있으므로 제외하고 판별
                    const hasUserInput = groupFields.some((field) => {
                      // 셀렉트/카테고리 필드는 기본값이 자동 설정되므로 무시
                      if (field.type === "select" || field.inputType === "code" || field.inputType === "category") {
                        return false;
                      }
                      const value = entry[field.name];
                      if (value === undefined || value === null || value === "") return false;
                      if (value === 0 || value === "0" || value === "0.00") return false;
                      return true;
                    });

                    if (hasUserInput) {
                      const priceRecord: Record<string, any> = {};
                      groupFields.forEach((field) => {
                        const val = entry[field.name];
                        if (val !== undefined && val !== null) {
                          priceRecord[field.name] = val;
                        }
                      });
                      // 기존 DB 레코드의 고유 id(PK)가 있으면 포함 → 백엔드에서 이 id로 UPDATE
                      if (entry._dbRecordId) {
                        priceRecord.id = entry._dbRecordId;
                      }
                      priceRecords.push(priceRecord);
                    }
                  });
                });

                // 빈 항목이라도 최소 레코드 생성 (우측 패널에 표시되도록)
                if (priceRecords.length === 0) {
                  // select/category 필드를 명시적 null로 설정 (DB DEFAULT 'KRW' 등 방지)
                  const emptyRecord: Record<string, any> = {};
                  const detailGroupFields = additionalFields.filter((f) =>
                    detailGroups.some((g) => g.id === f.groupId),
                  );
                  detailGroupFields.forEach((field) => {
                    if (field.type === "select" || field.inputType === "code" || field.inputType === "category") {
                      emptyRecord[field.name] = null;
                    }
                  });
                  priceRecords.push(emptyRecord);
                }

                // Step1에서 저장된 매핑 ID를 디테일 레코드에 주입
                // (customer_item_prices.mapping_id ← customer_item_mapping.id)
                if (savedMappingIds.length > 0) {
                  const mappingId = savedMappingIds[0]; // 일반적으로 1:N (매핑 1개 : 단가 N개)
                  priceRecords.forEach((record) => {
                    if (!record.mapping_id) {
                      record.mapping_id = mappingId;
                    }
                  });
                  console.log(`🔗 디테일 레코드에 mapping_id 주입: ${mappingId}`);
                }

                const priceHasDbIds = priceRecords.some((r) => !!r.id);
                const shouldDeleteDetailOrphans = isEditMode || priceHasDbIds;

                console.log(`[SelectedItemsDetailInput] ${detailTable} 저장:`, {
                  isEditMode,
                  priceHasDbIds,
                  shouldDeleteDetailOrphans,
                  recordCount: priceRecords.length,
                  recordIds: priceRecords.map(r => r.id || "NEW"),
                  parentKeys: itemParentKeys,
                });

                try {
                  const detailResult = await dataApi.upsertGroupedRecords(
                    detailTable,
                    itemParentKeys,
                    priceRecords,
                    { deleteOrphans: shouldDeleteDetailOrphans },
                  );

                  if (!detailResult.success) {
                    console.error(`❌ ${detailTable} 저장 실패:`, detailResult.error);
                  }
                } catch (err) {
                  console.error(`❌ ${detailTable} 오류:`, err);
                }
              }
            }

            // 저장 성공 이벤트 + 테이블 새로고침 (모든 아이템 저장 완료 후)
            window.dispatchEvent(
              new CustomEvent("formSaveSuccess", {
                detail: { message: "데이터가 저장되었습니다." },
              }),
            );
            // 분할 패널 우측 데이터 새로고침
            window.dispatchEvent(new CustomEvent("refreshTable"));
          } else {
            // ============================================================
            // 단일 테이블 저장 (기존 로직 - detailTable 없는 경우)
            // ============================================================
            const records = generateCartesianProduct(items);
            const singleHasDbIds = records.some((r) => !!r.id);
            const shouldDeleteSingleOrphans = isEditMode || singleHasDbIds;

            const result = await dataApi.upsertGroupedRecords(mainTable, parentKeys, records, { deleteOrphans: shouldDeleteSingleOrphans });

            if (result.success) {
              window.dispatchEvent(
                new CustomEvent("formSaveSuccess", {
                  detail: { message: "데이터가 저장되었습니다." },
                }),
              );
            } else {
              window.dispatchEvent(
                new CustomEvent("formSaveError", {
                  detail: { message: result.error || "데이터 저장 실패" },
                }),
              );
            }
          }
        } catch (error) {
          console.error("❌ UPSERT 오류:", error);
          window.dispatchEvent(
            new CustomEvent("formSaveError", {
              detail: { message: "데이터 저장 중 오류가 발생했습니다." },
            }),
          );

          // 🆕 오류 발생 시에도 기본 저장 건너뛰기 (중복 저장 방지)
          if (event instanceof CustomEvent && event.detail) {
            event.detail.skipDefaultSave = true;
          }
        } finally {
          // 저장 완료 후 가드 해제
          isSavingRef.current = false;
        }
      } else {
        // 생성 모드: 기존 로직
        if (event instanceof CustomEvent && event.detail) {
          event.detail.formData[componentKey] = items;
        }

        // 기존 onFormDataChange도 호출 (호환성)
        if (onFormDataChange) {
          onFormDataChange(componentKey, items);
        }
        isSavingRef.current = false;
      }
    };

    // 저장 버튼 클릭 시 데이터 수집
    window.addEventListener("beforeFormSave", handleSaveRequest as EventListener);

    return () => {
      window.removeEventListener("beforeFormSave", handleSaveRequest as EventListener);
    };
  }, [items, component.id, onFormDataChange, componentConfig, formData, generateCartesianProduct, dataRegistry]);

  // 스타일 계산
  const componentStyle: React.CSSProperties = {
    width: "100%",
    height: "100%",
    overflowY: "auto", // 항목이 많을 때 스크롤 지원
    ...component.style,
    ...style,
  };

  // 디자인 모드 스타일
  if (isDesignMode) {
    componentStyle.border = "1px dashed #cbd5e1";
    componentStyle.borderColor = isSelected ? "#3b82f6" : "#cbd5e1";
    componentStyle.padding = "16px";
    componentStyle.borderRadius = "8px";
  }

  // 이벤트 핸들러
  const handleClick = (e: React.MouseEvent) => {
    e.stopPropagation();
    onClick?.();
  };

  // 🆕 카테고리 코드 → 라벨명 변환 헬퍼
  const getOptionLabel = useCallback(
    (fieldName: string, valueCode: string): string => {
      const options = codeOptions[fieldName] || [];
      const matched = options.find((opt) => opt.value === valueCode);
      return matched?.label || valueCode || "";
    },
    [codeOptions],
  );

  // 🆕 실시간 단가 계산 함수 (라벨명 기반 - 회사별 코드 무관)
  const calculatePrice = useCallback(
    (entry: GroupEntry): number => {
      if (!componentConfig.autoCalculation) return 0;

      const { inputFields } = componentConfig.autoCalculation;

      // 기본 단가
      const basePrice = parseFloat(entry[inputFields.basePrice] || "0");
      if (basePrice === 0) return 0;

      let price = basePrice;

      // 1단계: 할인 적용 (라벨명으로 판단)
      const discountTypeCode = entry[inputFields.discountType];
      const discountTypeLabel = getOptionLabel("discount_type", discountTypeCode);
      const discountValue = parseFloat(entry[inputFields.discountValue] || "0");

      if (discountTypeLabel.includes("할인율") || discountTypeLabel.includes("%")) {
        // 할인율(%)
        price = price * (1 - discountValue / 100);
      } else if (discountTypeLabel.includes("할인금액") || discountTypeLabel.includes("금액")) {
        // 할인금액
        price = price - discountValue;
      }
      // "할인없음"이면 그대로

      // 2단계: 반올림 적용
      // rounding_type = 단위 (10원, 100원, 1000원)
      // rounding_unit_value = 방법 (반올림, 절삭, 올림, 반올림없음)
      const roundingTypeCode = entry[inputFields.roundingType];
      const roundingTypeLabel = getOptionLabel("rounding_type", roundingTypeCode);

      const roundingUnitCode = entry[inputFields.roundingUnit];
      const roundingUnitLabel = getOptionLabel("rounding_unit_value", roundingUnitCode);

      // roundingType 라벨에서 단위 숫자 추출 (예: "10원" → 10, "1000원" → 1000)
      const unitMatch = roundingTypeLabel.match(/(\d+)/);
      const unit = unitMatch ? parseInt(unitMatch[1]) : parseFloat(roundingTypeCode) || 1;

      const priceBeforeRounding = price;

      // roundingUnit 라벨로 반올림 방법 결정
      if (roundingUnitLabel.includes("없음") || !roundingUnitCode) {
        // 반올림없음: 할인 적용된 원래 값 그대로
        // price 변경 없음
      } else if (roundingUnitLabel.includes("절삭")) {
        price = Math.floor(price / unit) * unit;
      } else if (roundingUnitLabel.includes("올림")) {
        price = Math.ceil(price / unit) * unit;
      } else if (roundingUnitLabel.includes("반올림")) {
        price = Math.round(price / unit) * unit;
      }

      return price;
    },
    [componentConfig.autoCalculation, getOptionLabel],
  );

  // 🆕 그룹별 필드 변경 핸들러: itemId + groupId + entryId + fieldName
  const handleFieldChange = useCallback(
    (itemId: string, groupId: string, entryId: string, fieldName: string, value: any) => {
      setItems((prevItems) => {
        return prevItems.map((item) => {
          if (item.id !== itemId) return item;

          const groupEntries = item.fieldGroups[groupId] || [];
          const existingEntryIndex = groupEntries.findIndex((e) => e.id === entryId);

          if (existingEntryIndex >= 0) {
            const currentEntry = groupEntries[existingEntryIndex];

            // 날짜 검증: 종료일이 시작일보다 앞서면 차단
            if (fieldName === "end_date" && value && currentEntry.start_date) {
              if (new Date(value) < new Date(currentEntry.start_date as string)) {
                alert("종료일은 시작일보다 이후여야 합니다.");
                return item; // 변경 취소
              }
            }
            if (fieldName === "start_date" && value && currentEntry.end_date) {
              if (new Date(value) > new Date(currentEntry.end_date as string)) {
                alert("시작일은 종료일보다 이전이어야 합니다.");
                return item; // 변경 취소
              }
            }

            // 기존 entry 업데이트
            const updatedEntries = [...groupEntries];
            const updatedEntry = {
              ...updatedEntries[existingEntryIndex],
              [fieldName]: value,
            };

            // 가격 관련 필드가 변경되면 자동 계산
            if (componentConfig.autoCalculation) {
              const { inputFields, targetField } = componentConfig.autoCalculation;
              const priceRelatedFields = [
                inputFields.basePrice,
                inputFields.discountType,
                inputFields.discountValue,
                inputFields.roundingType,
                inputFields.roundingUnit,
              ];

              if (priceRelatedFields.includes(fieldName)) {
                const calculatedPrice = calculatePrice(updatedEntry);
                updatedEntry[targetField] = calculatedPrice;
              }
            }

            updatedEntries[existingEntryIndex] = updatedEntry;
            return {
              ...item,
              fieldGroups: {
                ...item.fieldGroups,
                [groupId]: updatedEntries,
              },
            };
          } else {
            // 이 경로는 발생하면 안 됨 (handleAddGroupEntry에서 미리 추가함)
            return item;
          }
        });
      });
    },
    [calculatePrice],
  );

  // 🆕 품목 제거 핸들러
  const handleRemoveItem = (itemId: string) => {
    setItems((prevItems) => prevItems.filter((item) => item.id !== itemId));
  };

  // 🆕 그룹 항목 추가 핸들러 (특정 그룹에 새 항목 추가)
  const handleAddGroupEntry = (itemId: string, groupId: string) => {
    const newEntryId = `entry-${Date.now()}`;

    // 🔧 미리 빈 entry를 추가하여 리렌더링 방지 (autoFillFrom 처리)
    setItems((prevItems) => {
      return prevItems.map((item) => {
        if (item.id !== itemId) return item;

        const groupEntries = item.fieldGroups[groupId] || [];
        const newEntry: GroupEntry = { id: newEntryId };

        // 🆕 autoFillFrom 필드 자동 채우기 (tableName으로 직접 접근)
        const groupFields = (componentConfig.additionalFields || []).filter((f) => f.groupId === groupId);

        groupFields.forEach((field) => {
          if (!field.autoFillFrom) return;

          // 데이터 소스 결정
          let sourceData: any = null;

          if (field.autoFillFromTable) {
            // 특정 테이블에서 가져오기
            const tableData = dataRegistry[field.autoFillFromTable];
            if (tableData && tableData.length > 0) {
              // 첫 번째 항목 사용 (또는 매칭 로직 추가 가능)
              sourceData = tableData[0].originalData || tableData[0];
            } else {
              sourceData = item.originalData;
            }
          } else {
            sourceData = item.originalData;
          }

          // 🆕 getFieldValue 사용하여 Entity Join 필드도 찾기
          if (sourceData) {
            const fieldValue = getFieldValue(sourceData, field.autoFillFrom);
            if (fieldValue !== undefined && fieldValue !== null) {
              newEntry[field.name] = fieldValue;
            }
          }
        });

        return {
          ...item,
          fieldGroups: {
            ...item.fieldGroups,
            [groupId]: [...groupEntries, newEntry],
          },
        };
      });
    });

    setIsEditing(true);
    setEditingItemId(itemId);
    setEditingDetailId(newEntryId);
    setEditingGroupId(groupId);
    // 그룹별 독립 편집: 해당 그룹만 열기 (다른 그룹은 유지)
    setEditingEntries((prev) => ({ ...prev, [groupId]: newEntryId }));
  };

  // 🆕 그룹 항목 제거 핸들러
  const handleRemoveGroupEntry = (itemId: string, groupId: string, entryId: string) => {
    setItems((prevItems) =>
      prevItems.map((item) => {
        if (item.id !== itemId) return item;
        return {
          ...item,
          fieldGroups: {
            ...item.fieldGroups,
            [groupId]: (item.fieldGroups[groupId] || []).filter((e) => e.id !== entryId),
          },
        };
      }),
    );
    // 제거된 항목이 편집 중이었으면 해당 그룹 편집 닫기
    setEditingEntries((prev) => {
      if (prev[groupId] === entryId) {
        const next = { ...prev };
        delete next[groupId];
        return next;
      }
      return prev;
    });
  };

  // 🆕 그룹 항목 편집 핸들러 (클릭하면 수정 가능) - 독립 편집
  const handleEditGroupEntry = (itemId: string, groupId: string, entryId: string) => {
    setIsEditing(true);
    setEditingItemId(itemId);
    setEditingGroupId(groupId);
    setEditingDetailId(entryId);
    // 그룹별 독립 편집: 해당 그룹만 토글 (다른 그룹은 유지)
    setEditingEntries((prev) => ({ ...prev, [groupId]: entryId }));
  };

  // 🆕 특정 그룹의 편집 닫기 (다른 그룹은 유지)
  const closeGroupEditing = (groupId: string) => {
    setEditingEntries((prev) => {
      const next = { ...prev };
      delete next[groupId];
      return next;
    });
  };

  // 🆕 다음 품목으로 이동
  const handleNextItem = () => {
    const currentIndex = items.findIndex((item) => item.id === editingItemId);
    if (currentIndex < items.length - 1) {
      // 다음 품목으로
      const nextItem = items[currentIndex + 1];
      setEditingItemId(nextItem.id);
      const groups = componentConfig.fieldGroups || [];
      const firstGroupId = groups.length > 0 ? groups[0].id : "default";
      const newEntryId = `entry-${Date.now()}`;
      setEditingDetailId(newEntryId);
      setEditingGroupId(firstGroupId);
      setIsEditing(true);
    } else {
      // 마지막 품목이면 편집 모드 종료
      setIsEditing(false);
      setEditingItemId(null);
      setEditingDetailId(null);
      setEditingGroupId(null);
    }
  };

  // 🆕 개별 필드 렌더링 (itemId, groupId, entryId, entry 데이터 전달)
  const renderField = (
    field: AdditionalFieldDefinition,
    itemId: string,
    groupId: string,
    entryId: string,
    entry: GroupEntry,
  ) => {
    const value = entry[field.name] || field.defaultValue || "";

    // 🆕 계산된 필드는 읽기 전용 (자동 계산 설정 기반)
    const isCalculatedField = componentConfig.autoCalculation?.targetField === field.name;

    const commonProps = {
      value: value || "",
      disabled: componentConfig.disabled || componentConfig.readonly,
      placeholder: field.placeholder,
      required: field.required,
    };

    // 🆕 inputType이 있으면 우선 사용, 없으면 field.type 사용
    const renderType = field.inputType || field.type;

    // 🆕 inputType에 따라 적절한 컴포넌트 렌더링
    switch (renderType) {
      // 기본 타입들
      case "text":
      case "varchar":
      case "char":
        return (
          <Input
            {...commonProps}
            type="text"
            onChange={(e) => handleFieldChange(itemId, groupId, entryId, field.name, e.target.value)}
            maxLength={field.validation?.maxLength}
            className="h-7 text-xs"
          />
        );

      case "number":
      case "int":
      case "integer":
      case "bigint":
      case "decimal":
      case "numeric": {
        // 숫자 포맷팅 헬퍼: 콤마 표시 + 실제 값은 숫자만 저장
        const rawNum = value ? String(value).replace(/,/g, "") : "";
        const displayNum = rawNum && !isNaN(Number(rawNum))
          ? centralFormatNumber(Number(rawNum))
          : rawNum;

        // 계산된 단가는 읽기 전용 + 강조 표시
        if (isCalculatedField) {
          return (
            <div className="relative">
              <Input
                value={displayNum}
                readOnly
                disabled
                className={cn(
                  "h-7 text-xs",
                  "bg-primary/10 border-primary/30 text-primary font-semibold",
                  "cursor-not-allowed",
                )}
              />
              <div className="text-primary/70 absolute top-1/2 right-2 -translate-y-1/2 text-[9px]">자동 계산</div>
            </div>
          );
        }

        return (
          <Input
            value={displayNum}
            placeholder={field.placeholder}
            disabled={componentConfig.disabled || componentConfig.readonly}
            type="text"
            inputMode="numeric"
            onChange={(e) => {
              // 콤마 제거 후 숫자만 저장
              const cleaned = e.target.value.replace(/,/g, "").replace(/[^0-9.\-]/g, "");
              handleFieldChange(itemId, groupId, entryId, field.name, cleaned);
            }}
            className="h-7 text-xs"
          />
        );
      }

      case "date":
      case "timestamp":
      case "datetime":
        return (
          <Input
            {...commonProps}
            type="date"
            onChange={(e) => handleFieldChange(itemId, groupId, entryId, field.name, e.target.value)}
            onClick={(e) => {
              // 날짜 선택기 강제 열기
              const target = e.target as HTMLInputElement;
              if (target && target.showPicker) {
                target.showPicker();
              }
            }}
            className="h-7 cursor-pointer text-xs"
          />
        );

      case "checkbox":
      case "boolean":
      case "bool":
        return (
          <Checkbox
            checked={value === true || value === "true"}
            onCheckedChange={(checked) => handleFieldChange(itemId, groupId, entryId, field.name, checked)}
            disabled={componentConfig.disabled || componentConfig.readonly}
          />
        );

      case "textarea":
        return (
          <Textarea
            {...commonProps}
            onChange={(e) => handleFieldChange(itemId, groupId, entryId, field.name, e.target.value)}
            rows={1}
            className="min-h-[28px] resize-none text-xs"
          />
        );

      // 🆕 추가 inputType들
      case "code":
      case "category":
        // 🆕 옵션을 field.name 또는 field.codeCategory 키로 찾기
        let categoryOptions = field.options; // 기본값

        // 1순위: 필드 이름으로 직접 찾기 (category 타입에서 사용)
        if (codeOptions[field.name]) {
          categoryOptions = codeOptions[field.name];
        }
        // 2순위: codeCategory로 찾기 (code 타입에서 사용)
        else if (field.codeCategory && codeOptions[field.codeCategory]) {
          categoryOptions = codeOptions[field.codeCategory];
        }

        return (
          <Select
            value={value || ""}
            onValueChange={(val) => handleFieldChange(itemId, groupId, entryId, field.name, val)}
            disabled={componentConfig.disabled || componentConfig.readonly}
          >
            <SelectTrigger className="h-7 w-full text-xs">
              <SelectValue placeholder={field.placeholder || "선택하세요"} />
            </SelectTrigger>
            <SelectContent>
              {categoryOptions && categoryOptions.length > 0 ? (
                categoryOptions
                  .filter((option) => option.value !== "")
                  .map((option) => (
                    <SelectItem key={option.value} value={option.value}>
                      {option.label}
                    </SelectItem>
                  ))
              ) : (
                <div className="text-muted-foreground py-6 text-center text-xs">옵션 로딩 중...</div>
              )}
            </SelectContent>
          </Select>
        );

      case "entity":
        // TODO: EntitySelect 컴포넌트 사용
        return (
          <Input
            {...commonProps}
            type="text"
            onChange={(e) => handleFieldChange(itemId, groupId, entryId, field.name, e.target.value)}
            className="h-7 text-xs"
          />
        );

      case "select":
        return (
          <Select
            value={value || ""}
            onValueChange={(val) => handleFieldChange(itemId, groupId, entryId, field.name, val)}
            disabled={componentConfig.disabled || componentConfig.readonly}
          >
            <SelectTrigger className="h-7 w-full text-xs">
              <SelectValue placeholder={field.placeholder || "선택하세요"} />
            </SelectTrigger>
            <SelectContent>
              {field.options
                ?.filter((option) => option.value !== "")
                .map((option) => (
                  <SelectItem key={option.value} value={option.value}>
                    {option.label}
                  </SelectItem>
                ))}
            </SelectContent>
          </Select>
        );

      // 기본값: 텍스트 입력
      default:
        return (
          <Input
            {...commonProps}
            type="text"
            onChange={(e) => handleFieldChange(itemId, groupId, entryId, field.name, e.target.value)}
            maxLength={field.validation?.maxLength}
            className="h-7 text-xs"
          />
        );
    }
  };

  // 🆕 Entity Join된 필드명도 찾는 헬퍼 함수
  const getFieldValue = useCallback((data: Record<string, any>, fieldName: string) => {
    // 1. Entity Join 형식으로 먼저 찾기 (*_fieldName) - 우선순위!
    //    예: item_id_item_name (품목의 품명) vs customer_item_name (거래처 품명)
    const possibleKeys = Object.keys(data).filter(
      (key) => key.endsWith(`_${fieldName}`) && key !== fieldName, // 자기 자신은 제외
    );

    if (possibleKeys.length > 0) {
      // 🆕 여러 개 있으면 가장 긴 키 선택 (더 구체적인 것)
      // 예: item_id_item_name (18자) vs customer_item_name (18자) 중 정렬 순서로 선택
      // 실제로는 item_id로 시작하는 것을 우선
      const entityJoinKey = possibleKeys.find((key) => key.includes("_id_")) || possibleKeys[0];
      return data[entityJoinKey];
    }

    // 2. 직접 필드명으로 찾기 (Entity Join이 없을 때만)
    if (data[fieldName] !== undefined) {
      return data[fieldName];
    }
    return null;
  }, []);

  // 🆕 displayItems를 렌더링하는 헬퍼 함수 (그룹별)
  const renderDisplayItems = useCallback(
    (entry: GroupEntry, item: ItemData, groupId: string) => {
      // 🆕 해당 그룹의 displayItems 가져오기
      const group = (componentConfig.fieldGroups || []).find((g) => g.id === groupId);
      const displayItems = group?.displayItems || [];

      if (displayItems.length === 0) {
        const fields = (componentConfig.additionalFields || []).filter((f) => {
          const matchGroup = componentConfig.fieldGroups && componentConfig.fieldGroups.length > 0
            ? f.groupId === groupId
            : true;
          const isVisible = f.width !== "0px";
          return matchGroup && isVisible;
        });

        // 헬퍼: 값을 사람이 읽기 좋은 형태로 변환
        const formatValue = (f: any, value: any): string => {
          if (!value && value !== 0) return "";
          const strValue = String(value);

          // 날짜 포맷
          const isoDateMatch = strValue.match(/^(\d{4})-(\d{2})-(\d{2})(T|\s|$)/);
          if (isoDateMatch) {
            const [, year, month, day] = isoDateMatch;
            return `${year}.${month}.${day}`;
          }

          // 카테고리/코드 -> 라벨명
          const renderType = f.inputType || f.type;
          if (renderType === "category" || renderType === "code" || renderType === "select") {
            const options = codeOptions[f.name] || f.options || [];
            const matched = options.find((opt: any) => opt.value === strValue);
            if (matched) return matched.label;
          }

          // 숫자는 천 단위 구분 (공통 formatNumber 사용)
          if (renderType === "number" && !isNaN(Number(strValue))) {
            return centralFormatNumber(Number(strValue));
          }

          return strValue;
        };

        // 간결한 요약 생성 (그룹별 핵심 정보만)
        const hasAnyValue = fields.some((f) => {
          const v = entry[f.name];
          return v !== undefined && v !== null && v !== "";
        });

        if (!hasAnyValue) {
          const fieldLabels = fields.slice(0, 2).map(f => f.label).join("/");
          return `신규 ${fieldLabels} 입력`;
        }

        // 날짜 범위가 있으면 우선 표시
        const startDate = entry["start_date"] ? formatValue({ inputType: "date" }, entry["start_date"]) : "";
        const endDate = entry["end_date"] ? formatValue({ inputType: "date" }, entry["end_date"]) : "";

        // 기준단가(calculated_price) 또는 기준가(base_price) 표시
        const calcPrice = entry["calculated_price"] ? formatValue({ inputType: "number" }, entry["calculated_price"]) : "";
        const basePrice = entry["base_price"] ? formatValue({ inputType: "number" }, entry["base_price"]) : "";

        // 통화코드
        const currencyCode = entry["currency_code"] ? formatValue(
          fields.find(f => f.name === "currency_code") || { inputType: "category", name: "currency_code" },
          entry["currency_code"]
        ) : "";

        if (startDate || calcPrice || basePrice) {
          // 날짜 + 단가 간결 표시
          const parts: string[] = [];
          if (startDate) {
            parts.push(endDate ? `${startDate} ~ ${endDate}` : `${startDate} ~`);
          }
          if (calcPrice) {
            parts.push(`${currencyCode || ""} ${calcPrice}`.trim());
          } else if (basePrice) {
            parts.push(`${currencyCode || ""} ${basePrice}`.trim());
          }
          return parts.join("  |  ");
        }

        // 그 외 그룹 (거래처 품번 등): 첫 2개 필드만 표시
        const summaryParts = fields
          .slice(0, 3)
          .map((f) => {
            const value = entry[f.name];
            if (!value && value !== 0) return null;
            return `${f.label}: ${formatValue(f, value)}`;
          })
          .filter(Boolean);
        return summaryParts.join("  ");
      }

      // displayItems 설정대로 렌더링
      return (
        <>
          {displayItems.map((displayItem) => {
            const styleClasses = cn(
              displayItem.bold && "font-bold",
              displayItem.underline && "underline",
              displayItem.italic && "italic",
            );

            const inlineStyle: React.CSSProperties = {
              color: displayItem.color,
              backgroundColor: displayItem.backgroundColor,
            };

            switch (displayItem.type) {
              case "icon": {
                if (!displayItem.icon) return null;
                const IconComponent = LUCIDE_ICON_MAP[displayItem.icon];
                if (!IconComponent) return null;
                return <IconComponent key={displayItem.id} className="mr-1 inline-block h-3 w-3" style={inlineStyle} />;
              }

              case "text":
                return (
                  <span key={displayItem.id} className={styleClasses} style={inlineStyle}>
                    {displayItem.value}
                  </span>
                );

              case "field": {
                const fieldValue = entry[displayItem.fieldName || ""];
                const isEmpty = fieldValue === null || fieldValue === undefined || fieldValue === "";

                // 🆕 빈 값 처리
                if (isEmpty) {
                  switch (displayItem.emptyBehavior) {
                    case "hide":
                      return null; // 항목 숨김
                    case "default":
                      // 기본값 표시
                      const defaultValue = displayItem.defaultValue || "-";
                      return (
                        <span
                          key={displayItem.id}
                          className={cn(styleClasses, "text-muted-foreground")}
                          style={inlineStyle}
                        >
                          {displayItem.label}
                          {defaultValue}
                        </span>
                      );
                    case "blank":
                    default:
                      // 빈 칸으로 표시
                      return (
                        <span key={displayItem.id} className={styleClasses} style={inlineStyle}>
                          {displayItem.label}
                        </span>
                      );
                  }
                }

                // 값이 있는 경우, 형식에 맞게 표시
                let formattedValue = fieldValue;

                // 🔧 자동 날짜 감지 (format 설정 없어도 ISO 날짜 자동 변환)
                const strValue = String(fieldValue);
                const isoDateMatch = strValue.match(/^(\d{4})-(\d{2})-(\d{2})(T|\s|$)/);
                if (isoDateMatch && !displayItem.format) {
                  const [, year, month, day] = isoDateMatch;
                  formattedValue = `${year}.${month}.${day}`;
                }

                switch (displayItem.format) {
                  case "currency":
                    formattedValue = centralFormatNumber(Number(fieldValue) || 0);
                    break;
                  case "number":
                    formattedValue = centralFormatNumber(Number(fieldValue) || 0);
                    break;
                  case "date":
                    // YYYY.MM.DD 형식
                    if (fieldValue) {
                      // 날짜 문자열을 직접 파싱 (타임존 문제 방지)
                      const dateStr = String(fieldValue);
                      const match = dateStr.match(/^(\d{4})-(\d{2})-(\d{2})/);
                      if (match) {
                        const [, year, month, day] = match;
                        formattedValue = `${year}.${month}.${day}`;
                      } else {
                        // Date 객체로 변환 시도 (fallback)
                        const date = new Date(fieldValue);
                        if (!isNaN(date.getTime())) {
                          formattedValue = date
                            .toLocaleDateString("ko-KR", {
                              year: "numeric",
                              month: "2-digit",
                              day: "2-digit",
                            })
                            .replace(/\. /g, ".")
                            .replace(/\.$/, "");
                        }
                      }
                    }
                    break;
                  case "badge":
                    // 배지로 표시
                    return (
                      <Badge
                        key={displayItem.id}
                        variant={displayItem.badgeVariant || "default"}
                        className={styleClasses}
                        style={inlineStyle}
                      >
                        {displayItem.label}
                        {formattedValue}
                      </Badge>
                    );
                  case "text":
                  default:
                    // 일반 텍스트
                    break;
                }

                // 🔧 마지막 안전장치: formattedValue가 여전히 ISO 형식이면 한번 더 변환
                let finalValue = formattedValue;
                if (typeof formattedValue === "string") {
                  const isoCheck = formattedValue.match(/^(\d{4})-(\d{2})-(\d{2})(T|\s|$)/);
                  if (isoCheck) {
                    const [, year, month, day] = isoCheck;
                    finalValue = `${year}.${month}.${day}`;
                  }
                }

                return (
                  <span key={displayItem.id} className={styleClasses} style={inlineStyle}>
                    {displayItem.label}
                    {finalValue}
                  </span>
                );
              }

              case "badge": {
                const fieldValue = displayItem.fieldName ? entry[displayItem.fieldName] : displayItem.value;
                return (
                  <Badge
                    key={displayItem.id}
                    variant={displayItem.badgeVariant || "default"}
                    className={styleClasses}
                    style={inlineStyle}
                  >
                    {displayItem.label}
                    {fieldValue}
                  </Badge>
                );
              }

              default:
                return null;
            }
          })}
        </>
      );
    },
    [componentConfig.fieldGroups, componentConfig.additionalFields, codeOptions],
  );

  // 빈 상태 렌더링
  if (items.length === 0) {
    // 디자인 모드: 샘플 데이터로 미리보기 표시
    if (isDesignMode) {
      const sampleDisplayCols = componentConfig.displayColumns || [];
      const sampleFields = (componentConfig.additionalFields || []).filter(f => f.name !== sourceKeyField && f.width !== "0px");
      const sampleGroups = componentConfig.fieldGroups || [{ id: "default", title: "입력 정보", order: 0 }];
      const gridCols = sampleGroups.length === 1 ? "grid-cols-1" : "grid-cols-2";

      return (
        <div style={componentStyle} className={className} onClick={handleClick}>
          <div className="bg-card space-y-3 p-3">
            {/* 미리보기 안내 배너 */}
            <div className="bg-muted/50 flex items-center gap-2 rounded-md px-3 py-2 text-xs">
              <span className="text-primary font-medium">[미리보기]</span>
              <span className="text-muted-foreground">실제 데이터가 전달되면 아래와 같은 형태로 표시됩니다</span>
            </div>

            {/* 샘플 품목 카드 2개 */}
            {[1, 2].map((idx) => (
              <Card key={idx} className="border shadow-sm">
                <CardHeader className="pb-2">
                  <CardTitle className="flex items-center justify-between text-sm font-semibold">
                    <span>
                      {idx}. {sampleDisplayCols.length > 0 ? `샘플 ${sampleDisplayCols[0]?.label || "품목"} ${idx}` : `샘플 항목 ${idx}`}
                    </span>
                    <Button type="button" variant="ghost" size="sm" className="h-6 w-6 p-0 text-destructive/80" disabled>
                      <X className="h-3 w-3" />
                    </Button>
                  </CardTitle>
                  {sampleDisplayCols.length > 0 && (
                    <div className="text-muted-foreground text-xs">
                      {sampleDisplayCols.map((col, i) => (
                        <span key={col.name}>
                          {i > 0 && " | "}
                          <span className="text-muted-foreground/60">{col.label}: 샘플값</span>
                        </span>
                      ))}
                    </div>
                  )}
                </CardHeader>
                <CardContent className="pt-0">
                  <div className={`grid ${gridCols} gap-2`}>
                    {sampleGroups.map((group) => {
                      const groupFields = sampleFields.filter(f => 
                        sampleGroups.length <= 1 || f.groupId === group.id
                      );
                      if (groupFields.length === 0) return null;
                      const isSingle = group.maxEntries === 1;

                      return (
                        <Card key={group.id} className="border shadow-sm">
                          <CardHeader className="pb-2">
                            <CardTitle className="flex items-center justify-between text-xs font-semibold">
                              <span>{group.title}</span>
                              {!isSingle && (
                                <Button type="button" size="sm" variant="outline" className="h-6 text-[10px]" disabled>
                                  + 추가
                                </Button>
                              )}
                            </CardTitle>
                          </CardHeader>
                          <CardContent className="space-y-1 pb-3">
                            {isSingle ? (
                              /* 1:1 그룹: 인라인 폼 미리보기 */
                              <div className="grid grid-cols-2 gap-1">
                                {groupFields.slice(0, 4).map(f => (
                                  <div key={f.name} className="space-y-0.5">
                                    <span className="text-muted-foreground text-[9px]">{f.label}</span>
                                    <div className="bg-muted/40 h-5 rounded border text-[10px] leading-5 px-1">샘플</div>
                                  </div>
                                ))}
                                {groupFields.length > 4 && (
                                  <div className="text-muted-foreground col-span-2 text-[9px]">외 {groupFields.length - 4}개 필드</div>
                                )}
                              </div>
                            ) : (
                              /* 1:N 그룹: 다중 항목 미리보기 */
                              <>
                                <div className="bg-muted/30 flex items-center justify-between rounded border p-1.5 text-[10px]">
                                  <span className="truncate">
                                    1. {groupFields.slice(0, 2).map(f => `${f.label}: 샘플`).join(" / ")}
                                  </span>
                                  <X className="h-2.5 w-2.5 shrink-0 text-muted-foreground/70" />
                                </div>
                                {idx === 1 && (
                                  <div className="bg-muted/30 flex items-center justify-between rounded border p-1.5 text-[10px]">
                                    <span className="truncate">
                                      2. {groupFields.slice(0, 2).map(f => `${f.label}: 샘플`).join(" / ")}
                                    </span>
                                    <X className="h-2.5 w-2.5 shrink-0 text-muted-foreground/70" />
                                  </div>
                                )}
                              </>
                            )}
                          </CardContent>
                        </Card>
                      );
                    })}
                  </div>
                </CardContent>
              </Card>
            ))}
          </div>
        </div>
      );
    }

    // 런타임 빈 상태
    return (
      <div style={componentStyle} className={className} onClick={handleClick}>
        <div className="border-border bg-muted/30 flex flex-col items-center justify-center rounded-lg border-2 border-dashed p-8 text-center">
          <p className="text-muted-foreground text-sm">{componentConfig.emptyMessage}</p>
        </div>
      </div>
    );
  }

  // 🆕 그룹별로 입력 항목들 렌더링 (각 그룹이 독립적으로 여러 항목 관리)
  const renderFieldsByGroup = (item: ItemData) => {
    const fields = componentConfig.additionalFields || [];
    const groups = componentConfig.fieldGroups || [];

    // 그룹이 정의되지 않은 경우, 기본 그룹 사용
    const effectiveGroups = groups.length > 0 ? groups : [{ id: "default", title: "입력 정보", order: 0 }];

    const sortedGroups = [...effectiveGroups].sort((a, b) => (a.order || 0) - (b.order || 0));

    // 그룹 수에 따라 grid 열 수 결정
    const gridCols = sortedGroups.length === 1 ? "grid-cols-1" : "grid-cols-2";

    return (
      <div className={`grid ${gridCols} gap-3`}>
        {sortedGroups.map((group) => {
          const groupFields = fields.filter((f) => (groups.length === 0 ? true : f.groupId === group.id));

          if (groupFields.length === 0) return null;

          const groupEntries = item.fieldGroups[group.id] || [];
          // 그룹별 독립 편집 상태 사용
          const editingEntryIdForGroup = editingEntries[group.id] || null;

          // 1:1 관계 그룹 (maxEntries === 1): 인라인 폼으로 바로 표시
          const isSingleEntry = group.maxEntries === 1;
          const singleEntry = isSingleEntry ? (groupEntries[0] || { id: `${group.id}_auto_1` }) : null;
          // hidden 필드 제외 (width: "0px"인 필드)
          const visibleFields = groupFields.filter((f) => f.width !== "0px");

          return (
            <Card key={group.id} className="border-2 shadow-sm">
              <CardHeader className="pb-2">
                <CardTitle className="flex items-center justify-between text-sm font-semibold">
                  <span>{group.title}</span>
                  {/* 1:N 그룹만 + 추가 버튼 표시 */}
                  {!isSingleEntry && (
                    <Button
                      type="button"
                      onClick={() => handleAddGroupEntry(item.id, group.id)}
                      size="sm"
                      variant="outline"
                      className="h-6 text-[11px]"
                    >
                      + 추가
                    </Button>
                  )}
                </CardTitle>
                {group.description && <p className="text-muted-foreground text-[11px]">{group.description}</p>}
              </CardHeader>
              <CardContent className="space-y-1.5 pt-0">
                {/* === 1:1 그룹: 인라인 폼 (항상 편집 모드) - 컴팩트 === */}
                {isSingleEntry && singleEntry && (
                  <div className="grid grid-cols-2 gap-x-2 gap-y-1.5">
                    {visibleFields.map((field) => (
                      <div key={field.name} className={cn(
                        "space-y-0.5",
                        field.type === "textarea" && "col-span-2"
                      )}>
                        <label className="text-[11px] font-medium leading-none">
                          {field.label}{(field.required || isColumnRequiredByMeta(componentConfig.targetTable, field.name)) && <span className="text-orange-500">*</span>}
                        </label>
                        {renderField(field, item.id, group.id, singleEntry.id, singleEntry)}
                      </div>
                    ))}
                  </div>
                )}

                {/* === 1:N 그룹: 다중 입력 (독립 편집) === */}
                {!isSingleEntry && (
                  <>
                    {groupEntries.length > 0 ? (
                      <div className="space-y-1.5">
                        {groupEntries.map((entry, idx) => {
                          // 그룹별 독립 편집 상태 확인
                          const isEditingThisEntry = editingEntryIdForGroup === entry.id;

                          return (
                            <div key={entry.id} className="bg-muted/30 rounded border">
                              {/* 헤더 (항상 표시) */}
                              <div
                                className="hover:bg-muted/50 flex cursor-pointer items-center justify-between px-2 py-1.5 text-xs"
                                onClick={() => {
                                  if (isEditingThisEntry) {
                                    // 이 그룹만 닫기 (다른 그룹은 유지)
                                    closeGroupEditing(group.id);
                                  } else {
                                    handleEditGroupEntry(item.id, group.id, entry.id);
                                  }
                                }}
                              >
                                <span className="flex items-center gap-1">
                                  {idx + 1}. {renderDisplayItems(entry, item, group.id)}
                                </span>
                                <div className="flex items-center gap-1">
                                  <Button
                                    type="button"
                                    variant="ghost"
                                    size="sm"
                                    onClick={(e) => {
                                      e.stopPropagation();
                                      handleRemoveGroupEntry(item.id, group.id, entry.id);
                                    }}
                                    className="h-5 w-5 p-0"
                                  >
                                    <X className="h-3 w-3" />
                                  </Button>
                                </div>
                              </div>

                              {/* 폼 영역 (편집 시에만 아래로 펼침) - 컴팩트 */}
                              {isEditingThisEntry && (
                                <div className="border-t px-2 pb-2 pt-1.5">
                                  <div className="grid grid-cols-2 gap-x-2 gap-y-1.5">
                                    {visibleFields.map((field) => (
                                      <div key={field.name} className={cn(
                                        "space-y-0.5",
                                        field.type === "textarea" && "col-span-2"
                                      )}>
                                        <label className="text-[11px] font-medium leading-none">
                                          {field.label}{(field.required || isColumnRequiredByMeta(componentConfig.targetTable, field.name)) && <span className="text-orange-500">*</span>}
                                        </label>
                                        {renderField(field, item.id, group.id, entry.id, entry)}
                                      </div>
                                    ))}
                                  </div>
                                  <div className="mt-1.5 flex justify-end">
                                    <Button
                                      type="button"
                                      size="sm"
                                      variant="outline"
                                      onClick={() => closeGroupEditing(group.id)}
                                      className="h-6 px-3 text-[11px]"
                                    >
                                      완료
                                    </Button>
                                  </div>
                                </div>
                              )}
                            </div>
                          );
                        })}
                      </div>
                    ) : (
                      <p className="text-muted-foreground text-xs italic">아직 입력된 항목이 없습니다.</p>
                    )}

                    {/* 새 항목은 handleAddGroupEntry에서 아코디언 항목으로 직접 추가됨 */}
                  </>
                )}
              </CardContent>
            </Card>
          );
        })}
      </div>
    );
  };

  // 🆕 Grid 레이아웃 렌더링 (완전히 재작성 - 그룹별 독립 관리)
  const renderGridLayout = () => {
    return (
      <div className="bg-card space-y-4 p-4">
        {items.map((item, index) => {
          // 제목용 첫 번째 컬럼 값
          const titleValue = componentConfig.displayColumns?.[0]?.name
            ? getFieldValue(item.originalData, componentConfig.displayColumns[0].name)
            : null;

          // 요약용 모든 컬럼 값들
          const summaryValues = componentConfig.displayColumns
            ?.map((col) => getFieldValue(item.originalData, col.name))
            .filter(Boolean);


          return (
            <Card key={item.id} className="border shadow-sm">
              <CardHeader className="pb-3">
                <CardTitle className="flex items-center justify-between text-base font-semibold">
                  <span>
                    {index + 1}. {titleValue || "항목"}
                  </span>
                  <Button
                    type="button"
                    variant="ghost"
                    size="sm"
                    onClick={() => handleRemoveItem(item.id)}
                    className="h-7 w-7 p-0 text-destructive"
                  >
                    <X className="h-4 w-4" />
                  </Button>
                </CardTitle>
                {/* 원본 데이터 요약 */}
                {summaryValues && summaryValues.length > 0 && (
                  <div className="text-muted-foreground text-xs">{summaryValues.join(" | ")}</div>
                )}
              </CardHeader>
              <CardContent>
                {/* 그룹별 입력 항목들 렌더링 */}
                {renderFieldsByGroup(item)}
              </CardContent>
            </Card>
          );
        })}
      </div>
    );
  };

  // 🔧 기존 renderGridLayout (백업 - 사용 안 함)
  const renderGridLayout_OLD = () => {
    return (
      <div className="bg-card space-y-4 p-4">
        {/* Modal 모드: 추가 버튼 */}
        {isModalMode && !isEditing && items.length === 0 && (
          <div className="py-8 text-center">
            <p className="text-muted-foreground mb-4 text-sm">항목을 추가하려면 추가 버튼을 클릭하세요</p>
            <Button type="button" onClick={() => setIsEditing(true)} size="sm" className="text-xs sm:text-sm">
              + 추가
            </Button>
          </div>
        )}

        {/* Modal 모드: 편집 중인 항목 (입력창 표시) */}
        {isModalMode &&
          isEditing &&
          editingItemId &&
          (() => {
            const editingItem = items.find((item) => item.id === editingItemId);
            if (!editingItem) {
              return null;
            }

            return (
              <Card className="border-primary border-2 shadow-sm">
                <CardHeader className="pb-3">
                  <CardTitle className="flex items-center justify-between text-sm font-semibold">
                    <span>
                      품목:{" "}
                      {getFieldValue(editingItem.originalData, componentConfig.displayColumns?.[0]?.name || "") ||
                        "항목"}
                    </span>
                    <Button
                      type="button"
                      variant="ghost"
                      size="sm"
                      onClick={() => {
                        setIsEditing(false);
                        setEditingItemId(null);
                        setEditingDetailId(null);
                      }}
                      className="h-7 text-xs"
                    >
                      취소
                    </Button>
                  </CardTitle>
                </CardHeader>
                <CardContent className="space-y-3">
                  {/* 원본 데이터 요약 */}
                  <div className="text-muted-foreground bg-muted rounded p-2 text-xs">
                    {componentConfig.displayColumns
                      ?.map((col) => editingItem.originalData[col.name])
                      .filter(Boolean)
                      .join(" | ")}
                  </div>

                  {/* 🆕 이미 입력된 상세 항목들 표시 */}
                  {editingItem.details.length > 0 && (
                    <div className="space-y-2">
                      <div className="text-xs font-medium">입력된 항목 ({editingItem.details.length}개)</div>
                      {editingItem.details.map((detail, idx) => (
                        <div
                          key={detail.id}
                          className="bg-muted/30 flex items-center justify-between rounded border p-2 text-xs"
                        >
                          <span>
                            {idx + 1}. {detail[componentConfig.additionalFields?.[0]?.name] || "입력됨"}
                          </span>
                          <Button
                            type="button"
                            variant="ghost"
                            size="sm"
                            onClick={() => handleRemoveDetail(editingItem.id, detail.id)}
                            className="h-6 w-6 p-0"
                          >
                            X
                          </Button>
                        </div>
                      ))}
                    </div>
                  )}

                  {/* 추가 입력 필드 */}
                  {componentConfig.additionalFields &&
                    componentConfig.additionalFields.length > 0 &&
                    editingDetailId &&
                    (() => {
                      // 현재 편집 중인 detail 찾기 (없으면 빈 객체)
                      const currentDetail = editingItem.details.find((d) => d.id === editingDetailId) || {
                        id: editingDetailId,
                      };
                      return renderFieldsByGroup(editingItem.id, editingDetailId, currentDetail);
                    })()}

                  {/* 액션 버튼들 */}
                  <div className="flex justify-end gap-2 pt-2">
                    <Button
                      type="button"
                      onClick={() => handleAddDetail(editingItem.id)}
                      size="sm"
                      variant="outline"
                      className="text-xs"
                    >
                      + 이 품목에 추가 입력
                    </Button>
                    <Button type="button" variant="default" onClick={handleNextItem} size="sm" className="text-xs">
                      다음 품목
                    </Button>
                  </div>
                </CardContent>
              </Card>
            );
          })()}

        {/* 저장된 항목들 (inline 모드 또는 modal 모드에서 편집 완료된 항목) */}
        {items.map((item, index) => {
          // Modal 모드에서 현재 편집 중인 항목은 위에서 렌더링하므로 스킵
          if (isModalMode && isEditing && item.id === editingItemId) {
            return null;
          }

          // Modal 모드: 작은 요약 카드
          if (isModalMode) {
            return (
              <Card key={item.id} className="bg-muted/50 border shadow-sm">
                <CardContent className="p-3">
                  <div className="mb-2 flex items-center justify-between">
                    <div className="flex-1">
                      <div className="mb-1 text-sm font-semibold">
                        {index + 1}.{" "}
                        {getFieldValue(item.originalData, componentConfig.displayColumns?.[0]?.name || "") || "항목"}
                      </div>
                      <div className="text-muted-foreground text-xs">
                        {componentConfig.displayColumns
                          ?.map((col) => getFieldValue(item.originalData, col.name))
                          .filter(Boolean)
                          .join(" | ")}
                      </div>
                    </div>
                    <div className="flex items-center gap-2">
                      <Button
                        type="button"
                        variant="ghost"
                        size="sm"
                        onClick={() => {
                          setIsEditing(true);
                          setEditingItemId(item.id);
                          const newDetailId = `detail-${Date.now()}`;
                          setEditingDetailId(newDetailId);
                        }}
                        className="h-7 text-xs text-amber-600"
                      >
                        추가 입력
                      </Button>
                      {componentConfig.allowRemove && (
                        <Button
                          type="button"
                          variant="ghost"
                          size="icon"
                          onClick={() => handleRemoveItem(item.id)}
                          className="h-7 w-7 text-destructive"
                        >
                          <X className="h-3 w-3" />
                        </Button>
                      )}
                    </div>
                  </div>
                  {/* 🆕 입력된 상세 항목들 표시 */}
                  {item.details && item.details.length > 0 && (
                    <div className="border-primary mt-2 space-y-1 border-l-2 pl-4">
                      {item.details.map((detail, detailIdx) => (
                        <div key={detail.id} className="text-primary text-xs">
                          {detailIdx + 1}. {detail[componentConfig.additionalFields?.[0]?.name] || "입력됨"}
                        </div>
                      ))}
                    </div>
                  )}
                </CardContent>
              </Card>
            );
          }

          // Inline 모드: 각 품목마다 여러 상세 항목 표시
          return (
            <Card key={item.id} className="border shadow-sm">
              <CardContent className="space-y-3 p-4">
                {/* 제목 (품명) */}
                <div className="flex items-center justify-between">
                  <div className="text-base font-semibold">
                    {index + 1}.{" "}
                    {getFieldValue(item.originalData, componentConfig.displayColumns?.[0]?.name || "") || "항목"}
                  </div>
                  <Button
                    type="button"
                    onClick={() => {
                      const newDetailId = `detail-${Date.now()}`;
                      handleAddDetail(item.id);
                    }}
                    size="sm"
                    variant="outline"
                    className="text-xs"
                  >
                    + 상세 입력 추가
                  </Button>
                </div>

                {/* 원본 데이터 요약 (작은 텍스트, | 구분자) */}
                <div className="text-muted-foreground text-xs">
                  {componentConfig.displayColumns
                    ?.map((col) => getFieldValue(item.originalData, col.name))
                    .filter(Boolean)
                    .join(" | ")}
                </div>

                {/* 🆕 각 상세 항목 표시 */}
                {item.details && item.details.length > 0 ? (
                  <div className="border-primary space-y-3 border-l-2 pl-4">
                    {item.details.map((detail, detailIdx) => (
                      <Card key={detail.id} className="border-dashed">
                        <CardContent className="space-y-2 p-3">
                          <div className="flex items-center justify-between">
                            <div className="text-xs font-medium">상세 항목 {detailIdx + 1}</div>
                            <Button
                              type="button"
                              variant="ghost"
                              size="sm"
                              onClick={() => handleRemoveDetail(item.id, detail.id)}
                              className="h-6 w-6 p-0 text-destructive"
                            >
                              X
                            </Button>
                          </div>
                          {/* 입력 필드들 */}
                          {renderFieldsByGroup(item.id, detail.id, detail)}
                        </CardContent>
                      </Card>
                    ))}
                  </div>
                ) : (
                  <div className="text-muted-foreground pl-4 text-xs italic">아직 입력된 상세 항목이 없습니다.</div>
                )}
              </CardContent>
            </Card>
          );
        })}

        {/* Modal 모드: 하단 추가 버튼 (항목이 있을 때) */}
        {isModalMode && !isEditing && items.length > 0 && (
          <Button
            type="button"
            onClick={() => {
              // 새 항목 추가 로직은 여기서 처리하지 않고, 기존 항목이 있으면 첫 항목을 편집 모드로
              setIsEditing(true);
              setEditingItemId(items[0]?.id || null);
            }}
            variant="outline"
            size="sm"
            className="w-full border-dashed text-xs sm:text-sm"
          >
            + 추가
          </Button>
        )}
      </div>
    );
  };

  // 기존 테이블 레이아웃 (사용 안 함, 삭제 예정)
  const renderOldGridLayout = () => {
    return (
      <div className="bg-card overflow-auto">
        <Table>
          <TableHeader>
            <TableRow className="bg-background">
              {componentConfig.showIndex && (
                <TableHead className="h-12 w-12 px-4 py-3 text-center text-xs font-semibold sm:text-sm">#</TableHead>
              )}

              {/* 원본 데이터 컬럼 */}
              {componentConfig.displayColumns?.map((col) => (
                <TableHead key={col.name} className="h-12 px-4 py-3 text-xs font-semibold sm:text-sm">
                  {col.label || col.name}
                </TableHead>
              ))}

              {/* 추가 입력 필드 컬럼 */}
              {componentConfig.additionalFields?.map((field) => (
                <TableHead key={field.name} className="h-12 px-4 py-3 text-xs font-semibold sm:text-sm">
                  {field.label}{(field.required || isColumnRequiredByMeta(componentConfig.targetTable, field.name)) && <span className="text-orange-500">*</span>}
                </TableHead>
              ))}

              {componentConfig.allowRemove && (
                <TableHead className="h-12 w-20 px-4 py-3 text-center text-xs font-semibold sm:text-sm">작업</TableHead>
              )}
            </TableRow>
          </TableHeader>
          <TableBody>
            {items.map((item, index) => (
              <TableRow key={item.id} className="bg-background hover:bg-muted/50 transition-colors">
                {/* 인덱스 번호 */}
                {componentConfig.showIndex && (
                  <TableCell className="h-14 px-4 py-3 text-center text-xs font-medium sm:text-sm">
                    {index + 1}
                  </TableCell>
                )}

                {/* 원본 데이터 표시 */}
                {componentConfig.displayColumns?.map((col) => (
                  <TableCell key={col.name} className="h-14 px-4 py-3 text-xs sm:text-sm">
                    {getFieldValue(item.originalData, col.name) || "-"}
                  </TableCell>
                ))}

                {/* 추가 입력 필드 */}
                {componentConfig.additionalFields?.map((field) => (
                  <TableCell key={field.name} className="h-14 px-4 py-3">
                    {renderField(field, item)}
                  </TableCell>
                ))}

                {/* 삭제 버튼 */}
                {componentConfig.allowRemove && (
                  <TableCell className="h-14 px-4 py-3 text-center">
                    {!componentConfig.disabled && !componentConfig.readonly && (
                      <Button
                        type="button"
                        variant="ghost"
                        size="icon"
                        onClick={() => handleRemoveItem(item.id)}
                        className="text-destructive hover:bg-destructive/10 hover:text-destructive h-7 w-7 sm:h-8 sm:w-8"
                        title="항목 제거"
                      >
                        <X className="h-3 w-3 sm:h-4 sm:w-4" />
                      </Button>
                    )}
                  </TableCell>
                )}
              </TableRow>
            ))}
          </TableBody>
        </Table>
      </div>
    );
  };

  // 🆕 Card 레이아웃 렌더링 (Grid와 동일)
  const renderCardLayout = () => {
    return renderGridLayout();
  };

  // 🔧 기존 renderCardLayout (백업 - 사용 안 함)
  const renderCardLayout_OLD = () => {
    const isModalMode = componentConfig.inputMode === "modal";
    return (
      <div className="bg-card space-y-3 p-4">
        {/* Modal 모드: 추가 버튼 */}
        {isModalMode && !isEditing && items.length === 0 && (
          <div className="py-8 text-center">
            <p className="text-muted-foreground mb-4 text-sm">항목을 추가하려면 추가 버튼을 클릭하세요</p>
            <Button type="button" onClick={() => setIsEditing(true)} size="sm" className="text-xs sm:text-sm">
              + 추가
            </Button>
          </div>
        )}

        {/* Modal 모드: 편집 중인 항목 (입력창 표시) */}
        {isModalMode &&
          isEditing &&
          editingItemId &&
          (() => {
            const editingItem = items.find((item) => item.id === editingItemId);
            if (!editingItem) {
              return null;
            }

            return (
              <Card className="border-primary border-2 shadow-sm">
                <CardHeader className="pb-3">
                  <CardTitle className="flex items-center justify-between text-sm font-semibold">
                    <span>
                      품목:{" "}
                      {getFieldValue(editingItem.originalData, componentConfig.displayColumns?.[0]?.name || "") ||
                        "항목"}
                    </span>
                    <Button
                      type="button"
                      variant="ghost"
                      size="sm"
                      onClick={() => {
                        setIsEditing(false);
                        setEditingItemId(null);
                        setEditingDetailId(null);
                      }}
                      className="h-7 text-xs"
                    >
                      취소
                    </Button>
                  </CardTitle>
                </CardHeader>
                <CardContent className="space-y-3">
                  {/* 원본 데이터 요약 */}
                  <div className="text-muted-foreground bg-muted rounded p-2 text-xs">
                    {componentConfig.displayColumns
                      ?.map((col) => editingItem.originalData[col.name])
                      .filter(Boolean)
                      .join(" | ")}
                  </div>

                  {/* 🆕 이미 입력된 상세 항목들 표시 */}
                  {editingItem.details.length > 0 && (
                    <div className="space-y-2">
                      <div className="text-xs font-medium">입력된 항목 ({editingItem.details.length}개)</div>
                      {editingItem.details.map((detail, idx) => (
                        <div
                          key={detail.id}
                          className="bg-muted/30 flex items-center justify-between rounded border p-2 text-xs"
                        >
                          <span>
                            {idx + 1}. {detail[componentConfig.additionalFields?.[0]?.name] || "입력됨"}
                          </span>
                          <Button
                            type="button"
                            variant="ghost"
                            size="sm"
                            onClick={() => handleRemoveDetail(editingItem.id, detail.id)}
                            className="h-6 w-6 p-0"
                          >
                            X
                          </Button>
                        </div>
                      ))}
                    </div>
                  )}

                  {/* 추가 입력 필드 */}
                  {componentConfig.additionalFields &&
                    componentConfig.additionalFields.length > 0 &&
                    editingDetailId &&
                    (() => {
                      // 현재 편집 중인 detail 찾기 (없으면 빈 객체)
                      const currentDetail = editingItem.details.find((d) => d.id === editingDetailId) || {
                        id: editingDetailId,
                      };
                      return renderFieldsByGroup(editingItem.id, editingDetailId, currentDetail);
                    })()}

                  {/* 액션 버튼들 */}
                  <div className="flex justify-end gap-2 pt-2">
                    <Button
                      type="button"
                      onClick={() => handleAddDetail(editingItem.id)}
                      size="sm"
                      variant="outline"
                      className="text-xs"
                    >
                      + 이 품목에 추가 입력
                    </Button>
                    <Button type="button" variant="default" onClick={handleNextItem} size="sm" className="text-xs">
                      다음 품목
                    </Button>
                  </div>
                </CardContent>
              </Card>
            );
          })()}

        {/* 저장된 항목들 (inline 모드 또는 modal 모드에서 편집 완료된 항목) */}
        {items.map((item, index) => {
          // Modal 모드에서 현재 편집 중인 항목은 위에서 렌더링하므로 스킵
          if (isModalMode && isEditing && item.id === editingItemId) {
            return null;
          }

          // Modal 모드: 작은 요약 카드
          if (isModalMode) {
            return (
              <Card key={item.id} className="bg-muted/50 border shadow-sm">
                <CardContent className="flex items-center justify-between p-3">
                  <div className="flex-1">
                    <div className="mb-1 text-sm font-semibold">
                      {index + 1}.{" "}
                      {getFieldValue(item.originalData, componentConfig.displayColumns?.[0]?.name || "") || "항목"}
                    </div>
                    <div className="text-muted-foreground text-xs">
                      {componentConfig.displayColumns
                        ?.map((col) => getFieldValue(item.originalData, col.name))
                        .filter(Boolean)
                        .join(" | ")}
                    </div>
                  </div>
                  <div className="flex items-center gap-2">
                    <Button
                      type="button"
                      variant="ghost"
                      size="sm"
                      onClick={() => {
                        setIsEditing(true);
                        setEditingItemId(item.id);
                      }}
                      className="h-7 text-xs text-amber-600"
                    >
                      수정
                    </Button>
                    {componentConfig.allowRemove && (
                      <Button
                        type="button"
                        variant="ghost"
                        size="sm"
                        onClick={() => handleRemoveItem(item.id)}
                        className="text-destructive h-7 text-xs"
                      >
                        X
                      </Button>
                    )}
                  </div>
                </CardContent>
              </Card>
            );
          }

          // Inline 모드: 각 품목마다 여러 상세 항목 표시
          return (
            <Card key={item.id} className="border shadow-sm">
              <CardContent className="space-y-3 p-4">
                {/* 제목 (품명) */}
                <div className="flex items-center justify-between">
                  <div className="text-base font-semibold">
                    {index + 1}.{" "}
                    {getFieldValue(item.originalData, componentConfig.displayColumns?.[0]?.name || "") || "항목"}
                  </div>
                  <Button
                    type="button"
                    onClick={() => {
                      const newDetailId = `detail-${Date.now()}`;
                      handleAddDetail(item.id);
                    }}
                    size="sm"
                    variant="outline"
                    className="text-xs"
                  >
                    + 상세 입력 추가
                  </Button>
                </div>

                {/* 원본 데이터 요약 (작은 텍스트, | 구분자) */}
                <div className="text-muted-foreground text-xs">
                  {componentConfig.displayColumns
                    ?.map((col) => getFieldValue(item.originalData, col.name))
                    .filter(Boolean)
                    .join(" | ")}
                </div>

                {/* 🆕 각 상세 항목 표시 */}
                {item.details && item.details.length > 0 ? (
                  <div className="border-primary space-y-3 border-l-2 pl-4">
                    {item.details.map((detail, detailIdx) => (
                      <Card key={detail.id} className="border-dashed">
                        <CardContent className="space-y-2 p-3">
                          <div className="flex items-center justify-between">
                            <div className="text-xs font-medium">상세 항목 {detailIdx + 1}</div>
                            <Button
                              type="button"
                              variant="ghost"
                              size="sm"
                              onClick={() => handleRemoveDetail(item.id, detail.id)}
                              className="h-6 w-6 p-0 text-destructive"
                            >
                              X
                            </Button>
                          </div>
                          {/* 입력 필드들 */}
                          {renderFieldsByGroup(item.id, detail.id, detail)}
                        </CardContent>
                      </Card>
                    ))}
                  </div>
                ) : (
                  <div className="text-muted-foreground pl-4 text-xs italic">아직 입력된 상세 항목이 없습니다.</div>
                )}
              </CardContent>
            </Card>
          );
        })}

        {/* Modal 모드: 하단 추가 버튼 (항목이 있을 때) */}
        {isModalMode && !isEditing && items.length > 0 && (
          <Button
            type="button"
            onClick={() => {
              // 새 항목 추가 로직은 여기서 처리하지 않고, 기존 항목이 있으면 첫 항목을 편집 모드로
              setIsEditing(true);
              setEditingItemId(items[0]?.id || null);
            }}
            variant="outline"
            size="sm"
            className="w-full border-dashed text-xs sm:text-sm"
          >
            + 추가
          </Button>
        )}
      </div>
    );
  };

  return (
    <div style={componentStyle} className={cn("space-y-4", className)} onClick={handleClick}>
      {/* 레이아웃에 따라 렌더링 */}
      {componentConfig.layout === "grid" ? renderGridLayout() : renderCardLayout()}

      {/* 항목 수 표시 */}
      <div className="text-muted-foreground flex justify-between text-xs">
        <span>총 {items.length}개 항목</span>
        {componentConfig.targetTable && <span>저장 대상: {componentConfig.targetTable}</span>}
      </div>
    </div>
  );
};

/**
 * SelectedItemsDetailInput 래퍼 컴포넌트
 * 추가적인 로직이나 상태 관리가 필요한 경우 사용
 */
export const SelectedItemsDetailInputWrapper: React.FC<SelectedItemsDetailInputComponentProps> = (props) => {
  return <SelectedItemsDetailInputComponent {...props} />;
};
