"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, Loader2, Check, ChevronsUpDown } from "lucide-react";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "@/components/ui/command";
import { toast } from "sonner";
import { cn } from "@/lib/utils";
import { apiClient } from "@/lib/api/client";
import { 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 필드 컴포넌트
 * allowCustomInput이 true이면 Combobox 형태로 직접 입력 가능
 */
interface CascadingSelectFieldProps {
  fieldId: string;
  config: CascadingDropdownConfig;
  parentValue?: string | number | null;
  value?: string;
  onChange: (value: string) => void;
  placeholder?: string;
  disabled?: boolean;
  allowCustomInput?: boolean;
}

const CascadingSelectField: React.FC<CascadingSelectFieldProps> = ({
  fieldId,
  config,
  parentValue,
  value,
  onChange,
  placeholder,
  disabled,
  allowCustomInput = false,
}) => {
  const [open, setOpen] = useState(false);
  const [inputValue, setInputValue] = useState(value || "");
  const { options, loading } = useCascadingDropdown({
    config,
    parentValue,
  });

  // value가 외부에서 변경되면 inputValue도 동기화
  useEffect(() => {
    setInputValue(value || "");
  }, [value]);

  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;

  // Combobox 형태 (직접 입력 허용)
  if (allowCustomInput) {
    return (
      <Popover open={open} onOpenChange={setOpen}>
        <PopoverTrigger asChild>
          <div className="relative w-full">
            <Input
              id={fieldId}
              value={inputValue}
              onChange={(e) => {
                setInputValue(e.target.value);
                onChange(e.target.value);
              }}
              placeholder={getPlaceholder()}
              disabled={isDisabled}
              className="w-full pr-8"
            />
            <Button
              type="button"
              variant="ghost"
              size="sm"
              className="absolute right-0 top-0 h-full px-2 hover:bg-transparent"
              onClick={() => !isDisabled && setOpen(!open)}
              disabled={isDisabled}
            >
              {loading ? (
                <Loader2 className="h-4 w-4 animate-spin text-muted-foreground" />
              ) : (
                <ChevronsUpDown className="h-4 w-4 text-muted-foreground" />
              )}
            </Button>
          </div>
        </PopoverTrigger>
        <PopoverContent className="w-[--radix-popover-trigger-width] p-0" align="start">
          <Command>
            <CommandInput placeholder="검색..." className="h-9" />
            <CommandList>
              <CommandEmpty>
                {!parentValue
                  ? config.emptyParentMessage || "상위 항목을 먼저 선택하세요"
                  : config.noOptionsMessage || "선택 가능한 항목이 없습니다"}
              </CommandEmpty>
              <CommandGroup>
                {options
                  .filter((option) => option.value && option.value !== "")
                  .map((option) => (
                    <CommandItem
                      key={option.value}
                      value={option.label}
                      onSelect={() => {
                        setInputValue(option.label);
                        onChange(option.value);
                        setOpen(false);
                      }}
                    >
                      <Check
                        className={cn(
                          "mr-2 h-4 w-4",
                          value === option.value ? "opacity-100" : "opacity-0"
                        )}
                      />
                      {option.label}
                    </CommandItem>
                  ))}
              </CommandGroup>
            </CommandList>
          </Command>
        </PopoverContent>
      </Popover>
    );
  }

  // 기본 Select 형태 (목록에서만 선택)
  return (
    <Select value={value || ""} onValueChange={onChange} disabled={isDisabled}>
      <SelectTrigger id={fieldId} className="w-full" size="default">
        {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,
        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>[];
  }>({});

  // 채번규칙 원본 값 추적 (수동 모드 감지용)
  // key: columnName, value: 자동 생성된 원본 값
  const [numberingOriginalValues, setNumberingOriginalValues] = useState<Record<string, string>>({});

  // 🆕 수정 모드: 원본 그룹 데이터 (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;

      // 필수값 검증 실행
      const validation = validateRequiredFields();
      if (!validation.valid) {
        event.detail.validationFailed = true;
        event.detail.validationErrors = validation.missingFields;
        toast.error(`필수 항목을 입력해주세요: ${validation.missingFields.join(", ")}`);
        console.log("[UniversalFormModal] 필수값 검증 실패:", validation.missingFields);
        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));

      // 🆕 시스템 필드 병합: id는 설정 여부와 관계없이 항상 전달 (UPDATE/INSERT 판단용)
      // - 신규 등록: formData.id가 없으므로 영향 없음
      // - 편집 모드: formData.id가 있으면 메인 테이블 UPDATE에 사용
      if (formData.id !== undefined && formData.id !== null && formData.id !== "") {
        event.detail.formData.id = formData.id;
        console.log(`[UniversalFormModal] 시스템 필드 병합: id =`, formData.id);
      }

      // 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);
        }
      }

      // 🆕 테이블 섹션 데이터 병합 (품목 리스트 등)
      // 참고: initializeForm에서 DB 로드 시 __tableSection_ (더블), 
      //       handleTableDataChange에서 수정 시 _tableSection_ (싱글) 사용
      for (const [key, value] of Object.entries(formData)) {
        // 싱글/더블 언더스코어 모두 처리
        if ((key.startsWith("_tableSection_") || key.startsWith("__tableSection_")) && Array.isArray(value)) {
          // 저장 시에는 _tableSection_ 키로 통일 (buttonActions.ts에서 이 키를 기대)
          const normalizedKey = key.startsWith("__tableSection_") 
            ? key.replace("__tableSection_", "_tableSection_") 
            : key;
          event.detail.formData[normalizedKey] = value;
          console.log(`[UniversalFormModal] 테이블 섹션 병합: ${key} → ${normalizedKey}, ${value.length}개 항목`);
        }
        
        // 🆕 원본 테이블 섹션 데이터도 병합 (삭제 추적용)
        if (key.startsWith("_originalTableSectionData_") && Array.isArray(value)) {
          event.detail.formData[key] = value;
          console.log(`[UniversalFormModal] 원본 테이블 섹션 데이터 병합: ${key}, ${value.length}개 항목`);
        }
      }

      // 🆕 수정 모드: 원본 그룹 데이터 전달 (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) {
      return;
    }

    // 원본 데이터 저장 (수정/삭제 추적용)
    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) {
                const generatedCode = response.data.generatedCode;
                updatedData[field.columnName] = generatedCode;

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

                // 원본 채번 값 저장 (수동 모드 감지용)
                setNumberingOriginalValues((prev) => ({
                  ...prev,
                  [field.columnName]: generatedCode,
                }));

                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;
              }
            }
          }
        }
      }
    }

    // 🆕 테이블 섹션(type: "table") 디테일 데이터 로드 (마스터-디테일 구조)
    // 수정 모드일 때 디테일 테이블에서 데이터 가져오기
    if (effectiveInitialData) {
      console.log("[initializeForm] 테이블 섹션 디테일 로드 시작", {
        sectionsCount: config.sections.length,
        effectiveInitialDataKeys: Object.keys(effectiveInitialData),
      });

      for (const section of config.sections) {
        if (section.type !== "table" || !section.tableConfig) {
          continue;
        }

        const tableConfig = section.tableConfig;
        // editConfig는 타입에 정의되지 않았지만 런타임에 존재할 수 있음
        const editConfig = (tableConfig as any).editConfig;
        const saveConfig = tableConfig.saveConfig;

        console.log(`[initializeForm] 테이블 섹션 ${section.id} 검사:`, {
          hasEditConfig: !!editConfig,
          loadOnEdit: editConfig?.loadOnEdit,
          hasSaveConfig: !!saveConfig,
          targetTable: saveConfig?.targetTable,
          linkColumn: editConfig?.linkColumn,
        });

        // 수정 모드 로드 설정 확인 (기본값: true)
        if (editConfig?.loadOnEdit === false) {
          console.log(`[initializeForm] 테이블 섹션 ${section.id}: loadOnEdit=false, 스킵`);
          continue;
        }

        // 디테일 테이블과 연결 정보 확인
        const detailTable = saveConfig?.targetTable;
        let linkColumn = editConfig?.linkColumn;

        if (!detailTable) {
          console.log(`[initializeForm] 테이블 섹션 ${section.id}: saveConfig.targetTable 미설정, 스킵`);
          continue;
        }

        // linkColumn이 설정되지 않았으면, 디테일 테이블 컬럼 정보 조회하여 자동 감지
        if (!linkColumn?.masterField || !linkColumn?.detailField) {
          try {
            // 마스터 테이블명 확인 (saveConfig에서)
            // 1. customApiSave.multiTable.mainTable.tableName (다중 테이블 저장)
            // 2. saveConfig.tableName (단일 테이블 저장)
            const masterTable =
              config.saveConfig?.customApiSave?.multiTable?.mainTable?.tableName || config.saveConfig?.tableName;

            // 디테일 테이블의 컬럼 목록 조회
            const columnsResponse = await apiClient.get(`/table-management/tables/${detailTable}/columns`);

            if (columnsResponse.data?.success && columnsResponse.data?.data) {
              // API 응답 구조: { success, data: { columns: [...], total, page, ... } }
              const columnsArray = columnsResponse.data.data.columns || columnsResponse.data.data || [];
              const detailColumnsData = Array.isArray(columnsArray) ? columnsArray : [];
              const detailColumns = detailColumnsData.map((col: any) => col.column_name || col.columnName);
              const masterKeys = Object.keys(effectiveInitialData);

              console.log(`[initializeForm] 테이블 섹션 ${section.id}: 연결 필드 자동 감지`, {
                masterTable,
                detailTable,
                detailColumnsCount: detailColumnsData.length,
              });

              // 방법 1: 엔티티 관계 기반 감지 (정확)
              // 디테일 테이블에서 마스터 테이블을 참조하는 엔티티 컬럼 찾기
              if (masterTable) {
                for (const col of detailColumnsData) {
                  const colName = col.column_name || col.columnName;
                  const inputType = col.input_type || col.inputType;

                  // 엔티티 타입 컬럼 확인
                  if (inputType === "entity") {
                    // reference_table 또는 detail_settings에서 참조 테이블 확인
                    let refTable = col.reference_table || col.referenceTable;

                    // detail_settings에서 referenceTable 확인
                    if (!refTable && col.detail_settings) {
                      try {
                        const settings =
                          typeof col.detail_settings === "string"
                            ? JSON.parse(col.detail_settings)
                            : col.detail_settings;
                        refTable = settings.referenceTable;
                      } catch {
                        // JSON 파싱 실패 무시
                      }
                    }

                    // 마스터 테이블을 참조하는 컬럼 발견
                    if (refTable === masterTable) {
                      // 참조 컬럼 확인 (마스터 테이블의 어떤 컬럼을 참조하는지)
                      let refColumn = col.reference_column || col.referenceColumn;
                      if (!refColumn && col.detail_settings) {
                        try {
                          const settings =
                            typeof col.detail_settings === "string"
                              ? JSON.parse(col.detail_settings)
                              : col.detail_settings;
                          refColumn = settings.referenceColumn;
                        } catch {
                          // JSON 파싱 실패 무시
                        }
                      }

                      // 마스터 데이터에 해당 컬럼 값이 있는지 확인
                      if (refColumn && effectiveInitialData[refColumn]) {
                        console.log(
                          `[initializeForm] 테이블 섹션 ${section.id}: 엔티티 관계 감지 - ${colName} → ${masterTable}.${refColumn}`,
                        );
                        linkColumn = { masterField: refColumn, detailField: colName };
                        break;
                      }
                    }
                  }
                }
              }

              // 방법 2: 공통 컬럼 패턴 기반 감지 (폴백)
              // 엔티티 관계가 없으면 공통 컬럼명 패턴으로 찾기
              if (!linkColumn) {
                const priorityPatterns = ["_no", "_number", "_code", "_id"];

                for (const pattern of priorityPatterns) {
                  for (const masterKey of masterKeys) {
                    if (
                      masterKey.endsWith(pattern) &&
                      detailColumns.includes(masterKey) &&
                      effectiveInitialData[masterKey] &&
                      masterKey !== "id" &&
                      masterKey !== "company_code"
                    ) {
                      console.log(`[initializeForm] 테이블 섹션 ${section.id}: 공통 컬럼 패턴 감지 - ${masterKey}`);
                      linkColumn = { masterField: masterKey, detailField: masterKey };
                      break;
                    }
                  }
                  if (linkColumn) break;
                }
              }

              // 방법 3: 일반 공통 컬럼 (마지막 폴백)
              if (!linkColumn) {
                for (const masterKey of masterKeys) {
                  if (
                    detailColumns.includes(masterKey) &&
                    effectiveInitialData[masterKey] &&
                    masterKey !== "id" &&
                    masterKey !== "company_code" &&
                    !masterKey.startsWith("__")
                  ) {
                    console.log(`[initializeForm] 테이블 섹션 ${section.id}: 공통 컬럼 감지 - ${masterKey}`);
                    linkColumn = { masterField: masterKey, detailField: masterKey };
                    break;
                  }
                }
              }
            }
          } catch (error) {
            console.warn(`[initializeForm] 테이블 섹션 ${section.id}: 컬럼 정보 조회 실패`, error);
          }
        }

        if (!linkColumn?.masterField || !linkColumn?.detailField) {
          console.log(`[initializeForm] 테이블 섹션 ${section.id}: linkColumn 미설정 및 자동 감지 실패, 스킵`);
          continue;
        }

        // 마스터 테이블의 연결 필드 값 가져오기
        const masterValue = effectiveInitialData[linkColumn.masterField];
        if (!masterValue) {
          console.log(
            `[initializeForm] 테이블 섹션 ${section.id}: masterField(${linkColumn.masterField}) 값 없음, 스킵`,
          );
          continue;
        }

        try {
          console.log(`[initializeForm] 테이블 섹션 ${section.id}: 디테일 데이터 로드 시작`, {
            detailTable,
            linkColumn,
            masterValue,
          });

          // 디테일 테이블에서 데이터 조회
          // operator: "equals"를 사용하여 정확히 일치하는 값만 검색 (엔티티 타입 컬럼에서 중요)
          const searchCondition: Record<string, any> = {
            [linkColumn.detailField]: { value: masterValue, operator: "equals" },
          };

          console.log(
            `[initializeForm] 테이블 섹션 ${section.id}: API 요청 - URL: /table-management/tables/${detailTable}/data`,
          );
          console.log(
            `[initializeForm] 테이블 섹션 ${section.id}: API 요청 - search:`,
            JSON.stringify(searchCondition),
          );

          const response = await apiClient.post(`/table-management/tables/${detailTable}/data`, {
            search: searchCondition, // filters가 아닌 search로 전달
            page: 1,
            size: 1000, // pageSize가 아닌 size로 전달
            autoFilter: { enabled: true }, // 멀티테넌시 필터 적용
          });

          console.log(
            `[initializeForm] 테이블 섹션 ${section.id}: API 응답 - success: ${response.data?.success}, total: ${response.data?.data?.total}, dataLength: ${response.data?.data?.data?.length}`,
          );

          if (response.data?.success) {
            // 다양한 응답 구조 처리
            let items: any[] = [];
            const data = response.data.data;

            if (Array.isArray(data)) {
              items = data;
            } else if (data?.items && Array.isArray(data.items)) {
              items = data.items;
            } else if (data?.rows && Array.isArray(data.rows)) {
              items = data.rows;
            } else if (data?.data && Array.isArray(data.data)) {
              items = data.data;
            }

            console.log(`[initializeForm] 테이블 섹션 ${section.id}: ${items.length}건 로드됨`, items);

            // 테이블 섹션 데이터를 formData에 저장 (TableSectionRenderer에서 사용)
            const tableSectionKey = `__tableSection_${section.id}`;
            newFormData[tableSectionKey] = items;
            console.log(`[initializeForm] 테이블 섹션 ${section.id}: formData[${tableSectionKey}]에 저장됨`);

            // 🆕 테이블 섹션 원본 데이터 저장 (삭제 추적용)
            // 각 테이블 섹션별로 별도의 키에 원본 데이터 저장 (groupedDataInitializedRef와 무관하게 항상 저장)
            const originalTableSectionKey = `_originalTableSectionData_${section.id}`;
            newFormData[originalTableSectionKey] = JSON.parse(JSON.stringify(items));
            console.log(`[initializeForm] 테이블 섹션 ${section.id}: formData[${originalTableSectionKey}]에 원본 ${items.length}건 저장`);
            
            // 기존 originalGroupedData에도 추가 (하위 호환성)
            if (!groupedDataInitializedRef.current) {
              setOriginalGroupedData((prev) => {
                const newOriginal = [...prev, ...JSON.parse(JSON.stringify(items))];
                console.log(`[initializeForm] 테이블 섹션 ${section.id}: originalGroupedData에 ${items.length}건 추가 (총 ${newOriginal.length}건)`);
                return newOriginal;
              });
            }
          }
        } catch (error) {
          console.error(`[initializeForm] 테이블 섹션 ${section.id}: 디테일 데이터 로드 실패`, error);
        }
      }
    }

    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) => {
      // 채번규칙 필드의 수동 모드 감지
      const originalNumberingValue = numberingOriginalValues[columnName];
      const ruleIdKey = `${columnName}_numberingRuleId`;

      // 해당 필드의 채번규칙 설정 찾기
      let fieldConfig: FormFieldConfig | undefined;
      for (const section of config.sections) {
        if (section.type === "table" || section.repeatable) continue;
        fieldConfig = section.fields?.find((f) => f.columnName === columnName);
        if (fieldConfig) break;
        // 옵셔널 필드 그룹에서도 찾기
        for (const group of section.optionalFieldGroups || []) {
          fieldConfig = group.fields?.find((f) => f.columnName === columnName);
          if (fieldConfig) break;
        }
        if (fieldConfig) break;
      }

      // 🆕 연쇄 드롭다운: 부모 필드 변경 시 자식 필드 초기화
      const childFieldsToReset: string[] = [];
      for (const section of config.sections) {
        if (section.type === "table" || section.repeatable) continue;
        for (const field of section.fields || []) {
          // field.cascading 방식 체크
          if (field.cascading?.enabled && field.cascading?.parentField === columnName) {
            if (field.cascading.clearOnParentChange !== false) {
              childFieldsToReset.push(field.columnName);
            }
          }
          // selectOptions.cascading 방식 체크
          if (field.selectOptions?.type === "cascading" && field.selectOptions?.cascading?.parentField === columnName) {
            if (field.selectOptions.cascading.clearOnParentChange !== false) {
              childFieldsToReset.push(field.columnName);
            }
          }
        }
      }

      setFormData((prev) => {
        const newData = { ...prev, [columnName]: value };

        // 채번규칙이 활성화된 필드이고, "사용자 수정 가능"이 ON인 경우
        if (fieldConfig?.numberingRule?.enabled && fieldConfig?.numberingRule?.editable && originalNumberingValue) {
          // 사용자가 값을 수정했으면 (원본과 다르면) ruleId 제거 → 수동 모드
          if (value !== originalNumberingValue) {
            delete newData[ruleIdKey];
            console.log(`[채번 수동 모드] ${columnName}: 사용자가 값 수정 → ruleId 제거`);
          } else {
            // 원본 값으로 복구하면 ruleId 복구 → 자동 모드
            if (fieldConfig.numberingRule.ruleId) {
              newData[ruleIdKey] = fieldConfig.numberingRule.ruleId;
              console.log(`[채번 자동 모드] ${columnName}: 원본 값 복구 → ruleId 복구`);
            }
          }
        }

        // 🆕 연쇄 드롭다운 자식 필드 초기화
        for (const childField of childFieldsToReset) {
          newData[childField] = "";
          console.log(`[연쇄 드롭다운] 부모 ${columnName} 변경 → 자식 ${childField} 초기화`);
        }

        // onChange는 렌더링 외부에서 호출해야 함 (setTimeout 사용)
        if (onChange) {
          setTimeout(() => onChange(newData), 0);
        }
        return newData;
      });
    },
    [onChange, numberingOriginalValues, config.sections],
  );

  // 반복 섹션 필드 값 변경 핸들러
  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.valueCode || item.value_code,
                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 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]);

  // 필드 요소 렌더링 (입력 컴포넌트만)
  // 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": {
          // 🆕 연쇄 드롭다운 처리 (기존 field.cascading 방식)
          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}
                allowCustomInput={field.selectOptions?.allowCustomInput}
              />
            );
          }
          
          // 🆕 연쇄 드롭다운 처리 (selectOptions.type === "cascading" 방식)
          if (field.selectOptions?.type === "cascading" && field.selectOptions?.cascading?.parentField) {
            const cascadingOpts = field.selectOptions.cascading;
            const parentValue = formData[cascadingOpts.parentField];
            
            // selectOptions 기반 cascading config를 CascadingDropdownConfig 형태로 변환
            const cascadingConfig: CascadingDropdownConfig = {
              enabled: true,
              parentField: cascadingOpts.parentField,
              sourceTable: cascadingOpts.sourceTable || field.selectOptions.tableName || "",
              parentKeyColumn: cascadingOpts.parentKeyColumn || "",
              valueColumn: field.selectOptions.valueColumn || "",
              labelColumn: field.selectOptions.labelColumn || "",
              emptyParentMessage: cascadingOpts.emptyParentMessage,
              noOptionsMessage: cascadingOpts.noOptionsMessage,
              clearOnParentChange: cascadingOpts.clearOnParentChange !== false,
            };

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

          // 다중 컬럼 저장이 활성화된 경우
          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);
              }}
              groupedData={_groupedData}
            />
          </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>

      {/* 삭제 확인 다이얼로그 */}
      <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 필드 컴포넌트 (옵션 로딩 포함)
// allowCustomInput이 true이면 Combobox 형태로 직접 입력 가능
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);
  const [open, setOpen] = useState(false);
  const [inputValue, setInputValue] = useState(value || "");
  
  const allowCustomInput = optionConfig?.allowCustomInput || false;

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

  // value가 외부에서 변경되면 inputValue도 동기화
  useEffect(() => {
    // 선택된 값이 있으면 해당 라벨을 표시, 없으면 value 그대로 표시
    const selectedOption = options.find((opt) => opt.value === value);
    setInputValue(selectedOption ? selectedOption.label : value || "");
  }, [value, options]);

  // Combobox 형태 (직접 입력 허용)
  if (allowCustomInput) {
    return (
      <Popover open={open} onOpenChange={setOpen}>
        <PopoverTrigger asChild>
          <div className="relative w-full">
            <Input
              id={fieldId}
              value={inputValue}
              onChange={(e) => {
                setInputValue(e.target.value);
                onChange(e.target.value);
              }}
              placeholder={loading ? "로딩 중..." : placeholder}
              disabled={disabled || loading}
              className="w-full pr-8"
            />
            <Button
              type="button"
              variant="ghost"
              size="sm"
              className="absolute right-0 top-0 h-full px-2 hover:bg-transparent"
              onClick={() => !disabled && !loading && setOpen(!open)}
              disabled={disabled || loading}
            >
              {loading ? (
                <Loader2 className="h-4 w-4 animate-spin text-muted-foreground" />
              ) : (
                <ChevronsUpDown className="h-4 w-4 text-muted-foreground" />
              )}
            </Button>
          </div>
        </PopoverTrigger>
        <PopoverContent className="w-[--radix-popover-trigger-width] p-0" align="start">
          <Command>
            <CommandInput placeholder="검색..." className="h-9" />
            <CommandList>
              <CommandEmpty>선택 가능한 항목이 없습니다</CommandEmpty>
              <CommandGroup>
                {options
                  .filter((option) => option.value && option.value !== "")
                  .map((option) => (
                    <CommandItem
                      key={option.value}
                      value={option.label}
                      onSelect={() => {
                        setInputValue(option.label);
                        onChange(option.value);
                        setOpen(false);
                      }}
                    >
                      <Check
                        className={cn(
                          "mr-2 h-4 w-4",
                          value === option.value ? "opacity-100" : "opacity-0"
                        )}
                      />
                      {option.label}
                    </CommandItem>
                  ))}
              </CommandGroup>
            </CommandList>
          </Command>
        </PopoverContent>
      </Popover>
    );
  }

  // 기본 Select 형태 (목록에서만 선택)
  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;
