"use client";

import React, { useState, useEffect, useCallback, useMemo, useRef } from "react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Textarea } from "@/components/ui/textarea";
import { Checkbox } from "@/components/ui/checkbox";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible";
import {
  AlertDialog,
  AlertDialogAction,
  AlertDialogCancel,
  AlertDialogContent,
  AlertDialogDescription,
  AlertDialogFooter,
  AlertDialogHeader,
  AlertDialogTitle,
} from "@/components/ui/alert-dialog";
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card";
import { ChevronDown, ChevronUp, ChevronRight, Plus, Trash2, RefreshCw, Loader2 } from "lucide-react";
import { toast } from "sonner";
import { cn } from "@/lib/utils";
import { apiClient } from "@/lib/api/client";
import { generateNumberingCode, allocateNumberingCode, previewNumberingCode } from "@/lib/api/numberingRule";
import { useCascadingDropdown } from "@/hooks/useCascadingDropdown";
import { CascadingDropdownConfig } from "@/types/screen-management";

import {
  UniversalFormModalComponentProps,
  UniversalFormModalConfig,
  FormSectionConfig,
  FormFieldConfig,
  FormDataState,
  RepeatSectionItem,
  SelectOptionConfig,
  OptionalFieldGroupConfig,
} from "./types";
import { defaultConfig, generateUniqueId } from "./config";
import { TableSectionRenderer } from "./TableSectionRenderer";

/**
 * 🔗 연쇄 드롭다운 Select 필드 컴포넌트
 */
interface CascadingSelectFieldProps {
  fieldId: string;
  config: CascadingDropdownConfig;
  parentValue?: string | number | null;
  value?: string;
  onChange: (value: string) => void;
  placeholder?: string;
  disabled?: boolean;
}

const CascadingSelectField: React.FC<CascadingSelectFieldProps> = ({
  fieldId,
  config,
  parentValue,
  value,
  onChange,
  placeholder,
  disabled,
}) => {
  const { options, loading } = useCascadingDropdown({
    config,
    parentValue,
  });

  const getPlaceholder = () => {
    if (!parentValue) {
      return config.emptyParentMessage || "상위 항목을 먼저 선택하세요";
    }
    if (loading) {
      return config.loadingMessage || "로딩 중...";
    }
    if (options.length === 0) {
      return config.noOptionsMessage || "선택 가능한 항목이 없습니다";
    }
    return placeholder || "선택하세요";
  };

  const isDisabled = disabled || !parentValue || loading;

  return (
    <Select value={value || ""} onValueChange={onChange} disabled={isDisabled}>
      <SelectTrigger id={fieldId} className="w-full">
        {loading ? (
          <div className="flex items-center gap-2">
            <Loader2 className="h-4 w-4 animate-spin" />
            <span className="text-muted-foreground text-sm">로딩 중...</span>
          </div>
        ) : (
          <SelectValue placeholder={getPlaceholder()} />
        )}
      </SelectTrigger>
      <SelectContent>
        {options.length === 0 ? (
          <div className="text-muted-foreground px-2 py-4 text-center text-sm">
            {!parentValue
              ? config.emptyParentMessage || "상위 항목을 먼저 선택하세요"
              : config.noOptionsMessage || "선택 가능한 항목이 없습니다"}
          </div>
        ) : (
          options
            .filter((option) => option.value && option.value !== "")
            .map((option) => (
              <SelectItem key={option.value} value={option.value}>
                {option.label}
              </SelectItem>
            ))
        )}
      </SelectContent>
    </Select>
  );
};

/**
 * 범용 폼 모달 컴포넌트
 *
 * 섹션 기반 폼 레이아웃, 채번규칙, 다중 행 저장을 지원합니다.
 */
export function UniversalFormModalComponent({
  component,
  config: propConfig,
  isDesignMode = false,
  isSelected = false,
  className,
  style,
  initialData: propInitialData,
  // DynamicComponentRenderer에서 전달되는 props (DOM 전달 방지를 위해 _ prefix 사용)
  _initialData,
  _originalData,
  _groupedData,
  onSave,
  onCancel,
  onChange,
  ...restProps // 나머지 props는 DOM에 전달하지 않음
}: UniversalFormModalComponentProps & { _initialData?: any; _originalData?: any; _groupedData?: any }) {
  // initialData 우선순위: 직접 전달된 prop > DynamicComponentRenderer에서 전달된 prop
  const initialData = propInitialData || _initialData;
  // 설정 병합
  const config: UniversalFormModalConfig = useMemo(() => {
    const componentConfig = component?.config || {};
    return {
      ...defaultConfig,
      ...propConfig,
      ...componentConfig,
      modal: {
        ...defaultConfig.modal,
        ...propConfig?.modal,
        ...componentConfig.modal,
      },
      saveConfig: {
        ...defaultConfig.saveConfig,
        ...propConfig?.saveConfig,
        ...componentConfig.saveConfig,
        multiRowSave: {
          ...defaultConfig.saveConfig.multiRowSave,
          ...propConfig?.saveConfig?.multiRowSave,
          ...componentConfig.saveConfig?.multiRowSave,
        },
        afterSave: {
          ...defaultConfig.saveConfig.afterSave,
          ...propConfig?.saveConfig?.afterSave,
          ...componentConfig.saveConfig?.afterSave,
        },
      },
    };
  }, [component?.config, propConfig]);

  // 폼 데이터 상태
  const [formData, setFormData] = useState<FormDataState>({});
  const [, setOriginalData] = useState<Record<string, any>>({});

  // 반복 섹션 데이터
  const [repeatSections, setRepeatSections] = useState<{
    [sectionId: string]: RepeatSectionItem[];
  }>({});

  // 섹션 접힘 상태
  const [collapsedSections, setCollapsedSections] = useState<Set<string>>(new Set());

  // 옵셔널 필드 그룹 활성화 상태 (섹션ID-그룹ID 조합)
  const [activatedOptionalFieldGroups, setActivatedOptionalFieldGroups] = useState<Set<string>>(new Set());

  // Select 옵션 캐시
  const [selectOptionsCache, setSelectOptionsCache] = useState<{
    [key: string]: { value: string; label: string }[];
  }>({});

  // 연동 필드 그룹 데이터 캐시 (테이블별 데이터)
  const [linkedFieldDataCache, setLinkedFieldDataCache] = useState<{
    [tableKey: string]: Record<string, any>[];
  }>({});

  // 로딩 상태
  const [saving, setSaving] = useState(false);

  // 🆕 수정 모드: 원본 그룹 데이터 (INSERT/UPDATE/DELETE 추적용)
  const [originalGroupedData, setOriginalGroupedData] = useState<any[]>([]);
  const groupedDataInitializedRef = useRef(false);

  // 삭제 확인 다이얼로그
  const [deleteDialog, setDeleteDialog] = useState<{
    open: boolean;
    sectionId: string;
    itemId: string;
  }>({ open: false, sectionId: "", itemId: "" });

  // 초기 데이터를 한 번만 캡처 (컴포넌트 마운트 시)
  const capturedInitialData = useRef<Record<string, any> | undefined>(undefined);
  const hasInitialized = useRef(false);
  // 마지막으로 초기화된 데이터의 ID를 추적 (수정 모달에서 다른 항목 선택 시 재초기화 필요)
  const lastInitializedId = useRef<string | undefined>(undefined);

  // 초기화 - 최초 마운트 시 또는 initialData가 변경되었을 때 실행
  useEffect(() => {
    console.log("[UniversalFormModal] useEffect 시작", {
      initialData,
      hasInitialized: hasInitialized.current,
      lastInitializedId: lastInitializedId.current,
    });
    
    // initialData에서 ID 값 추출 (id, ID, objid 등)
    const currentId = initialData?.id || initialData?.ID || initialData?.objid;
    const currentIdString = currentId !== undefined ? String(currentId) : undefined;
    
    // 생성 모드에서 부모로부터 전달받은 데이터 해시 (ID가 없을 때만)
    const createModeDataHash = !currentIdString && initialData && Object.keys(initialData).length > 0
      ? JSON.stringify(initialData)
      : undefined;

    // 이미 초기화되었고, ID가 동일하고, 생성 모드 데이터도 동일하면 스킵
    if (hasInitialized.current && lastInitializedId.current === currentIdString) {
      // 생성 모드에서 데이터가 새로 전달된 경우는 재초기화 필요
      if (!createModeDataHash || capturedInitialData.current) {
        console.log("[UniversalFormModal] 초기화 스킵 - 이미 초기화됨");
        // 🆕 채번 플래그가 true인데 formData에 값이 없으면 재생성 필요
        // (컴포넌트 remount로 인해 state가 초기화된 경우)
        return;
      }
    }
    
    // 🆕 컴포넌트 remount 감지: hasInitialized가 true인데 formData가 비어있으면 재초기화
    // (React의 Strict Mode나 EmbeddedScreen 리렌더링으로 인한 remount)
    if (hasInitialized.current && !currentIdString) {
      console.log("[UniversalFormModal] 컴포넌트 remount 감지 - 채번 플래그 초기화");
      numberingGeneratedRef.current = false;
      isGeneratingRef.current = false;
    }

    // 🆕 수정 모드: initialData에 데이터가 있으면서 ID가 변경된 경우 재초기화
    if (hasInitialized.current && currentIdString && lastInitializedId.current !== currentIdString) {
      console.log("[UniversalFormModal] ID 변경 감지 - 재초기화:", {
        prevId: lastInitializedId.current,
        newId: currentIdString,
        initialData: initialData,
      });
      // 채번 플래그 초기화 (새 항목이므로)
      numberingGeneratedRef.current = false;
      isGeneratingRef.current = false;
    }

    // 최초 initialData 캡처 (이후 변경되어도 이 값 사용)
    if (initialData && Object.keys(initialData).length > 0) {
      capturedInitialData.current = JSON.parse(JSON.stringify(initialData)); // 깊은 복사
      lastInitializedId.current = currentIdString;
      console.log("[UniversalFormModal] 초기 데이터 캡처:", capturedInitialData.current);
    }

    console.log("[UniversalFormModal] initializeForm 호출 예정");
    hasInitialized.current = true;
    initializeForm();
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [initialData]); // initialData 전체 변경 시 재초기화

  // config 변경 시에만 재초기화 (initialData 변경은 무시) - 채번규칙 제외
  useEffect(() => {
    if (!hasInitialized.current) return; // 최초 초기화 전이면 스킵

    console.log("[useEffect config 변경] 재초기화 스킵 (채번 중복 방지)");
    // initializeForm(); // 주석 처리 - config 변경 시 재초기화 안 함 (채번 중복 방지)
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [config]);

  // 컴포넌트 unmount 시 채번 플래그 초기화
  useEffect(() => {
    return () => {
      console.log("[채번] 컴포넌트 unmount - 플래그 초기화");
      numberingGeneratedRef.current = false;
      isGeneratingRef.current = false;
    };
  }, []);

  // 🆕 beforeFormSave 이벤트 리스너 - ButtonPrimary 저장 시 formData를 전달
  // 설정된 필드(columnName)만 병합하여 의도치 않은 덮어쓰기 방지
  useEffect(() => {
    const handleBeforeFormSave = (event: Event) => {
      if (!(event instanceof CustomEvent) || !event.detail?.formData) return;

      // 설정에 정의된 필드 columnName 목록 수집
      const configuredFields = new Set<string>();
      config.sections.forEach((section) => {
        (section.fields || []).forEach((field) => {
          if (field.columnName) {
            configuredFields.add(field.columnName);
          }
        });
      });

      console.log("[UniversalFormModal] beforeFormSave 이벤트 수신");
      console.log("[UniversalFormModal] 설정된 필드 목록:", Array.from(configuredFields));

      // UniversalFormModal에 설정된 필드만 병합 (채번 규칙 포함)
      // 외부 formData에 이미 값이 있어도 UniversalFormModal 값으로 덮어씀
      // (UniversalFormModal이 해당 필드의 주인이므로)
      for (const [key, value] of Object.entries(formData)) {
        // 설정에 정의된 필드 또는 채번 규칙 ID 필드만 병합
        const isConfiguredField = configuredFields.has(key);
        const isNumberingRuleId = key.endsWith("_numberingRuleId");

        if (isConfiguredField || isNumberingRuleId) {
          if (value !== undefined && value !== null && value !== "") {
            event.detail.formData[key] = value;
            console.log(`[UniversalFormModal] 필드 병합: ${key} =`, value);
          }
        }
      }

      // 반복 섹션 데이터도 병합 (필요한 경우)
      if (Object.keys(repeatSections).length > 0) {
        for (const [sectionId, items] of Object.entries(repeatSections)) {
          const sectionKey = `_repeatSection_${sectionId}`;
          event.detail.formData[sectionKey] = items;
          console.log(`[UniversalFormModal] 반복 섹션 병합: ${sectionKey}`, items);
        }
      }

      // 🆕 수정 모드: 원본 그룹 데이터 전달 (UPDATE/DELETE 추적용)
      if (originalGroupedData.length > 0) {
        event.detail.formData._originalGroupedData = originalGroupedData;
        console.log(`[UniversalFormModal] 원본 그룹 데이터 병합: ${originalGroupedData.length}개`);
      }
    };

    window.addEventListener("beforeFormSave", handleBeforeFormSave as EventListener);

    return () => {
      window.removeEventListener("beforeFormSave", handleBeforeFormSave as EventListener);
    };
  }, [formData, repeatSections, config.sections, originalGroupedData]);

  // 🆕 수정 모드: _groupedData가 있으면 테이블 섹션 초기화
  useEffect(() => {
    if (!_groupedData || _groupedData.length === 0) return;
    if (groupedDataInitializedRef.current) return; // 이미 초기화됨

    // 테이블 타입 섹션 찾기
    const tableSection = config.sections.find((s) => s.type === "table");
    if (!tableSection) {
      console.log("[UniversalFormModal] 테이블 섹션 없음 - _groupedData 무시");
      return;
    }

    console.log("[UniversalFormModal] 수정 모드 - 테이블 섹션 초기화:", {
      sectionId: tableSection.id,
      itemCount: _groupedData.length,
    });

    // 원본 데이터 저장 (수정/삭제 추적용)
    setOriginalGroupedData(JSON.parse(JSON.stringify(_groupedData)));

    // 테이블 섹션 데이터 설정
    const tableSectionKey = `_tableSection_${tableSection.id}`;
    setFormData((prev) => ({
      ...prev,
      [tableSectionKey]: _groupedData,
    }));

    groupedDataInitializedRef.current = true;
  }, [_groupedData, config.sections]);

  // 필드 레벨 linkedFieldGroup 데이터 로드
  useEffect(() => {
    const loadData = async () => {
      const tablesToLoad = new Set<string>();

      // 모든 섹션의 필드에서 linkedFieldGroup.sourceTable 수집
      config.sections.forEach((section) => {
        (section.fields || []).forEach((field) => {
          if (field.linkedFieldGroup?.enabled && field.linkedFieldGroup?.sourceTable) {
            tablesToLoad.add(field.linkedFieldGroup.sourceTable);
          }
        });
      });

      // 각 테이블 데이터 로드
      for (const tableName of tablesToLoad) {
        if (!linkedFieldDataCache[tableName]) {
          await loadLinkedFieldData(tableName);
        }
      }
    };

    loadData();
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [config.sections]);

  // 채번규칙 자동 생성 (중복 호출 방지)
  // 중요: initializeForm에서 호출되므로 반드시 initializeForm보다 먼저 선언해야 함
  const numberingGeneratedRef = useRef(false);
  const isGeneratingRef = useRef(false); // 진행 중 플래그 추가

  const generateNumberingValues = useCallback(
    async (currentFormData: FormDataState) => {
      // 이미 생성되었거나 진행 중이면 스킵
      if (numberingGeneratedRef.current) {
        console.log("[채번] 이미 생성됨 - 스킵");
        return;
      }

      if (isGeneratingRef.current) {
        console.log("[채번] 생성 진행 중 - 스킵");
        return;
      }

      isGeneratingRef.current = true; // 진행 중 표시
      console.log("[채번] 생성 시작", { sectionsCount: config.sections.length });

      const updatedData = { ...currentFormData };
      let hasChanges = false;

      for (const section of config.sections) {
        console.log("[채번] 섹션 검사:", section.title, { type: section.type, repeatable: section.repeatable, fieldsCount: section.fields?.length });
        if (section.repeatable || section.type === "table") continue;

        for (const field of (section.fields || [])) {
          // generateOnOpen은 기본값 true (undefined일 경우 true로 처리)
          const shouldGenerateOnOpen = field.numberingRule?.generateOnOpen !== false;
          console.log("[채번] 필드 검사:", field.columnName, {
            hasNumberingRule: !!field.numberingRule,
            enabled: field.numberingRule?.enabled,
            generateOnOpen: field.numberingRule?.generateOnOpen,
            shouldGenerateOnOpen,
            ruleId: field.numberingRule?.ruleId,
            currentValue: updatedData[field.columnName],
          });
          if (
            field.numberingRule?.enabled &&
            shouldGenerateOnOpen &&
            field.numberingRule?.ruleId &&
            !updatedData[field.columnName]
          ) {
            try {
              console.log(`[채번 미리보기 API 호출] ${field.columnName}, ruleId: ${field.numberingRule.ruleId}`);
              // generateOnOpen: 미리보기만 표시 (DB 시퀀스 증가 안 함)
              const response = await previewNumberingCode(field.numberingRule.ruleId);
              if (response.success && response.data?.generatedCode) {
                updatedData[field.columnName] = response.data.generatedCode;

                // 저장 시 실제 할당을 위해 ruleId 저장 (TextInput과 동일한 키 형식)
                const ruleIdKey = `${field.columnName}_numberingRuleId`;
                updatedData[ruleIdKey] = field.numberingRule.ruleId;

                hasChanges = true;
                numberingGeneratedRef.current = true; // 생성 완료 표시
                console.log(
                  `[채번 미리보기 완료] ${field.columnName} = ${response.data.generatedCode} (저장 시 실제 할당)`,
                );
                console.log(`[채번 규칙 ID 저장] ${ruleIdKey} = ${field.numberingRule.ruleId}`);

                // 부모 컴포넌트에도 ruleId 전달 (ModalRepeaterTable → ScreenModal)
                if (onChange) {
                  onChange({
                    ...updatedData,
                    [ruleIdKey]: field.numberingRule.ruleId,
                  });
                  console.log(`[채번] 부모에게 ruleId 전달: ${ruleIdKey}`);
                }
              }
            } catch (error) {
              console.error(`채번규칙 미리보기 실패 (${field.columnName}):`, error);
            }
          }
        }
      }

      isGeneratingRef.current = false; // 진행 완료

      if (hasChanges) {
        setFormData(updatedData);
      }
    },
    [config, onChange],
  );

  // 폼 초기화
  const initializeForm = useCallback(async () => {
    console.log("[initializeForm] 시작");

    // 캡처된 initialData 사용 (props로 전달된 initialData가 아닌)
    const effectiveInitialData = capturedInitialData.current || initialData;

    console.log("[initializeForm] 초기 데이터:", {
      capturedInitialData: capturedInitialData.current,
      initialData: initialData,
      effectiveInitialData: effectiveInitialData,
      hasData: effectiveInitialData && Object.keys(effectiveInitialData).length > 0,
    });

    const newFormData: FormDataState = {};
    const newRepeatSections: { [sectionId: string]: RepeatSectionItem[] } = {};
    const newCollapsed = new Set<string>();
    const newActivatedGroups = new Set<string>();

    // 섹션별 초기화
    for (const section of config.sections) {
      // 접힘 상태 초기화
      if (section.defaultCollapsed) {
        newCollapsed.add(section.id);
      }

      if (section.repeatable) {
        // 반복 섹션 초기화
        const minItems = section.repeatConfig?.minItems || 0;
        const items: RepeatSectionItem[] = [];
        for (let i = 0; i < minItems; i++) {
          items.push(createRepeatItem(section, i));
        }
        newRepeatSections[section.id] = items;
      } else if (section.type === "table") {
        // 테이블 섹션은 필드 초기화 스킵 (TableSectionRenderer에서 처리)
        continue;
      } else {
        // 일반 섹션 필드 초기화
        for (const field of (section.fields || [])) {
          // 기본값 설정
          let value = field.defaultValue ?? "";

          // 부모에서 전달받은 값 적용 (receiveFromParent 또는 effectiveInitialData에 해당 값이 있으면)
          if (effectiveInitialData) {
            const parentField = field.parentFieldName || field.columnName;
            if (effectiveInitialData[parentField] !== undefined) {
              // receiveFromParent가 true이거나, effectiveInitialData에 값이 있으면 적용
              if (field.receiveFromParent || value === "" || value === undefined) {
                value = effectiveInitialData[parentField];
              }
            }
          }

          newFormData[field.columnName] = value;
        }

        // 옵셔널 필드 그룹 처리
        if (section.optionalFieldGroups) {
          for (const group of section.optionalFieldGroups) {
            const key = `${section.id}-${group.id}`;
            
            // 수정 모드: triggerField 값이 triggerValueOnAdd와 일치하면 그룹 자동 활성화
            if (effectiveInitialData && group.triggerField && group.triggerValueOnAdd !== undefined) {
              const triggerValue = effectiveInitialData[group.triggerField];
              if (triggerValue === group.triggerValueOnAdd) {
                newActivatedGroups.add(key);
                console.log(`[initializeForm] 옵셔널 그룹 자동 활성화: ${key}, triggerField=${group.triggerField}, value=${triggerValue}`);
                
                // 활성화된 그룹의 필드값도 초기화
                for (const field of group.fields || []) {
                  let value = field.defaultValue ?? "";
                  const parentField = field.parentFieldName || field.columnName;
                  if (effectiveInitialData[parentField] !== undefined) {
                    value = effectiveInitialData[parentField];
                  }
                  newFormData[field.columnName] = value;
                }
              }
            }
            
            // 신규 등록 모드: triggerValueOnRemove를 기본값으로 설정
            if (group.triggerField && group.triggerValueOnRemove !== undefined) {
              // effectiveInitialData에 해당 값이 없는 경우에만 기본값 설정
              if (!effectiveInitialData || effectiveInitialData[group.triggerField] === undefined) {
                newFormData[group.triggerField] = group.triggerValueOnRemove;
              }
            }
          }
        }
      }
    }

    setFormData(newFormData);
    setRepeatSections(newRepeatSections);
    setCollapsedSections(newCollapsed);
    setActivatedOptionalFieldGroups(newActivatedGroups);
    setOriginalData(effectiveInitialData || {});

    // 수정 모드에서 서브 테이블 데이터 로드 (겸직 등)
    const multiTable = config.saveConfig?.customApiSave?.multiTable;
    if (multiTable && effectiveInitialData) {
      const pkColumn = multiTable.mainTable?.primaryKeyColumn;
      const pkValue = effectiveInitialData[pkColumn];
      
      // PK 값이 있으면 수정 모드로 판단
      if (pkValue) {
        console.log("[initializeForm] 수정 모드 - 서브 테이블 데이터 로드 시작");
        
        for (const subTableConfig of multiTable.subTables || []) {
          // loadOnEdit 옵션이 활성화된 경우에만 로드
          if (!subTableConfig.enabled || !subTableConfig.options?.loadOnEdit) {
            continue;
          }
          
          const { tableName, linkColumn, repeatSectionId, fieldMappings, options } = subTableConfig;
          if (!tableName || !linkColumn?.subColumn || !repeatSectionId) {
            continue;
          }
          
          try {
            // 서브 테이블에서 데이터 조회
            const filters: Record<string, any> = {
              [linkColumn.subColumn]: pkValue,
            };
            
            // 서브 항목만 로드 (메인 항목 제외)
            if (options?.loadOnlySubItems && options?.mainMarkerColumn) {
              filters[options.mainMarkerColumn] = options.subMarkerValue ?? false;
            }
            
            console.log(`[initializeForm] 서브 테이블 ${tableName} 조회:`, filters);
            
            const response = await apiClient.get(`/table-management/tables/${tableName}/data`, {
              params: {
                filters: JSON.stringify(filters),
                page: 1,
                pageSize: 100,
              },
            });
            
            if (response.data?.success && response.data?.data?.items) {
              const subItems = response.data.data.items;
              console.log(`[initializeForm] 서브 테이블 ${tableName} 데이터 ${subItems.length}건 로드됨`);
              
              // 역매핑: 서브 테이블 데이터 → 반복 섹션 데이터
              const repeatItems: RepeatSectionItem[] = subItems.map((item: any, index: number) => {
                const repeatItem: RepeatSectionItem = {
                  _id: generateUniqueId("repeat"),
                  _index: index,
                  _originalData: item, // 원본 데이터 보관 (수정 시 필요)
                };
                
                // 필드 매핑 역변환 (targetColumn → formField)
                for (const mapping of fieldMappings || []) {
                  if (mapping.formField && mapping.targetColumn) {
                    repeatItem[mapping.formField] = item[mapping.targetColumn];
                  }
                }
                
                return repeatItem;
              });
              
              // 반복 섹션에 데이터 설정
              newRepeatSections[repeatSectionId] = repeatItems;
              setRepeatSections({ ...newRepeatSections });
              console.log(`[initializeForm] 반복 섹션 ${repeatSectionId}에 ${repeatItems.length}건 설정`);
            }
          } catch (error) {
            console.error(`[initializeForm] 서브 테이블 ${tableName} 로드 실패:`, error);
          }
        }
      }
    }

    // 채번규칙 자동 생성
    console.log("[initializeForm] generateNumberingValues 호출");
    await generateNumberingValues(newFormData);
    console.log("[initializeForm] 완료");
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [config]); // initialData는 의존성에서 제거 (capturedInitialData.current 사용)

  // 반복 섹션 아이템 생성
  const createRepeatItem = (section: FormSectionConfig, index: number): RepeatSectionItem => {
    const item: RepeatSectionItem = {
      _id: generateUniqueId("repeat"),
      _index: index,
    };

    for (const field of (section.fields || [])) {
      item[field.columnName] = field.defaultValue ?? "";
    }

    return item;
  };

  // 필드 값 변경 핸들러
  const handleFieldChange = useCallback(
    (columnName: string, value: any) => {
      setFormData((prev) => {
        const newData = { ...prev, [columnName]: value };
        // onChange는 렌더링 외부에서 호출해야 함 (setTimeout 사용)
        if (onChange) {
          setTimeout(() => onChange(newData), 0);
        }
        return newData;
      });
    },
    [onChange],
  );

  // 반복 섹션 필드 값 변경 핸들러
  const handleRepeatFieldChange = useCallback((sectionId: string, itemId: string, columnName: string, value: any) => {
    setRepeatSections((prev) => {
      const items = prev[sectionId] || [];
      const newItems = items.map((item) => (item._id === itemId ? { ...item, [columnName]: value } : item));
      return { ...prev, [sectionId]: newItems };
    });
  }, []);

  // 반복 섹션 아이템 추가
  const handleAddRepeatItem = useCallback(
    (sectionId: string) => {
      const section = config.sections.find((s) => s.id === sectionId);
      if (!section) return;

      const maxItems = section.repeatConfig?.maxItems || 10;

      setRepeatSections((prev) => {
        const items = prev[sectionId] || [];
        if (items.length >= maxItems) {
          toast.error(`최대 ${maxItems}개까지만 추가할 수 있습니다.`);
          return prev;
        }

        const newItem = createRepeatItem(section, items.length);
        return { ...prev, [sectionId]: [...items, newItem] };
      });
    },
    [config],
  );

  // 반복 섹션 아이템 삭제
  const handleRemoveRepeatItem = useCallback(
    (sectionId: string, itemId: string) => {
      const section = config.sections.find((s) => s.id === sectionId);
      if (!section) return;

      const minItems = section.repeatConfig?.minItems || 0;

      setRepeatSections((prev) => {
        const items = prev[sectionId] || [];
        if (items.length <= minItems) {
          toast.error(`최소 ${minItems}개는 유지해야 합니다.`);
          return prev;
        }

        const newItems = items.filter((item) => item._id !== itemId).map((item, index) => ({ ...item, _index: index }));

        return { ...prev, [sectionId]: newItems };
      });

      setDeleteDialog({ open: false, sectionId: "", itemId: "" });
    },
    [config],
  );

  // 섹션 접힘 토글
  const toggleSectionCollapse = useCallback((sectionId: string) => {
    setCollapsedSections((prev) => {
      const newSet = new Set(prev);
      if (newSet.has(sectionId)) {
        newSet.delete(sectionId);
      } else {
        newSet.add(sectionId);
      }
      return newSet;
    });
  }, []);

  // 옵셔널 필드 그룹 활성화
  const activateOptionalFieldGroup = useCallback((sectionId: string, groupId: string) => {
    const section = config.sections.find((s) => s.id === sectionId);
    const group = section?.optionalFieldGroups?.find((g) => g.id === groupId);
    if (!group) return;

    const key = `${sectionId}-${groupId}`;
    setActivatedOptionalFieldGroups((prev) => {
      const newSet = new Set(prev);
      newSet.add(key);
      return newSet;
    });

    // 연동 필드 값 변경 (추가 시)
    if (group.triggerField && group.triggerValueOnAdd !== undefined) {
      handleFieldChange(group.triggerField, group.triggerValueOnAdd);
    }
  }, [config, handleFieldChange]);

  // 옵셔널 필드 그룹 비활성화
  const deactivateOptionalFieldGroup = useCallback((sectionId: string, groupId: string) => {
    const section = config.sections.find((s) => s.id === sectionId);
    const group = section?.optionalFieldGroups?.find((g) => g.id === groupId);
    if (!group) return;

    const key = `${sectionId}-${groupId}`;
    setActivatedOptionalFieldGroups((prev) => {
      const newSet = new Set(prev);
      newSet.delete(key);
      return newSet;
    });

    // 연동 필드 값 변경 (제거 시)
    if (group.triggerField && group.triggerValueOnRemove !== undefined) {
      handleFieldChange(group.triggerField, group.triggerValueOnRemove);
    }

    // 옵셔널 필드 그룹 필드 값 초기화
    (group.fields || []).forEach((field) => {
      handleFieldChange(field.columnName, field.defaultValue || "");
    });
  }, [config, handleFieldChange]);

  // Select 옵션 로드
  const loadSelectOptions = useCallback(
    async (fieldId: string, optionConfig: SelectOptionConfig): Promise<{ value: string; label: string }[]> => {
      // 캐시 확인
      if (selectOptionsCache[fieldId]) {
        return selectOptionsCache[fieldId];
      }

      let options: { value: string; label: string }[] = [];

      try {
        if (optionConfig.type === "static") {
          // 직접 입력: 설정된 정적 옵션 사용
          options = optionConfig.staticOptions || [];
        } else if (optionConfig.type === "table" && optionConfig.tableName) {
          // 테이블 참조: POST 방식으로 테이블 데이터 조회 (autoFilter 포함)
          const response = await apiClient.post(`/table-management/tables/${optionConfig.tableName}/data`, {
            page: 1,
            size: 1000,
            autoFilter: { enabled: true, filterColumn: "company_code" },
          });

          // 응답 데이터 파싱
          let dataArray: any[] = [];
          if (response.data?.success) {
            const responseData = response.data?.data;
            if (responseData?.data && Array.isArray(responseData.data)) {
              dataArray = responseData.data;
            } else if (Array.isArray(responseData)) {
              dataArray = responseData;
            } else if (responseData?.rows && Array.isArray(responseData.rows)) {
              dataArray = responseData.rows;
            }
          }

          options = dataArray.map((row: any) => ({
            value: String(row[optionConfig.valueColumn || "id"]),
            label: String(row[optionConfig.labelColumn || "name"]),
          }));
        } else if (optionConfig.type === "code" && optionConfig.categoryKey) {
          // 공통코드(카테고리 컬럼): table_column_category_values 테이블에서 조회
          // categoryKey 형식: "tableName.columnName"
          const [categoryTable, categoryColumn] = optionConfig.categoryKey.split(".");
          if (categoryTable && categoryColumn) {
            const response = await apiClient.get(
              `/table-categories/${categoryTable}/${categoryColumn}/values`
            );
            if (response.data?.success && response.data?.data) {
              // 라벨값을 DB에 저장 (화면에 표시되는 값 그대로 저장)
              options = response.data.data.map((item: any) => ({
                value: item.valueLabel || item.value_label,
                label: item.valueLabel || item.value_label,
              }));
            }
          }
        }

        // 캐시 저장
        setSelectOptionsCache((prev) => ({ ...prev, [fieldId]: options }));
      } catch (error) {
        console.error(`Select 옵션 로드 실패 (${fieldId}):`, error);
      }

      return options;
    },
    [selectOptionsCache],
  );

  // 연동 필드 그룹 데이터 로드
  const loadLinkedFieldData = useCallback(
    async (sourceTable: string): Promise<Record<string, any>[]> => {
      // 캐시 확인 - 이미 배열로 캐시되어 있으면 반환
      if (Array.isArray(linkedFieldDataCache[sourceTable]) && linkedFieldDataCache[sourceTable].length > 0) {
        return linkedFieldDataCache[sourceTable];
      }

      let data: Record<string, any>[] = [];

      try {
        console.log(`[연동필드] ${sourceTable} 데이터 로드 시작`);
        // 현재 회사 기준으로 데이터 조회 (POST 메서드, autoFilter 사용)
        const response = await apiClient.post(`/table-management/tables/${sourceTable}/data`, {
          page: 1,
          size: 1000,
          autoFilter: { enabled: true, filterColumn: "company_code" }, // 현재 회사 기준 자동 필터링
        });

        console.log(`[연동필드] ${sourceTable} API 응답:`, response.data);

        if (response.data?.success) {
          // data 구조 확인: { data: { data: [...], total, page, ... } } 또는 { data: [...] }
          const responseData = response.data?.data;
          if (Array.isArray(responseData)) {
            // 직접 배열인 경우
            data = responseData;
          } else if (responseData?.data && Array.isArray(responseData.data)) {
            // { data: [...], total: ... } 형태 (tableManagementService 응답)
            data = responseData.data;
          } else if (responseData?.rows && Array.isArray(responseData.rows)) {
            // { rows: [...], total: ... } 형태 (다른 API 응답)
            data = responseData.rows;
          }
          console.log(`[연동필드] ${sourceTable} 파싱된 데이터 ${data.length}개:`, data.slice(0, 3));
        }

        // 캐시 저장 (빈 배열이라도 저장하여 중복 요청 방지)
        setLinkedFieldDataCache((prev) => ({ ...prev, [sourceTable]: data }));
      } catch (error) {
        console.error(`연동 필드 데이터 로드 실패 (${sourceTable}):`, error);
        // 실패해도 빈 배열로 캐시하여 무한 요청 방지
        setLinkedFieldDataCache((prev) => ({ ...prev, [sourceTable]: [] }));
      }

      return data;
    },
    [linkedFieldDataCache],
  );

  // 필수 필드 검증
  const validateRequiredFields = useCallback((): { valid: boolean; missingFields: string[] } => {
    const missingFields: string[] = [];

    for (const section of config.sections) {
      if (section.repeatable || section.type === "table") continue; // 반복 섹션 및 테이블 섹션은 별도 검증

      for (const field of (section.fields || [])) {
        if (field.required && !field.hidden && !field.numberingRule?.hidden) {
          const value = formData[field.columnName];
          if (value === undefined || value === null || value === "") {
            missingFields.push(field.label || field.columnName);
          }
        }
      }
    }

    return { valid: missingFields.length === 0, missingFields };
  }, [config.sections, formData]);

  // 단일 행 저장
  const saveSingleRow = useCallback(async () => {
    const dataToSave = { ...formData };
    
    // 테이블 섹션 데이터 추출 (별도 저장용)
    const tableSectionData: Record<string, any[]> = {};

    // 메타데이터 필드 제거 (채번 규칙 ID는 유지 - buttonActions.ts에서 사용)
    Object.keys(dataToSave).forEach((key) => {
      if (key.startsWith("_tableSection_")) {
        // 테이블 섹션 데이터는 별도로 저장
        const sectionId = key.replace("_tableSection_", "");
        tableSectionData[sectionId] = dataToSave[key] || [];
        delete dataToSave[key];
      } else if (key.startsWith("_") && !key.includes("_numberingRuleId")) {
        delete dataToSave[key];
      }
    });

    // 저장 시점 채번규칙 처리 (generateOnSave만 처리)
    for (const section of config.sections) {
      // 테이블 타입 섹션은 건너뛰기
      if (section.type === "table") continue;
      
      for (const field of (section.fields || [])) {
        if (field.numberingRule?.enabled && field.numberingRule?.generateOnSave && field.numberingRule?.ruleId) {
          const response = await allocateNumberingCode(field.numberingRule.ruleId);
          if (response.success && response.data?.generatedCode) {
            dataToSave[field.columnName] = response.data.generatedCode;
            console.log(`[채번 할당] ${field.columnName} = ${response.data.generatedCode}`);
          } else {
            console.error(`[채번 실패] ${field.columnName}:`, response.error);
          }
        }
      }
    }

    // 별도 테이블에 저장해야 하는 테이블 섹션 목록
    const tableSectionsForSeparateTable = config.sections.filter(
      (s) => s.type === "table" && 
        s.tableConfig?.saveConfig?.targetTable && 
        s.tableConfig.saveConfig.targetTable !== config.saveConfig.tableName
    );
    
    // 테이블 섹션이 있고 메인 테이블에 품목별로 저장하는 경우 (공통 + 개별 병합 저장)
    // targetTable이 없거나 메인 테이블과 같은 경우
    const tableSectionsForMainTable = config.sections.filter(
      (s) => s.type === "table" && 
        (!s.tableConfig?.saveConfig?.targetTable || 
         s.tableConfig.saveConfig.targetTable === config.saveConfig.tableName)
    );

    console.log("[saveSingleRow] 메인 테이블:", config.saveConfig.tableName);
    console.log("[saveSingleRow] 메인 테이블에 저장할 테이블 섹션:", tableSectionsForMainTable.map(s => s.id));
    console.log("[saveSingleRow] 별도 테이블에 저장할 테이블 섹션:", tableSectionsForSeparateTable.map(s => s.id));
    console.log("[saveSingleRow] 테이블 섹션 데이터 키:", Object.keys(tableSectionData));
    console.log("[saveSingleRow] dataToSave 키:", Object.keys(dataToSave));

    if (tableSectionsForMainTable.length > 0) {
      // 공통 저장 필드 수집 (sectionSaveModes 설정에 따라)
      const commonFieldsData: Record<string, any> = {};
      const { sectionSaveModes } = config.saveConfig;
      
      // 필드 타입 섹션에서 공통 저장 필드 수집
      for (const section of config.sections) {
        if (section.type === "table") continue;
        
        const sectionMode = sectionSaveModes?.find((s) => s.sectionId === section.id);
        const defaultMode = "common"; // 필드 타입 섹션의 기본값은 공통 저장
        const sectionSaveMode = sectionMode?.saveMode || defaultMode;
        
        if (section.fields) {
          for (const field of section.fields) {
            const fieldOverride = sectionMode?.fieldOverrides?.find((f) => f.fieldName === field.columnName);
            const fieldSaveMode = fieldOverride?.saveMode || sectionSaveMode;
            
            if (fieldSaveMode === "common" && dataToSave[field.columnName] !== undefined) {
              commonFieldsData[field.columnName] = dataToSave[field.columnName];
            }
          }
        }
      }
      
      // 각 테이블 섹션의 품목 데이터에 공통 필드 병합하여 저장
      for (const tableSection of tableSectionsForMainTable) {
        const sectionData = tableSectionData[tableSection.id] || [];
        
        if (sectionData.length > 0) {
          // 품목별로 행 저장
          for (const item of sectionData) {
            const rowToSave = { ...commonFieldsData, ...item };
            
            // _sourceData 등 내부 메타데이터 제거
            Object.keys(rowToSave).forEach((key) => {
              if (key.startsWith("_")) {
                delete rowToSave[key];
              }
            });
            
            const response = await apiClient.post(
              `/table-management/tables/${config.saveConfig.tableName}/add`,
              rowToSave
            );
            
            if (!response.data?.success) {
              throw new Error(response.data?.message || "품목 저장 실패");
            }
          }
          
          // 이미 저장했으므로 아래 로직에서 다시 저장하지 않도록 제거
          delete tableSectionData[tableSection.id];
        }
      }
      
      // 품목이 없으면 공통 데이터만 저장하지 않음 (품목이 필요한 화면이므로)
      // 다른 테이블 섹션이 있는 경우에만 메인 데이터 저장
      const hasOtherTableSections = Object.keys(tableSectionData).length > 0;
      if (!hasOtherTableSections) {
        return; // 메인 테이블에 저장할 품목이 없으면 종료
      }
    }

    // 메인 데이터 저장 (테이블 섹션이 없거나 별도 테이블에 저장하는 경우)
    const response = await apiClient.post(`/table-management/tables/${config.saveConfig.tableName}/add`, dataToSave);

    if (!response.data?.success) {
      throw new Error(response.data?.message || "저장 실패");
    }
    
    // 테이블 섹션 데이터 저장 (별도 테이블에)
    for (const section of config.sections) {
      if (section.type === "table" && section.tableConfig?.saveConfig?.targetTable) {
        const sectionData = tableSectionData[section.id];
        if (sectionData && sectionData.length > 0) {
          // 메인 레코드 ID가 필요한 경우 (response.data에서 가져오기)
          const mainRecordId = response.data?.data?.id;
          
          // 공통 저장 필드 수집: 다른 섹션(필드 타입)에서 공통 저장으로 설정된 필드 값
          // 기본값: 필드 타입 섹션은 'common', 테이블 타입 섹션은 'individual'
          const commonFieldsData: Record<string, any> = {};
          const { sectionSaveModes } = config.saveConfig;
          
          // 다른 섹션에서 공통 저장으로 설정된 필드 값 수집
          for (const otherSection of config.sections) {
            if (otherSection.id === section.id) continue; // 현재 테이블 섹션은 건너뛰기
            
            const sectionMode = sectionSaveModes?.find((s) => s.sectionId === otherSection.id);
            // 기본값: 필드 타입 섹션은 'common', 테이블 타입 섹션은 'individual'
            const defaultMode = otherSection.type === "table" ? "individual" : "common";
            const sectionSaveMode = sectionMode?.saveMode || defaultMode;
            
            // 필드 타입 섹션의 필드들 처리
            if (otherSection.type !== "table" && otherSection.fields) {
              for (const field of otherSection.fields) {
                // 필드별 오버라이드 확인
                const fieldOverride = sectionMode?.fieldOverrides?.find((f) => f.fieldName === field.columnName);
                const fieldSaveMode = fieldOverride?.saveMode || sectionSaveMode;
                
                // 공통 저장이면 formData에서 값을 가져와 모든 품목에 적용
                if (fieldSaveMode === "common" && formData[field.columnName] !== undefined) {
                  commonFieldsData[field.columnName] = formData[field.columnName];
                }
              }
            }
            
            // 🆕 선택적 필드 그룹 (optionalFieldGroups)도 처리
            if (otherSection.optionalFieldGroups && otherSection.optionalFieldGroups.length > 0) {
              for (const optGroup of otherSection.optionalFieldGroups) {
                if (optGroup.fields) {
                  for (const field of optGroup.fields) {
                    // 선택적 필드 그룹은 기본적으로 common 저장
                    if (formData[field.columnName] !== undefined) {
                      commonFieldsData[field.columnName] = formData[field.columnName];
                    }
                  }
                }
              }
            }
          }
          
          console.log("[saveSingleRow] 별도 테이블 저장 - 공통 필드:", Object.keys(commonFieldsData));
          
          for (const item of sectionData) {
            // 공통 필드 병합 + 개별 품목 데이터
            const itemToSave = { ...commonFieldsData, ...item };
            
            // saveToTarget: false인 컬럼은 저장에서 제외
            const columns = section.tableConfig?.columns || [];
            for (const col of columns) {
              if (col.saveConfig?.saveToTarget === false && col.field in itemToSave) {
                delete itemToSave[col.field];
              }
            }
            
            // _sourceData 등 내부 메타데이터 제거
            Object.keys(itemToSave).forEach((key) => {
              if (key.startsWith("_")) {
                delete itemToSave[key];
              }
            });
            
            // 메인 레코드와 연결이 필요한 경우
            if (mainRecordId && config.saveConfig.primaryKeyColumn) {
              itemToSave[config.saveConfig.primaryKeyColumn] = mainRecordId;
            }
            
            const saveResponse = await apiClient.post(
              `/table-management/tables/${section.tableConfig.saveConfig.targetTable}/add`,
              itemToSave
            );
            
            if (!saveResponse.data?.success) {
              throw new Error(saveResponse.data?.message || `${section.title || "테이블 섹션"} 저장 실패`);
            }
          }
        }
      }
    }
  }, [config.sections, config.saveConfig.tableName, config.saveConfig.primaryKeyColumn, config.saveConfig.sectionSaveModes, formData]);

  // 다중 행 저장 (겸직 등)
  const saveMultipleRows = useCallback(async () => {
    const { multiRowSave } = config.saveConfig;
    if (!multiRowSave) return;

    let { commonFields = [], repeatSectionId = "" } = multiRowSave;
    const { typeColumn, mainTypeValue, subTypeValue, mainSectionFields = [] } = multiRowSave;

    // 공통 필드가 설정되지 않은 경우, 기본정보 섹션의 모든 필드를 공통 필드로 사용
    if (commonFields.length === 0) {
      const nonRepeatableSections = config.sections.filter((s) => !s.repeatable);
      commonFields = nonRepeatableSections.flatMap((s) => (s.fields || []).map((f) => f.columnName));
    }

    // 반복 섹션 ID가 설정되지 않은 경우, 첫 번째 반복 섹션 사용
    if (!repeatSectionId) {
      const repeatableSection = config.sections.find((s) => s.repeatable);
      if (repeatableSection) {
        repeatSectionId = repeatableSection.id;
      }
    }

    // 반복 섹션 데이터
    const repeatItems = repeatSections[repeatSectionId] || [];

    // 저장할 행들 생성
    const rowsToSave: any[] = [];

    // 공통 데이터 (모든 행에 적용)
    const commonData: any = {};
    commonFields.forEach((fieldName) => {
      if (formData[fieldName] !== undefined) {
        commonData[fieldName] = formData[fieldName];
      }
    });

    // 메인 섹션 필드 데이터 (메인 행에만 적용되는 부서/직급 등)
    const mainSectionData: any = {};
    mainSectionFields.forEach((fieldName) => {
      if (formData[fieldName] !== undefined) {
        mainSectionData[fieldName] = formData[fieldName];
      }
    });

    // 메인 행 (공통 데이터 + 메인 섹션 필드)
    const mainRow: any = { ...commonData, ...mainSectionData };
    if (typeColumn) {
      mainRow[typeColumn] = mainTypeValue || "main";
    }
    rowsToSave.push(mainRow);

    // 반복 섹션 행들 (공통 데이터 + 반복 섹션 필드)
    for (const item of repeatItems) {
      const subRow: any = { ...commonData };

      // 반복 섹션의 필드 값 추가
      const repeatSection = config.sections.find((s) => s.id === repeatSectionId);
      (repeatSection?.fields || []).forEach((field) => {
        if (item[field.columnName] !== undefined) {
          subRow[field.columnName] = item[field.columnName];
        }
      });

      if (typeColumn) {
        subRow[typeColumn] = subTypeValue || "concurrent";
      }

      rowsToSave.push(subRow);
    }

    // 저장 시점 채번규칙 처리 (메인 행만)
    for (const section of config.sections) {
      if (section.repeatable || section.type === "table") continue;

      for (const field of (section.fields || [])) {
        if (field.numberingRule?.enabled && field.numberingRule?.ruleId) {
          // generateOnSave 또는 generateOnOpen 모두 저장 시 실제 순번 할당
          const shouldAllocate = field.numberingRule.generateOnSave || field.numberingRule.generateOnOpen;
          if (shouldAllocate) {
            const response = await allocateNumberingCode(field.numberingRule.ruleId);
            if (response.success && response.data?.generatedCode) {
              // 모든 행에 동일한 채번 값 적용 (공통 필드인 경우)
              if (commonFields.includes(field.columnName)) {
                rowsToSave.forEach((row) => {
                  row[field.columnName] = response.data?.generatedCode;
                });
              } else {
                rowsToSave[0][field.columnName] = response.data?.generatedCode;
              }
            }
          }
        }
      }
    }

    // 모든 행 저장
    for (let i = 0; i < rowsToSave.length; i++) {
      const row = rowsToSave[i];

      // 빈 객체 체크
      if (Object.keys(row).length === 0) {
        continue;
      }

      const response = await apiClient.post(`/table-management/tables/${config.saveConfig.tableName}/add`, row);

      if (!response.data?.success) {
        throw new Error(response.data?.message || `${i + 1}번째 행 저장 실패`);
      }
    }
  }, [config.sections, config.saveConfig, formData, repeatSections]);

  // 다중 테이블 저장 (범용)
  const saveWithMultiTable = useCallback(async () => {
    const { customApiSave } = config.saveConfig;
    if (!customApiSave?.multiTable) return;

    const { multiTable } = customApiSave;

    // 1. 메인 테이블 데이터 구성
    const mainData: Record<string, any> = {};
    config.sections.forEach((section) => {
      if (section.repeatable || section.type === "table") return; // 반복 섹션 및 테이블 타입 제외
      (section.fields || []).forEach((field) => {
        const value = formData[field.columnName];
        if (value !== undefined && value !== null && value !== "") {
          mainData[field.columnName] = value;
        }
      });
    });
    
    // 1-0. receiveFromParent 필드 값도 mainData에 추가 (서브 테이블 저장용)
    // 이 필드들은 메인 테이블에는 저장되지 않지만, 서브 테이블 저장 시 필요할 수 있음
    config.sections.forEach((section) => {
      if (section.repeatable || section.type === "table") return;
      (section.fields || []).forEach((field) => {
        if (field.receiveFromParent && !mainData[field.columnName]) {
          const value = formData[field.columnName];
          if (value !== undefined && value !== null && value !== "") {
            mainData[field.columnName] = value;
          }
        }
      });
    });

    // 1-1. 채번규칙 처리 (저장 시점에 실제 순번 할당)
    for (const section of config.sections) {
      if (section.repeatable || section.type === "table") continue;

      for (const field of (section.fields || [])) {
        // 채번규칙이 활성화된 필드 처리
        if (field.numberingRule?.enabled && field.numberingRule?.ruleId) {
          // 신규 생성이거나 값이 없는 경우에만 채번
          const isNewRecord = !initialData?.[multiTable.mainTable.primaryKeyColumn];
          const hasNoValue = !mainData[field.columnName];

          if (isNewRecord || hasNoValue) {
            try {
              // allocateNumberingCode로 실제 순번 증가
              const response = await allocateNumberingCode(field.numberingRule.ruleId);
              if (response.success && response.data?.generatedCode) {
                mainData[field.columnName] = response.data.generatedCode;
              }
            } catch (error) {
              console.error(`채번규칙 할당 실패 (${field.columnName}):`, error);
            }
          }
        }
      }
    }

    // 2. 서브 테이블 데이터 구성
    const subTablesData: Array<{
      tableName: string;
      linkColumn: { mainField: string; subColumn: string };
      items: Record<string, any>[];
      options?: {
        saveMainAsFirst?: boolean;
        mainFieldMappings?: Array<{ formField: string; targetColumn: string }>;
        mainMarkerColumn?: string;
        mainMarkerValue?: any;
        subMarkerValue?: any;
        deleteExistingBefore?: boolean;
      };
    }> = [];

    for (const subTableConfig of multiTable.subTables || []) {
      // 서브 테이블이 활성화되어 있고 테이블명이 있어야 함
      // repeatSectionId는 선택사항 (saveMainAsFirst만 사용하는 경우 없을 수 있음)
      if (!subTableConfig.enabled || !subTableConfig.tableName) {
        continue;
      }

      const subItems: Record<string, any>[] = [];
      
      // 반복 섹션이 있는 경우에만 반복 데이터 처리
      if (subTableConfig.repeatSectionId) {
        const repeatData = repeatSections[subTableConfig.repeatSectionId] || [];

        // 반복 섹션 데이터를 필드 매핑에 따라 변환
        for (const item of repeatData) {
          const mappedItem: Record<string, any> = {};

          // 연결 컬럼 값 설정
          if (subTableConfig.linkColumn?.mainField && subTableConfig.linkColumn?.subColumn) {
            mappedItem[subTableConfig.linkColumn.subColumn] = mainData[subTableConfig.linkColumn.mainField];
          }

          // 필드 매핑에 따라 데이터 변환
          for (const mapping of subTableConfig.fieldMappings || []) {
            if (mapping.formField && mapping.targetColumn) {
              mappedItem[mapping.targetColumn] = item[mapping.formField];
            }
          }

          // 메인/서브 구분 컬럼 설정 (서브 데이터는 서브 마커 값)
          if (subTableConfig.options?.mainMarkerColumn) {
            mappedItem[subTableConfig.options.mainMarkerColumn] = subTableConfig.options?.subMarkerValue ?? false;
          }

          if (Object.keys(mappedItem).length > 0) {
            subItems.push(mappedItem);
          }
        }
      }

      // saveMainAsFirst가 활성화된 경우, 메인 데이터를 서브 테이블에 저장하기 위한 매핑 생성
      let mainFieldMappings: Array<{ formField: string; targetColumn: string }> | undefined;
      if (subTableConfig.options?.saveMainAsFirst) {
        mainFieldMappings = [];

        // fieldMappings에 정의된 targetColumn만 매핑 (서브 테이블에 존재하는 컬럼만)
        for (const mapping of subTableConfig.fieldMappings || []) {
          if (mapping.targetColumn) {
            // formData에서 동일한 컬럼명이 있으면 매핑 (receiveFromParent 필드 포함)
            const formValue = formData[mapping.targetColumn];
            if (formValue !== undefined && formValue !== null && formValue !== "") {
              mainFieldMappings.push({
                formField: mapping.targetColumn,
                targetColumn: mapping.targetColumn,
              });
            }
            // 또는 메인 섹션의 필드 중 같은 이름이 있으면 매핑
            else {
              config.sections.forEach((section) => {
                if (section.repeatable || section.type === "table") return;
                const matchingField = (section.fields || []).find((f) => f.columnName === mapping.targetColumn);
                if (matchingField) {
                  const fieldValue = formData[matchingField.columnName];
                  if (fieldValue !== undefined && fieldValue !== null && fieldValue !== "") {
                    mainFieldMappings!.push({
                      formField: matchingField.columnName,
                      targetColumn: mapping.targetColumn,
                    });
                  }
                }
              });
            }
          }
        }

        // 중복 제거
        mainFieldMappings = mainFieldMappings.filter(
          (m, idx, arr) => arr.findIndex((x) => x.targetColumn === m.targetColumn) === idx,
        );
      }

      // 서브 테이블 데이터 추가 (반복 데이터가 없어도 saveMainAsFirst가 있으면 추가)
      if (subItems.length > 0 || subTableConfig.options?.saveMainAsFirst) {
        subTablesData.push({
          tableName: subTableConfig.tableName,
          linkColumn: subTableConfig.linkColumn,
          items: subItems,
          options: {
            ...subTableConfig.options,
            mainFieldMappings, // 메인 데이터 매핑 추가
          },
        });
      }
    }

    // 3. 범용 다중 테이블 저장 API 호출
    const response = await apiClient.post("/table-management/multi-table-save", {
      mainTable: multiTable.mainTable,
      mainData,
      subTables: subTablesData,
      isUpdate: !!initialData?.[multiTable.mainTable.primaryKeyColumn],
    });

    if (!response.data?.success) {
      throw new Error(response.data?.message || "다중 테이블 저장 실패");
    }
  }, [config.sections, config.saveConfig, formData, repeatSections, initialData]);

  // 커스텀 API 저장
  const saveWithCustomApi = useCallback(async () => {
    const { customApiSave } = config.saveConfig;
    if (!customApiSave) return;

    const saveWithGenericCustomApi = async () => {
      if (!customApiSave.customEndpoint) {
        throw new Error("커스텀 API 엔드포인트가 설정되지 않았습니다.");
      }

      const dataToSave = { ...formData };

      // 메타데이터 필드 제거
      Object.keys(dataToSave).forEach((key) => {
        if (key.startsWith("_")) {
          delete dataToSave[key];
        }
      });

      // 반복 섹션 데이터 포함
      if (Object.keys(repeatSections).length > 0) {
        dataToSave._repeatSections = repeatSections;
      }

      const method = customApiSave.customMethod || "POST";
      const response =
        method === "PUT"
          ? await apiClient.put(customApiSave.customEndpoint, dataToSave)
          : await apiClient.post(customApiSave.customEndpoint, dataToSave);

      if (!response.data?.success) {
        throw new Error(response.data?.message || "저장 실패");
      }
    };

    switch (customApiSave.apiType) {
      case "multi-table":
        await saveWithMultiTable();
        break;
      case "custom":
        await saveWithGenericCustomApi();
        break;
      default:
        throw new Error(`지원하지 않는 API 타입: ${customApiSave.apiType}`);
    }
  }, [config.saveConfig, formData, repeatSections, saveWithMultiTable]);

  // 저장 처리
  const handleSave = useCallback(async () => {
    // 커스텀 API 저장 모드가 아닌 경우에만 테이블명 체크
    if (!config.saveConfig.customApiSave?.enabled && !config.saveConfig.tableName) {
      toast.error("저장할 테이블이 설정되지 않았습니다.");
      return;
    }

    // 필수 필드 검증
    const { valid, missingFields } = validateRequiredFields();
    if (!valid) {
      toast.error(`필수 항목을 입력해주세요: ${missingFields.join(", ")}`);
      return;
    }

    setSaving(true);

    try {
      const { multiRowSave, customApiSave } = config.saveConfig;

      // 커스텀 API 저장 모드
      if (customApiSave?.enabled) {
        await saveWithCustomApi();
      } else if (multiRowSave?.enabled) {
        // 다중 행 저장
        await saveMultipleRows();
      } else {
        // 단일 행 저장
        await saveSingleRow();
      }

      // 저장 후 동작
      if (config.saveConfig.afterSave?.showToast) {
        toast.success("저장되었습니다.");
      }

      if (config.saveConfig.afterSave?.refreshParent) {
        window.dispatchEvent(new CustomEvent("refreshParentData"));
      }

      // onSave 콜백은 저장 완료 알림용으로만 사용
      // 실제 저장은 이미 위에서 완료됨 (saveSingleRow 또는 saveMultipleRows)
      // EditModal 등 부모 컴포넌트의 저장 로직이 다시 실행되지 않도록
      // _saveCompleted 플래그를 포함하여 전달
      if (onSave) {
        onSave({ ...formData, _saveCompleted: true });
      }

      // 저장 완료 후 모달 닫기 이벤트 발생
      if (config.saveConfig.afterSave?.closeModal !== false) {
        window.dispatchEvent(new CustomEvent("closeEditModal"));
      }
    } catch (error: any) {
      console.error("저장 실패:", error);
      // axios 에러의 경우 서버 응답 메시지 추출
      const errorMessage =
        error.response?.data?.message ||
        error.response?.data?.error?.details ||
        error.message ||
        "저장에 실패했습니다.";
      toast.error(errorMessage);
    } finally {
      setSaving(false);
    }
  }, [
    config,
    formData,
    repeatSections,
    onSave,
    validateRequiredFields,
    saveSingleRow,
    saveMultipleRows,
    saveWithCustomApi,
  ]);

  // 폼 초기화
  const handleReset = useCallback(() => {
    initializeForm();
    toast.info("폼이 초기화되었습니다.");
  }, [initializeForm]);

  // 필드 요소 렌더링 (입력 컴포넌트만)
  // repeatContext: 반복 섹션인 경우 { sectionId, itemId }를 전달
  const renderFieldElement = (
    field: FormFieldConfig,
    value: any,
    onChangeHandler: (value: any) => void,
    fieldKey: string,
    isDisabled: boolean,
    repeatContext?: { sectionId: string; itemId: string },
  ) => {
    return (() => {
      switch (field.fieldType) {
        case "textarea":
          return (
            <Textarea
              id={fieldKey}
              value={value || ""}
              onChange={(e) => onChangeHandler(e.target.value)}
              placeholder={field.placeholder}
              disabled={isDisabled}
              readOnly={field.readOnly}
              className="min-h-[80px]"
            />
          );

        case "checkbox":
          return (
            <div className="flex items-center space-x-2 pt-2">
              <Checkbox
                id={fieldKey}
                checked={!!value}
                onCheckedChange={(checked) => onChangeHandler(checked)}
                disabled={isDisabled}
              />
              <Label htmlFor={fieldKey} className="text-sm font-normal">
                {field.placeholder || field.label}
              </Label>
            </div>
          );

        case "select": {
          // 🆕 연쇄 드롭다운 처리
          if (field.cascading?.enabled) {
            const cascadingConfig = field.cascading;
            const parentValue = formData[cascadingConfig.parentField];

            return (
              <CascadingSelectField
                fieldId={fieldKey}
                config={cascadingConfig as CascadingDropdownConfig}
                parentValue={parentValue}
                value={value}
                onChange={onChangeHandler}
                placeholder={field.placeholder || "선택하세요"}
                disabled={isDisabled}
              />
            );
          }

          // 다중 컬럼 저장이 활성화된 경우
          const lfgMappings = field.linkedFieldGroup?.mappings;
          if (
            field.linkedFieldGroup?.enabled &&
            field.linkedFieldGroup?.sourceTable &&
            lfgMappings &&
            lfgMappings.length > 0
          ) {
            const lfg = field.linkedFieldGroup;
            const sourceTableName = lfg.sourceTable as string;
            const cachedData = linkedFieldDataCache[sourceTableName];
            const sourceData = Array.isArray(cachedData) ? cachedData : [];

            // 첫 번째 매핑의 sourceColumn을 드롭다운 값으로 사용
            const valueColumn = lfgMappings[0].sourceColumn || "";

            // 데이터 로드 (아직 없으면)
            if (!cachedData && sourceTableName) {
              loadLinkedFieldData(sourceTableName);
            }

            // 표시 텍스트 생성 함수
            const getDisplayText = (row: Record<string, unknown>): string => {
              // 메인 표시 컬럼 (displayColumn)
              const mainDisplayVal = row[lfg.displayColumn || ""] || "";
              // 서브 표시 컬럼 (subDisplayColumn이 있으면 사용, 없으면 valueColumn 사용)
              const subDisplayVal = lfg.subDisplayColumn 
                ? (row[lfg.subDisplayColumn] || "")
                : (row[valueColumn] || "");
              
              switch (lfg.displayFormat) {
                case "code_name":
                  // 서브 - 메인 형식
                  return `${subDisplayVal} - ${mainDisplayVal}`;
                case "name_code":
                  // 메인 (서브) 형식
                  return `${mainDisplayVal} (${subDisplayVal})`;
                case "custom":
                  // 커스텀 형식: {컬럼명}을 실제 값으로 치환
                  if (lfg.customDisplayFormat) {
                    let result = lfg.customDisplayFormat;
                    // {컬럼명} 패턴을 찾아서 실제 값으로 치환
                    const matches = result.match(/\{([^}]+)\}/g);
                    if (matches) {
                      matches.forEach((match) => {
                        const columnName = match.slice(1, -1); // { } 제거
                        const columnValue = row[columnName];
                        result = result.replace(match, columnValue !== undefined && columnValue !== null ? String(columnValue) : "");
                      });
                    }
                    return result;
                  }
                  return String(mainDisplayVal);
                case "name_only":
                default:
                  return String(mainDisplayVal);
              }
            };

            return (
              <Select
                value={value || ""}
                onValueChange={(selectedValue) => {
                  // 선택된 값에 해당하는 행 찾기
                  const selectedRow = sourceData.find((row) => String(row[valueColumn]) === selectedValue);

                  // 기본 필드 값 변경 (첫 번째 매핑의 값)
                  onChangeHandler(selectedValue);

                  // 매핑된 컬럼들도 함께 저장
                  if (selectedRow && lfg.mappings) {
                    lfg.mappings.forEach((mapping) => {
                      if (mapping.sourceColumn && mapping.targetColumn) {
                        const mappedValue = selectedRow[mapping.sourceColumn];

                        // 반복 섹션인 경우 repeatSections에 저장, 아니면 formData에 저장
                        if (repeatContext) {
                          setRepeatSections((prev) => {
                            const items = prev[repeatContext.sectionId] || [];
                            const newItems = items.map((item) =>
                              item._id === repeatContext.itemId
                                ? { ...item, [mapping.targetColumn]: mappedValue }
                                : item,
                            );
                            return { ...prev, [repeatContext.sectionId]: newItems };
                          });
                        } else {
                          setFormData((prev) => ({
                            ...prev,
                            [mapping.targetColumn]: mappedValue,
                          }));
                        }
                      }
                    });
                  }
                }}
                disabled={isDisabled}
              >
                <SelectTrigger id={fieldKey} className="w-full" size="default">
                  <SelectValue placeholder={field.placeholder || "선택하세요"} />
                </SelectTrigger>
                <SelectContent>
                  {sourceData.length > 0 ? (
                    sourceData
                      .filter((row) => row[valueColumn] !== null && row[valueColumn] !== undefined && String(row[valueColumn]) !== "")
                      .map((row, index) => (
                        <SelectItem key={`${row[valueColumn]}_${index}`} value={String(row[valueColumn])}>
                          {getDisplayText(row)}
                        </SelectItem>
                      ))
                  ) : (
                    <SelectItem value="_empty" disabled>
                      {cachedData === undefined ? "데이터를 불러오는 중..." : "데이터가 없습니다"}
                    </SelectItem>
                  )}
                </SelectContent>
              </Select>
            );
          }

          // 일반 select 필드
          return (
            <SelectField
              fieldId={fieldKey}
              value={value}
              onChange={onChangeHandler}
              optionConfig={field.selectOptions}
              placeholder={field.placeholder || "선택하세요"}
              disabled={isDisabled}
              loadOptions={loadSelectOptions}
            />
          );
        }

        case "date":
          return (
            <Input
              id={fieldKey}
              type="date"
              value={value || ""}
              onChange={(e) => onChangeHandler(e.target.value)}
              disabled={isDisabled}
              readOnly={field.readOnly}
            />
          );

        case "datetime":
          return (
            <Input
              id={fieldKey}
              type="datetime-local"
              value={value || ""}
              onChange={(e) => onChangeHandler(e.target.value)}
              disabled={isDisabled}
              readOnly={field.readOnly}
            />
          );

        case "number":
          return (
            <Input
              id={fieldKey}
              type="number"
              value={value ?? ""}
              onChange={(e) => onChangeHandler(e.target.value ? Number(e.target.value) : "")}
              placeholder={field.placeholder}
              disabled={isDisabled}
              readOnly={field.readOnly}
            />
          );

        case "password":
          return (
            <Input
              id={fieldKey}
              type="password"
              value={value || ""}
              onChange={(e) => onChangeHandler(e.target.value)}
              placeholder={field.placeholder}
              disabled={isDisabled}
              readOnly={field.readOnly}
            />
          );

        case "email":
          return (
            <Input
              id={fieldKey}
              type="email"
              value={value || ""}
              onChange={(e) => onChangeHandler(e.target.value)}
              placeholder={field.placeholder}
              disabled={isDisabled}
              readOnly={field.readOnly}
            />
          );

        case "tel":
          return (
            <Input
              id={fieldKey}
              type="tel"
              value={value || ""}
              onChange={(e) => onChangeHandler(e.target.value)}
              placeholder={field.placeholder}
              disabled={isDisabled}
              readOnly={field.readOnly}
            />
          );

        default:
          return (
            <Input
              id={fieldKey}
              type="text"
              value={value || ""}
              onChange={(e) => onChangeHandler(e.target.value)}
              placeholder={field.placeholder}
              disabled={isDisabled}
              readOnly={field.readOnly}
            />
          );
      }
    })();
  };

  // 섹션의 열 수에 따른 기본 gridSpan 계산
  const getDefaultGridSpan = (sectionColumns: number = 2): number => {
    // 12칸 그리드 기준: 1열=12, 2열=6, 3열=4, 4열=3
    return Math.floor(12 / sectionColumns);
  };

  // 필드 렌더링 (섹션 열 수 적용)
  // repeatContext: 반복 섹션인 경우 { sectionId, itemId }를 전달
  const renderFieldWithColumns = (
    field: FormFieldConfig,
    value: any,
    onChangeHandler: (value: any) => void,
    fieldKey: string,
    sectionColumns: number = 2,
    repeatContext?: { sectionId: string; itemId: string },
  ) => {
    // 섹션 열 수에 따른 기본 gridSpan 계산 (섹션 열 수가 우선)
    const defaultSpan = getDefaultGridSpan(sectionColumns);
    // 섹션이 1열이면 무조건 12(전체 너비), 그 외에는 필드 설정 또는 기본값 사용
    const actualGridSpan = sectionColumns === 1 ? 12 : field.gridSpan || defaultSpan;

    const isDisabled = !!(field.disabled || (field.numberingRule?.enabled && !field.numberingRule?.editable));
    const isHidden = field.hidden || field.numberingRule?.hidden;

    if (isHidden) {
      return null;
    }

    const fieldElement = renderFieldElement(field, value, onChangeHandler, fieldKey, isDisabled, repeatContext);

    if (field.fieldType === "checkbox") {
      return (
        <div key={fieldKey} className="space-y-1" style={{ gridColumn: `span ${actualGridSpan}` }}>
          {fieldElement}
        </div>
      );
    }

    return (
      <div key={fieldKey} className="space-y-1" style={{ gridColumn: `span ${actualGridSpan}` }}>
        <Label htmlFor={fieldKey} className="text-sm font-medium">
          {field.label}
          {field.required && <span className="text-destructive ml-1">*</span>}
          {field.numberingRule?.enabled && <span className="text-muted-foreground ml-1 text-xs">(자동생성)</span>}
        </Label>
        {fieldElement}
      </div>
    );
  };

  // 섹션 렌더링
  const renderSection = (section: FormSectionConfig) => {
    const isCollapsed = collapsedSections.has(section.id);
    const sectionColumns = section.columns || 2;

    // 반복 섹션
    if (section.repeatable) {
      return renderRepeatableSection(section, isCollapsed);
    }

    // 테이블 타입 섹션
    if (section.type === "table" && section.tableConfig) {
      return (
        <Card key={section.id} className="mb-4">
          <CardHeader className="pb-3">
            <CardTitle className="text-base">{section.title}</CardTitle>
            {section.description && <CardDescription className="text-xs">{section.description}</CardDescription>}
          </CardHeader>
          <CardContent>
            <TableSectionRenderer
              sectionId={section.id}
              tableConfig={section.tableConfig}
              formData={formData}
              onFormDataChange={handleFieldChange}
              onTableDataChange={(data) => {
                // 테이블 섹션 데이터를 formData에 저장
                handleFieldChange(`_tableSection_${section.id}`, data);
              }}
            />
          </CardContent>
        </Card>
      );
    }

    // 기본 필드 타입 섹션
    return (
      <Card key={section.id} className="mb-4">
        {section.collapsible ? (
          <Collapsible open={!isCollapsed} onOpenChange={() => toggleSectionCollapse(section.id)}>
            <CollapsibleTrigger asChild>
              <CardHeader className="hover:bg-muted/50 cursor-pointer transition-colors">
                <div className="flex items-center justify-between">
                  <div>
                    <CardTitle className="text-base">{section.title}</CardTitle>
                    {section.description && (
                      <CardDescription className="mt-1 text-xs">{section.description}</CardDescription>
                    )}
                  </div>
                  {isCollapsed ? <ChevronDown className="h-4 w-4" /> : <ChevronUp className="h-4 w-4" />}
                </div>
              </CardHeader>
            </CollapsibleTrigger>
            <CollapsibleContent>
              <CardContent>
                <div className="grid gap-4" style={{ gridTemplateColumns: `repeat(12, 1fr)` }}>
                  {/* 일반 필드 렌더링 */}
                  {(section.fields || []).map((field) =>
                    renderFieldWithColumns(
                      field,
                      formData[field.columnName],
                      (value) => handleFieldChange(field.columnName, value),
                      `${section.id}-${field.id}`,
                      sectionColumns,
                    ),
                  )}
                </div>
              </CardContent>
            </CollapsibleContent>
          </Collapsible>
        ) : (
          <>
            <CardHeader className="pb-3">
              <CardTitle className="text-base">{section.title}</CardTitle>
              {section.description && <CardDescription className="text-xs">{section.description}</CardDescription>}
            </CardHeader>
            <CardContent>
              <div className="grid gap-4" style={{ gridTemplateColumns: `repeat(12, 1fr)` }}>
                {/* 일반 필드 렌더링 */}
                {(section.fields || []).map((field) =>
                  renderFieldWithColumns(
                    field,
                    formData[field.columnName],
                    (value) => handleFieldChange(field.columnName, value),
                    `${section.id}-${field.id}`,
                    sectionColumns,
                  ),
                )}
              </div>
              
              {/* 옵셔널 필드 그룹 렌더링 */}
              {section.optionalFieldGroups && section.optionalFieldGroups.length > 0 && (
                <div className="mt-4 space-y-3">
                  {section.optionalFieldGroups.map((group) => 
                    renderOptionalFieldGroup(section, group, sectionColumns)
                  )}
                </div>
              )}
            </CardContent>
          </>
        )}
      </Card>
    );
  };

  // 옵셔널 필드 그룹 접힘 상태 관리
  const [collapsedOptionalGroups, setCollapsedOptionalGroups] = useState<Set<string>>(() => {
    // 초기 접힘 상태 설정
    const initialCollapsed = new Set<string>();
    config.sections.forEach((section) => {
      section.optionalFieldGroups?.forEach((group) => {
        if (group.defaultCollapsed) {
          initialCollapsed.add(`${section.id}-${group.id}`);
        }
      });
    });
    return initialCollapsed;
  });

  // 옵셔널 필드 그룹 렌더링
  const renderOptionalFieldGroup = (
    section: FormSectionConfig,
    group: OptionalFieldGroupConfig,
    sectionColumns: number
  ) => {
    const key = `${section.id}-${group.id}`;
    const isActivated = activatedOptionalFieldGroups.has(key);
    const isCollapsed = collapsedOptionalGroups.has(key);
    const groupColumns = group.columns || sectionColumns;
    const addButtonText = group.addButtonText || `+ ${group.title} 추가`;
    const removeButtonText = group.removeButtonText || "제거";

    // 비활성화 상태: 추가 버튼만 표시
    if (!isActivated) {
      return (
        <div
          key={group.id}
          className="hover:border-primary/50 hover:bg-muted/30 border-muted rounded-lg border-2 border-dashed p-3 transition-colors"
        >
          <div className="flex items-center justify-between">
            <div>
              <p className="text-muted-foreground text-sm font-medium">{group.title}</p>
              {group.description && (
                <p className="text-muted-foreground/70 mt-0.5 text-xs">{group.description}</p>
              )}
            </div>
            <Button
              variant="outline"
              size="sm"
              onClick={() => activateOptionalFieldGroup(section.id, group.id)}
              className="h-8 shrink-0 text-xs"
            >
              <Plus className="mr-1 h-3.5 w-3.5" />
              {addButtonText}
            </Button>
          </div>
        </div>
      );
    }

    // 활성화 상태: 필드 그룹 표시
    // collapsible 설정에 따라 접기/펼치기 지원
    if (group.collapsible) {
      return (
        <Collapsible
          key={group.id}
          open={!isCollapsed}
          onOpenChange={(open) => {
            setCollapsedOptionalGroups((prev) => {
              const newSet = new Set(prev);
              if (open) {
                newSet.delete(key);
              } else {
                newSet.add(key);
              }
              return newSet;
            });
          }}
          className="border-primary/30 bg-muted/10 rounded-lg border"
        >
          <div className="flex items-center justify-between p-3">
            <CollapsibleTrigger asChild>
              <button className="flex items-center gap-2 text-left hover:opacity-80">
                {isCollapsed ? (
                  <ChevronRight className="h-4 w-4" />
                ) : (
                  <ChevronDown className="h-4 w-4" />
                )}
                <div>
                  <p className="text-sm font-medium">{group.title}</p>
                  {group.description && (
                    <p className="text-muted-foreground mt-0.5 text-xs">{group.description}</p>
                  )}
                </div>
              </button>
            </CollapsibleTrigger>
            <Button
              variant="ghost"
              size="sm"
              onClick={() => {
                if (group.confirmRemove) {
                  if (confirm(`${group.title}을(를) 제거하시겠습니까?\n입력한 내용이 초기화됩니다.`)) {
                    deactivateOptionalFieldGroup(section.id, group.id);
                  }
                } else {
                  deactivateOptionalFieldGroup(section.id, group.id);
                }
              }}
              className="text-muted-foreground hover:text-destructive h-7 shrink-0 text-xs"
            >
              <Trash2 className="mr-1 h-3 w-3" />
              {removeButtonText}
            </Button>
          </div>
          <CollapsibleContent>
            <div className="grid gap-3 px-3 pb-3" style={{ gridTemplateColumns: "repeat(12, 1fr)" }}>
              {(group.fields || []).map((field) =>
                renderFieldWithColumns(
                  field,
                  formData[field.columnName],
                  (value) => handleFieldChange(field.columnName, value),
                  `${section.id}-${group.id}-${field.id}`,
                  groupColumns
                )
              )}
            </div>
          </CollapsibleContent>
        </Collapsible>
      );
    }

    // 접기 비활성화: 일반 표시
    return (
      <div key={group.id} className="border-primary/30 bg-muted/10 rounded-lg border p-3">
        <div className="mb-3 flex items-center justify-between">
          <div>
            <p className="text-sm font-medium">{group.title}</p>
            {group.description && (
              <p className="text-muted-foreground mt-0.5 text-xs">{group.description}</p>
            )}
          </div>
          <Button
            variant="ghost"
            size="sm"
            onClick={() => {
              if (group.confirmRemove) {
                if (confirm(`${group.title}을(를) 제거하시겠습니까?\n입력한 내용이 초기화됩니다.`)) {
                  deactivateOptionalFieldGroup(section.id, group.id);
                }
              } else {
                deactivateOptionalFieldGroup(section.id, group.id);
              }
            }}
            className="text-muted-foreground hover:text-destructive h-7 shrink-0 text-xs"
          >
            <Trash2 className="mr-1 h-3 w-3" />
            {removeButtonText}
          </Button>
        </div>
        <div className="grid gap-3" style={{ gridTemplateColumns: "repeat(12, 1fr)" }}>
          {(group.fields || []).map((field) =>
            renderFieldWithColumns(
              field,
              formData[field.columnName],
              (value) => handleFieldChange(field.columnName, value),
              `${section.id}-${group.id}-${field.id}`,
              groupColumns
            )
          )}
        </div>
      </div>
    );
  };

  // 반복 섹션 렌더링
  const renderRepeatableSection = (section: FormSectionConfig, isCollapsed: boolean) => {
    const items = repeatSections[section.id] || [];
    const maxItems = section.repeatConfig?.maxItems || 10;
    const canAdd = items.length < maxItems;
    const sectionColumns = section.columns || 2;

    const content = (
      <>
        {items.length === 0 ? (
          <div className="text-muted-foreground py-8 text-center">
            <p className="text-sm">등록된 항목이 없습니다.</p>
            <Button variant="outline" size="sm" className="mt-2" onClick={() => handleAddRepeatItem(section.id)}>
              <Plus className="mr-1 h-4 w-4" />
              {section.repeatConfig?.addButtonText || "+ 추가"}
            </Button>
          </div>
        ) : (
          <div className="space-y-4">
            {items.map((item, index) => (
              <div key={item._id} className="bg-muted/30 relative rounded-lg border p-4">
                <div className="mb-3 flex items-center justify-between">
                  <span className="text-muted-foreground text-sm font-medium">
                    {(section.repeatConfig?.itemTitle || "항목 {index}").replace("{index}", String(index + 1))}
                  </span>
                  <Button
                    variant="ghost"
                    size="sm"
                    className="text-destructive hover:text-destructive h-8 w-8 p-0"
                    onClick={() => {
                      if (section.repeatConfig?.confirmRemove) {
                        setDeleteDialog({ open: true, sectionId: section.id, itemId: item._id });
                      } else {
                        handleRemoveRepeatItem(section.id, item._id);
                      }
                    }}
                  >
                    <Trash2 className="h-4 w-4" />
                  </Button>
                </div>
                <div className="grid gap-4" style={{ gridTemplateColumns: `repeat(12, 1fr)` }}>
                  {/* 일반 필드 렌더링 */}
                  {(section.fields || []).map((field) =>
                    renderFieldWithColumns(
                      field,
                      item[field.columnName],
                      (value) => handleRepeatFieldChange(section.id, item._id, field.columnName, value),
                      `${section.id}-${item._id}-${field.id}`,
                      sectionColumns,
                      { sectionId: section.id, itemId: item._id }, // 반복 섹션 컨텍스트 전달
                    ),
                  )}
                </div>
              </div>
            ))}

            {canAdd && (
              <Button variant="outline" size="sm" className="w-full" onClick={() => handleAddRepeatItem(section.id)}>
                <Plus className="mr-1 h-4 w-4" />
                {section.repeatConfig?.addButtonText || "+ 추가"}
              </Button>
            )}
          </div>
        )}
      </>
    );

    return (
      <Card key={section.id} className="mb-4">
        {section.collapsible ? (
          <Collapsible open={!isCollapsed} onOpenChange={() => toggleSectionCollapse(section.id)}>
            <CollapsibleTrigger asChild>
              <CardHeader className="hover:bg-muted/50 cursor-pointer transition-colors">
                <div className="flex items-center justify-between">
                  <div>
                    <CardTitle className="text-base">
                      {section.title}
                      <span className="text-muted-foreground ml-2 text-sm font-normal">({items.length}개)</span>
                    </CardTitle>
                    {section.description && (
                      <CardDescription className="mt-1 text-xs">{section.description}</CardDescription>
                    )}
                  </div>
                  {isCollapsed ? <ChevronDown className="h-4 w-4" /> : <ChevronUp className="h-4 w-4" />}
                </div>
              </CardHeader>
            </CollapsibleTrigger>
            <CollapsibleContent>
              <CardContent>{content}</CardContent>
            </CollapsibleContent>
          </Collapsible>
        ) : (
          <>
            <CardHeader className="pb-3">
              <CardTitle className="text-base">
                {section.title}
                <span className="text-muted-foreground ml-2 text-sm font-normal">({items.length}개)</span>
              </CardTitle>
              {section.description && <CardDescription className="text-xs">{section.description}</CardDescription>}
            </CardHeader>
            <CardContent>{content}</CardContent>
          </>
        )}
      </Card>
    );
  };

  // 디자인 모드 렌더링
  if (isDesignMode) {
    return (
      <div
        className={cn(
          "min-h-[200px] rounded-lg border-2 border-dashed p-4",
          isSelected ? "border-primary bg-primary/5" : "border-muted",
          className,
        )}
        style={style}
      >
        <div className="text-muted-foreground text-center">
          <p className="font-medium">{config.modal.title || "범용 폼 모달"}</p>
          <p className="mt-1 text-xs">
            {config.sections.length}개 섹션 |{config.sections.reduce((acc, s) => acc + (s.fields?.length || 0), 0)}개 필드
          </p>
          <p className="mt-1 text-xs">저장 테이블: {config.saveConfig.tableName || "(미설정)"}</p>
        </div>
      </div>
    );
  }

  return (
    <div className={cn("w-full", className)} style={style}>
      {/* 섹션들 */}
      <div className="space-y-4">{config.sections.map((section) => renderSection(section))}</div>

      {/* 버튼 영역 - 저장 버튼이 표시될 때만 렌더링 */}
      {config.modal.showSaveButton !== false && (
        <div className="mt-6 flex justify-end gap-2 border-t pt-4">
          {config.modal.showResetButton && (
            <Button
              type="button"
              variant="outline"
              onClick={(e) => {
                e.preventDefault();
                e.stopPropagation();
                handleReset();
              }}
              disabled={saving}
            >
              <RefreshCw className="mr-1 h-4 w-4" />
              {config.modal.resetButtonText || "초기화"}
            </Button>
          )}
          <Button
            type="button"
            onClick={(e) => {
              e.preventDefault();
              e.stopPropagation();
              handleSave();
            }}
            disabled={saving || !config.saveConfig.tableName}
          >
            {saving ? "저장 중..." : config.modal.saveButtonText || "저장"}
          </Button>
        </div>
      )}

      {/* 삭제 확인 다이얼로그 */}
      <AlertDialog
        open={deleteDialog.open}
        onOpenChange={(open) => !open && setDeleteDialog({ open: false, sectionId: "", itemId: "" })}
      >
        <AlertDialogContent>
          <AlertDialogHeader>
            <AlertDialogTitle>항목 삭제</AlertDialogTitle>
            <AlertDialogDescription>이 항목을 삭제하시겠습니까? 이 작업은 되돌릴 수 없습니다.</AlertDialogDescription>
          </AlertDialogHeader>
          <AlertDialogFooter>
            <AlertDialogCancel>취소</AlertDialogCancel>
            <AlertDialogAction
              onClick={() => handleRemoveRepeatItem(deleteDialog.sectionId, deleteDialog.itemId)}
              className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
            >
              삭제
            </AlertDialogAction>
          </AlertDialogFooter>
        </AlertDialogContent>
      </AlertDialog>
    </div>
  );
}

// Select 필드 컴포넌트 (옵션 로딩 포함)
interface SelectFieldProps {
  fieldId: string;
  value: any;
  onChange: (value: any) => void;
  optionConfig?: SelectOptionConfig;
  placeholder?: string;
  disabled?: boolean;
  loadOptions: (fieldId: string, config: SelectOptionConfig) => Promise<{ value: string; label: string }[]>;
}

function SelectField({ fieldId, value, onChange, optionConfig, placeholder, disabled, loadOptions }: SelectFieldProps) {
  const [options, setOptions] = useState<{ value: string; label: string }[]>([]);
  const [loading, setLoading] = useState(false);

  useEffect(() => {
    if (optionConfig) {
      setLoading(true);
      loadOptions(fieldId, optionConfig)
        .then(setOptions)
        .finally(() => setLoading(false));
    }
  }, [fieldId, optionConfig, loadOptions]);

  return (
    <Select value={value || ""} onValueChange={onChange} disabled={disabled || loading}>
      <SelectTrigger size="default">
        <SelectValue placeholder={loading ? "로딩 중..." : placeholder} />
      </SelectTrigger>
      <SelectContent>
        {options
          .filter((option) => option.value && option.value !== "")
          .map((option) => (
            <SelectItem key={option.value} value={option.value}>
              {option.label}
            </SelectItem>
          ))}
      </SelectContent>
    </Select>
  );
}

export default UniversalFormModalComponent;
