"use client";

/**
 * UnifiedRepeater 컴포넌트
 *
 * 렌더링 모드:
 * - inline: 현재 테이블 컬럼 직접 입력
 * - modal: 엔티티 선택 (FK 저장) + 추가 입력 컬럼
 *
 * RepeaterTable 및 ItemSelectionModal 재사용
 *
 * 데이터 전달 인터페이스:
 * - DataProvidable: 선택된 데이터 제공
 * - DataReceivable: 외부에서 데이터 수신
 * - repeaterDataChange 이벤트 발행
 */

import React, { useState, useEffect, useCallback, useMemo, useRef } from "react";
import { Button } from "@/components/ui/button";
import { Plus } from "lucide-react";
import { cn } from "@/lib/utils";
import {
  UnifiedRepeaterConfig,
  UnifiedRepeaterProps,
  RepeaterColumnConfig as UnifiedColumnConfig,
  DEFAULT_REPEATER_CONFIG,
} from "@/types/unified-repeater";
import { apiClient } from "@/lib/api/client";
import { allocateNumberingCode } from "@/lib/api/numberingRule";
import { v2EventBus, V2_EVENTS, V2ErrorBoundary } from "@/lib/v2-core";

// modal-repeater-table 컴포넌트 재사용
import { RepeaterTable } from "@/lib/registry/components/modal-repeater-table/RepeaterTable";
import { ItemSelectionModal } from "@/lib/registry/components/modal-repeater-table/ItemSelectionModal";
import { RepeaterColumnConfig } from "@/lib/registry/components/modal-repeater-table/types";

// 데이터 전달 인터페이스
import type { DataProvidable, DataReceivable, DataReceiverConfig } from "@/types/data-transfer";
import { useScreenContextOptional } from "@/contexts/ScreenContext";

// V2 이벤트 시스템
import { V2_EVENTS, dispatchV2Event } from "@/types/component-events";

// 전역 UnifiedRepeater 등록 (buttonActions에서 사용)
declare global {
  interface Window {
    __unifiedRepeaterInstances?: Set<string>;
  }
}

export const UnifiedRepeater: React.FC<UnifiedRepeaterProps> = ({
  config: propConfig,
  parentId,
  data: initialData,
  onDataChange,
  onRowClick,
  className,
}) => {
  // 설정 병합
  const config: UnifiedRepeaterConfig = useMemo(
    () => ({
      ...DEFAULT_REPEATER_CONFIG,
      ...propConfig,
      dataSource: { ...DEFAULT_REPEATER_CONFIG.dataSource, ...propConfig.dataSource },
      features: { ...DEFAULT_REPEATER_CONFIG.features, ...propConfig.features },
      modal: { ...DEFAULT_REPEATER_CONFIG.modal, ...propConfig.modal },
    }),
    [propConfig],
  );

  // ScreenContext (데이터 전달 인터페이스 등록용)
  const screenContext = useScreenContextOptional();

  // 상태
  const [data, setData] = useState<any[]>(initialData || []);
  const [selectedRows, setSelectedRows] = useState<Set<number>>(new Set());
  const [modalOpen, setModalOpen] = useState(false);

  // 🆕 데이터 변경 시 자동으로 컬럼 너비 조정 트리거
  const [autoWidthTrigger, setAutoWidthTrigger] = useState(0);

  // 소스 테이블 컬럼 라벨 매핑
  const [sourceColumnLabels, setSourceColumnLabels] = useState<Record<string, string>>({});

  // 🆕 소스 테이블의 카테고리 타입 컬럼 목록
  const [sourceCategoryColumns, setSourceCategoryColumns] = useState<string[]>([]);

  // 🆕 카테고리 코드 → 라벨 매핑 (RepeaterTable 표시용)
  const [categoryLabelMap, setCategoryLabelMap] = useState<Record<string, string>>({});

  // 현재 테이블 컬럼 정보 (inputType 매핑용)
  const [currentTableColumnInfo, setCurrentTableColumnInfo] = useState<Record<string, any>>({});

  // 동적 데이터 소스 상태
  const [activeDataSources, setActiveDataSources] = useState<Record<string, string>>({});

  // 🆕 최신 엔티티 참조 정보 (column_labels에서 조회)
  const [resolvedSourceTable, setResolvedSourceTable] = useState<string>("");
  const [resolvedReferenceKey, setResolvedReferenceKey] = useState<string>("id");

  const isModalMode = config.renderMode === "modal";

  // 전역 리피터 등록
  // 🆕 useCustomTable이 설정된 경우 mainTableName 사용 (실제 저장될 테이블)
  useEffect(() => {
    const targetTableName =
      config.useCustomTable && config.mainTableName ? config.mainTableName : config.dataSource?.tableName;

    if (targetTableName) {
      if (!window.__unifiedRepeaterInstances) {
        window.__unifiedRepeaterInstances = new Set();
      }
      window.__unifiedRepeaterInstances.add(targetTableName);
    }

    return () => {
      if (targetTableName && window.__unifiedRepeaterInstances) {
        window.__unifiedRepeaterInstances.delete(targetTableName);
      }
    };
  }, [config.useCustomTable, config.mainTableName, config.dataSource?.tableName]);

  // ============================================================
  // DataProvidable 인터페이스 구현
  // 다른 컴포넌트에서 이 리피터의 데이터를 가져갈 수 있게 함
  // ============================================================
  const dataProvider: DataProvidable = useMemo(
    () => ({
      componentId: parentId || config.fieldName || "unified-repeater",
      componentType: "unified-repeater",

      // 선택된 행 데이터 반환
      getSelectedData: () => {
        return Array.from(selectedRows)
          .map((idx) => data[idx])
          .filter(Boolean);
      },

      // 전체 데이터 반환
      getAllData: () => {
        return [...data];
      },

      // 선택 초기화
      clearSelection: () => {
        setSelectedRows(new Set());
      },
    }),
    [parentId, config.fieldName, data, selectedRows],
  );

  // ============================================================
  // DataReceivable 인터페이스 구현
  // 외부에서 이 리피터로 데이터를 전달받을 수 있게 함
  // ============================================================
  const dataReceiver: DataReceivable = useMemo(
    () => ({
      componentId: parentId || config.fieldName || "unified-repeater",
      componentType: "repeater",

      // 데이터 수신 (append, replace, merge 모드 지원)
      receiveData: async (incomingData: any[], receiverConfig: DataReceiverConfig) => {
        if (!incomingData || incomingData.length === 0) return;

        // 매핑 규칙 적용
        const mappedData = incomingData.map((item, index) => {
          const newRow: any = { _id: `received_${Date.now()}_${index}` };

          if (receiverConfig.mappingRules && receiverConfig.mappingRules.length > 0) {
            receiverConfig.mappingRules.forEach((rule) => {
              const sourceValue = item[rule.sourceField];
              newRow[rule.targetField] = sourceValue !== undefined ? sourceValue : rule.defaultValue;
            });
          } else {
            // 매핑 규칙 없으면 그대로 복사
            Object.assign(newRow, item);
          }

          return newRow;
        });

        // 모드에 따라 데이터 처리
        switch (receiverConfig.mode) {
          case "replace":
            setData(mappedData);
            onDataChange?.(mappedData);
            break;
          case "merge":
            // 중복 제거 후 병합 (id 또는 _id 기준)
            const existingIds = new Set(data.map((row) => row.id || row._id));
            const newItems = mappedData.filter((row) => !existingIds.has(row.id || row._id));
            const mergedData = [...data, ...newItems];
            setData(mergedData);
            onDataChange?.(mergedData);
            break;
          case "append":
          default:
            const appendedData = [...data, ...mappedData];
            setData(appendedData);
            onDataChange?.(appendedData);
            break;
        }
      },

      // 현재 데이터 반환
      getData: () => {
        return [...data];
      },
    }),
    [parentId, config.fieldName, data, onDataChange],
  );

  // ============================================================
  // ScreenContext에 DataProvider/DataReceiver 등록
  // ============================================================
  useEffect(() => {
    if (screenContext && (parentId || config.fieldName)) {
      const componentId = parentId || config.fieldName || "unified-repeater";

      screenContext.registerDataProvider(componentId, dataProvider);
      screenContext.registerDataReceiver(componentId, dataReceiver);

      return () => {
        screenContext.unregisterDataProvider(componentId);
        screenContext.unregisterDataReceiver(componentId);
      };
    }
  }, [screenContext, parentId, config.fieldName, dataProvider, dataReceiver]);

  // ============================================================
  // repeaterDataChange 이벤트 발행
  // 데이터 변경 시 다른 컴포넌트(aggregation-widget 등)에 알림
  // ============================================================
  const prevDataLengthRef = useRef(data.length);
  useEffect(() => {
    // 데이터가 변경되었을 때만 이벤트 발행
    if (typeof window !== "undefined" && data.length !== prevDataLengthRef.current) {
      dispatchV2Event(V2_EVENTS.REPEATER_DATA_CHANGE, {
        componentId: parentId || config.fieldName || "unified-repeater",
        tableName: config.dataSource?.tableName || "",
        data: data,
        selectedData: Array.from(selectedRows)
          .map((idx) => data[idx])
          .filter(Boolean),
      });
      prevDataLengthRef.current = data.length;
    }
  }, [data, selectedRows, parentId, config.fieldName, config.dataSource?.tableName]);

  // 저장 이벤트 리스너
  useEffect(() => {
    const handleSaveEvent = async (event: CustomEvent) => {
      // 🆕 mainTableName이 설정된 경우 우선 사용, 없으면 dataSource.tableName 사용
      const tableName =
        config.useCustomTable && config.mainTableName ? config.mainTableName : config.dataSource?.tableName;
      const eventParentId = event.detail?.parentId;
      const mainFormData = event.detail?.mainFormData;

      // 🆕 마스터 테이블에서 생성된 ID (FK 연결용)
      const masterRecordId = event.detail?.masterRecordId || mainFormData?.id;

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

      // UnifiedRepeater 저장 시작
      const saveInfo = {
        tableName,
        useCustomTable: config.useCustomTable,
        mainTableName: config.mainTableName,
        foreignKeyColumn: config.foreignKeyColumn,
        masterRecordId,
        dataLength: data.length,
      };
      console.log("UnifiedRepeater 저장 시작", saveInfo);

      try {
        // 테이블 유효 컬럼 조회
        let validColumns: Set<string> = new Set();
        try {
          const columnsResponse = await apiClient.get(`/table-management/tables/${tableName}/columns`);
          const columns =
            columnsResponse.data?.data?.columns || columnsResponse.data?.columns || columnsResponse.data || [];
          validColumns = new Set(columns.map((col: any) => col.columnName || col.column_name || col.name));
        } catch {
          console.warn("테이블 컬럼 정보 조회 실패");
        }

        for (let i = 0; i < data.length; i++) {
          const row = data[i];

          // 내부 필드 제거
          const cleanRow = Object.fromEntries(Object.entries(row).filter(([key]) => !key.startsWith("_")));

          // 메인 폼 데이터 병합 (커스텀 테이블 사용 시에는 메인 폼 데이터 병합 안함)
          let mergedData: Record<string, any>;
          if (config.useCustomTable && config.mainTableName) {
            // 커스텀 테이블: 리피터 데이터만 저장
            mergedData = { ...cleanRow };

            // 🆕 FK 자동 연결 - foreignKeySourceColumn이 설정된 경우 해당 컬럼 값 사용
            if (config.foreignKeyColumn) {
              // foreignKeySourceColumn이 있으면 mainFormData에서 해당 컬럼 값 사용
              // 없으면 마스터 레코드 ID 사용 (기존 동작)
              const sourceColumn = config.foreignKeySourceColumn;
              let fkValue: any;

              if (sourceColumn && mainFormData && mainFormData[sourceColumn] !== undefined) {
                // mainFormData에서 참조 컬럼 값 가져오기
                fkValue = mainFormData[sourceColumn];
              } else {
                // 기본: 마스터 레코드 ID 사용
                fkValue = masterRecordId;
              }

              if (fkValue !== undefined && fkValue !== null) {
                mergedData[config.foreignKeyColumn] = fkValue;
              }
            }
          } else {
            // 기존 방식: 메인 폼 데이터 병합
            const { id: _mainId, ...mainFormDataWithoutId } = mainFormData || {};
            mergedData = {
              ...mainFormDataWithoutId,
              ...cleanRow,
            };
          }

          // 유효하지 않은 컬럼 제거
          const filteredData: Record<string, any> = {};
          for (const [key, value] of Object.entries(mergedData)) {
            if (validColumns.size === 0 || validColumns.has(key)) {
              filteredData[key] = value;
            }
          }

          await apiClient.post(`/table-management/tables/${tableName}/add`, filteredData);
        }
      } catch (error) {
        console.error("❌ UnifiedRepeater 저장 실패:", error);
        throw error;
      }
    };

    // V2 EventBus 구독
    const unsubscribe = v2EventBus.subscribe(
      V2_EVENTS.REPEATER_SAVE,
      async (payload) => {
        const tableName =
          config.useCustomTable && config.mainTableName ? config.mainTableName : config.dataSource?.tableName;
        if (payload.tableName === tableName) {
          await handleSaveEvent({ detail: payload } as CustomEvent);
        }
      },
      { componentId: `unified-repeater-${config.dataSource?.tableName}` },
    );

    // 레거시 이벤트도 계속 지원 (점진적 마이그레이션)
    window.addEventListener("repeaterSave" as any, handleSaveEvent);
    return () => {
      unsubscribe();
      window.removeEventListener("repeaterSave" as any, handleSaveEvent);
    };
  }, [
    data,
    config.dataSource?.tableName,
    config.useCustomTable,
    config.mainTableName,
    config.foreignKeyColumn,
    parentId,
  ]);

  // 현재 테이블 컬럼 정보 로드
  useEffect(() => {
    const loadCurrentTableColumnInfo = async () => {
      const tableName = config.dataSource?.tableName;
      if (!tableName) return;

      try {
        const response = await apiClient.get(`/table-management/tables/${tableName}/columns`);
        const columns = response.data?.data?.columns || response.data?.columns || response.data || [];

        const columnMap: Record<string, any> = {};
        columns.forEach((col: any) => {
          const name = col.columnName || col.column_name || col.name;
          columnMap[name] = {
            inputType: col.inputType || col.input_type || col.webType || "text",
            displayName: col.displayName || col.display_name || col.label || name,
            detailSettings: col.detailSettings || col.detail_settings,
          };
        });
        setCurrentTableColumnInfo(columnMap);
      } catch (error) {
        console.error("컬럼 정보 로드 실패:", error);
      }
    };
    loadCurrentTableColumnInfo();
  }, [config.dataSource?.tableName]);

  // 🆕 FK 컬럼 기반으로 최신 참조 테이블 정보 조회 (column_labels에서)
  useEffect(() => {
    const resolveEntityReference = async () => {
      const tableName = config.dataSource?.tableName;
      const foreignKey = config.dataSource?.foreignKey;

      if (!isModalMode || !tableName || !foreignKey) {
        // config에 저장된 값을 기본값으로 사용
        setResolvedSourceTable(config.dataSource?.sourceTable || "");
        setResolvedReferenceKey(config.dataSource?.referenceKey || "id");
        return;
      }

      try {
        // 현재 테이블의 컬럼 정보에서 FK 컬럼의 참조 테이블 조회
        const response = await apiClient.get(`/table-management/tables/${tableName}/columns`);
        const columns = response.data?.data?.columns || response.data?.columns || response.data || [];

        const fkColumn = columns.find((col: any) => (col.columnName || col.column_name || col.name) === foreignKey);

        if (fkColumn) {
          // column_labels의 reference_table 사용 (항상 최신값)
          const refTable =
            fkColumn.detailSettings?.referenceTable ||
            fkColumn.reference_table ||
            fkColumn.referenceTable ||
            config.dataSource?.sourceTable ||
            "";
          const refKey =
            fkColumn.detailSettings?.referenceColumn ||
            fkColumn.reference_column ||
            fkColumn.referenceColumn ||
            config.dataSource?.referenceKey ||
            "id";

          setResolvedSourceTable(refTable);
          setResolvedReferenceKey(refKey);
        } else {
          // FK 컬럼을 찾지 못한 경우 config 값 사용
          setResolvedSourceTable(config.dataSource?.sourceTable || "");
          setResolvedReferenceKey(config.dataSource?.referenceKey || "id");
        }
      } catch (error) {
        console.error("엔티티 참조 정보 조회 실패:", error);
        // 오류 시 config 값 사용
        setResolvedSourceTable(config.dataSource?.sourceTable || "");
        setResolvedReferenceKey(config.dataSource?.referenceKey || "id");
      }
    };

    resolveEntityReference();
  }, [
    config.dataSource?.tableName,
    config.dataSource?.foreignKey,
    config.dataSource?.sourceTable,
    config.dataSource?.referenceKey,
    isModalMode,
  ]);

  // 소스 테이블 컬럼 라벨 로드 (modal 모드) - resolvedSourceTable 사용
  // 🆕 카테고리 타입 컬럼도 함께 감지
  useEffect(() => {
    const loadSourceColumnLabels = async () => {
      if (!isModalMode || !resolvedSourceTable) return;

      try {
        const response = await apiClient.get(`/table-management/tables/${resolvedSourceTable}/columns`);
        const columns = response.data?.data?.columns || response.data?.columns || response.data || [];

        const labels: Record<string, string> = {};
        const categoryCols: string[] = [];

        columns.forEach((col: any) => {
          const name = col.columnName || col.column_name || col.name;
          labels[name] = col.displayName || col.display_name || col.label || name;

          // 🆕 카테고리 타입 컬럼 감지
          const inputType = col.inputType || col.input_type || "";
          if (inputType === "category") {
            categoryCols.push(name);
          }
        });

        setSourceColumnLabels(labels);
        setSourceCategoryColumns(categoryCols);
      } catch (error) {
        console.error("소스 컬럼 라벨 로드 실패:", error);
      }
    };
    loadSourceColumnLabels();
  }, [resolvedSourceTable, isModalMode]);

  // UnifiedColumnConfig → RepeaterColumnConfig 변환
  // 🆕 모든 컬럼을 columns 배열의 순서대로 처리 (isSourceDisplay 플래그로 구분)
  const repeaterColumns: RepeaterColumnConfig[] = useMemo(() => {
    return config.columns
      .filter((col: UnifiedColumnConfig) => col.visible !== false)
      .map((col: UnifiedColumnConfig): RepeaterColumnConfig => {
        const colInfo = currentTableColumnInfo[col.key];
        const inputType = col.inputType || colInfo?.inputType || "text";

        // 소스 표시 컬럼인 경우 (모달 모드에서 읽기 전용)
        if (col.isSourceDisplay) {
          const label = col.title || sourceColumnLabels[col.key] || col.key;
          return {
            field: `_display_${col.key}`,
            label,
            type: "text",
            editable: false,
            calculated: true,
            width: col.width === "auto" ? undefined : col.width,
          };
        }

        // 일반 입력 컬럼
        let type: "text" | "number" | "date" | "select" | "category" = "text";
        if (inputType === "number" || inputType === "decimal") type = "number";
        else if (inputType === "date" || inputType === "datetime") type = "date";
        else if (inputType === "code") type = "select";
        else if (inputType === "category") type = "category"; // 🆕 카테고리 타입

        // 🆕 카테고리 참조 ID 가져오기 (tableName.columnName 형식)
        // category 타입인 경우 현재 테이블명과 컬럼명을 조합
        let categoryRef: string | undefined;
        if (inputType === "category") {
          // 🆕 소스 표시 컬럼이면 소스 테이블 사용, 아니면 타겟 테이블 사용
          const tableName = col.isSourceDisplay ? resolvedSourceTable : config.dataSource?.tableName;
          if (tableName) {
            categoryRef = `${tableName}.${col.key}`;
          }
        }

        return {
          field: col.key,
          label: col.title || colInfo?.displayName || col.key,
          type,
          editable: col.editable !== false,
          width: col.width === "auto" ? undefined : col.width,
          required: false,
          categoryRef, // 🆕 카테고리 참조 ID 전달
          hidden: col.hidden, // 🆕 히든 처리
          autoFill: col.autoFill, // 🆕 자동 입력 설정
        };
      });
  }, [config.columns, sourceColumnLabels, currentTableColumnInfo, resolvedSourceTable, config.dataSource?.tableName]);

  // 🆕 데이터 변경 시 카테고리 라벨 로드 (RepeaterTable 표시용)
  useEffect(() => {
    const loadCategoryLabels = async () => {
      if (sourceCategoryColumns.length === 0 || data.length === 0) {
        return;
      }

      // 데이터에서 카테고리 컬럼의 모든 고유 코드 수집
      const allCodes = new Set<string>();
      for (const row of data) {
        for (const col of sourceCategoryColumns) {
          // _display_ 접두사가 있는 컬럼과 원본 컬럼 모두 확인
          const val = row[`_display_${col}`] || row[col];
          if (val && typeof val === "string") {
            const codes = val
              .split(",")
              .map((c: string) => c.trim())
              .filter(Boolean);
            for (const code of codes) {
              if (!categoryLabelMap[code] && code.startsWith("CATEGORY_")) {
                allCodes.add(code);
              }
            }
          }
        }
      }

      if (allCodes.size === 0) {
        return;
      }

      try {
        const response = await apiClient.post("/table-categories/labels-by-codes", {
          valueCodes: Array.from(allCodes),
        });

        if (response.data?.success && response.data.data) {
          setCategoryLabelMap((prev) => ({
            ...prev,
            ...response.data.data,
          }));
        }
      } catch (error) {
        console.error("카테고리 라벨 조회 실패:", error);
      }
    };

    loadCategoryLabels();
  }, [data, sourceCategoryColumns]);

  // 데이터 변경 핸들러
  const handleDataChange = useCallback(
    (newData: any[]) => {
      setData(newData);

      // 🆕 _targetTable 메타데이터 포함하여 전달 (백엔드에서 테이블 분리용)
      if (onDataChange) {
        const targetTable =
          config.useCustomTable && config.mainTableName ? config.mainTableName : config.dataSource?.tableName;

        if (targetTable) {
          // 각 행에 _targetTable 추가
          const dataWithTarget = newData.map((row) => ({
            ...row,
            _targetTable: targetTable,
          }));
          onDataChange(dataWithTarget);
        } else {
          onDataChange(newData);
        }
      }

      // 🆕 데이터 변경 시 자동으로 컬럼 너비 조정
      setAutoWidthTrigger((prev) => prev + 1);
    },
    [onDataChange, config.useCustomTable, config.mainTableName, config.dataSource?.tableName],
  );

  // 행 변경 핸들러
  const handleRowChange = useCallback(
    (index: number, newRow: any) => {
      const newData = [...data];
      newData[index] = newRow;
      setData(newData);

      // 🆕 _targetTable 메타데이터 포함
      if (onDataChange) {
        const targetTable =
          config.useCustomTable && config.mainTableName ? config.mainTableName : config.dataSource?.tableName;

        if (targetTable) {
          const dataWithTarget = newData.map((row) => ({
            ...row,
            _targetTable: targetTable,
          }));
          onDataChange(dataWithTarget);
        } else {
          onDataChange(newData);
        }
      }
    },
    [data, onDataChange, config.useCustomTable, config.mainTableName, config.dataSource?.tableName],
  );

  // 행 삭제 핸들러
  const handleRowDelete = useCallback(
    (index: number) => {
      const newData = data.filter((_, i) => i !== index);
      handleDataChange(newData); // 🆕 handleDataChange 사용

      // 선택 상태 업데이트
      const newSelected = new Set<number>();
      selectedRows.forEach((i) => {
        if (i < index) newSelected.add(i);
        else if (i > index) newSelected.add(i - 1);
      });
      setSelectedRows(newSelected);
    },
    [data, selectedRows, handleDataChange],
  );

  // 일괄 삭제 핸들러
  const handleBulkDelete = useCallback(() => {
    const newData = data.filter((_, index) => !selectedRows.has(index));
    handleDataChange(newData); // 🆕 handleDataChange 사용
    setSelectedRows(new Set());
  }, [data, selectedRows, handleDataChange]);

  // 행 추가 (inline 모드)
  // 🆕 자동 입력 값 생성 함수 (동기 - 채번 제외)
  const generateAutoFillValueSync = useCallback(
    (col: any, rowIndex: number, mainFormData?: Record<string, unknown>) => {
      if (!col.autoFill || col.autoFill.type === "none") return undefined;

      const now = new Date();

      switch (col.autoFill.type) {
        case "currentDate": {
          const pad = (n: number) => String(n).padStart(2, "0");
          return `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}`;
        }

        case "currentDateTime": {
          const pad = (n: number) => String(n).padStart(2, "0");
          return `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())} ${pad(now.getHours())}:${pad(now.getMinutes())}:${pad(now.getSeconds())}`;
        }

        case "sequence":
          return rowIndex + 1; // 1부터 시작하는 순번

        case "numbering":
          // 채번은 별도 비동기 처리 필요
          return null; // null 반환하여 비동기 처리 필요함을 표시

        case "fromMainForm":
          if (col.autoFill.sourceField && mainFormData) {
            return mainFormData[col.autoFill.sourceField];
          }
          return "";

        case "fixed":
          return col.autoFill.fixedValue ?? "";

        default:
          return undefined;
      }
    },
    [],
  );

  // 🆕 채번 API 호출 (비동기)
  // 🆕 수동 입력 부분 지원을 위해 userInputCode 파라미터 추가
  const generateNumberingCode = useCallback(
    async (ruleId: string, userInputCode?: string, formData?: Record<string, any>): Promise<string> => {
      try {
        const result = await allocateNumberingCode(ruleId, userInputCode, formData);
        if (result.success && result.data?.generatedCode) {
          return result.data.generatedCode;
        }
        console.error("채번 실패:", result.error);
        return "";
      } catch (error) {
        console.error("채번 API 호출 실패:", error);
        return "";
      }
    },
    [],
  );

  // 🆕 행 추가 (inline 모드 또는 모달 열기) - 비동기로 변경
  const handleAddRow = useCallback(async () => {
    if (isModalMode) {
      setModalOpen(true);
    } else {
      const newRow: any = { _id: `new_${Date.now()}` };
      const currentRowCount = data.length;

      // 먼저 동기적 자동 입력 값 적용
      for (const col of config.columns) {
        const autoValue = generateAutoFillValueSync(col, currentRowCount);
        if (autoValue === null && col.autoFill?.type === "numbering" && col.autoFill.numberingRuleId) {
          // 채번 규칙: 즉시 API 호출
          newRow[col.key] = await generateNumberingCode(col.autoFill.numberingRuleId);
        } else if (autoValue !== undefined) {
          newRow[col.key] = autoValue;
        } else {
          newRow[col.key] = "";
        }
      }

      const newData = [...data, newRow];
      handleDataChange(newData);
    }
  }, [isModalMode, config.columns, data, handleDataChange, generateAutoFillValueSync, generateNumberingCode]);

  // 모달에서 항목 선택 - 비동기로 변경
  const handleSelectItems = useCallback(
    async (items: Record<string, unknown>[]) => {
      const fkColumn = config.dataSource?.foreignKey;
      const currentRowCount = data.length;

      // 채번이 필요한 컬럼 찾기
      const numberingColumns = config.columns.filter(
        (col) => !col.isSourceDisplay && col.autoFill?.type === "numbering" && col.autoFill.numberingRuleId,
      );

      const newRows = await Promise.all(
        items.map(async (item, index) => {
          const row: any = { _id: `new_${Date.now()}_${Math.random()}` };

          // FK 값 저장 (resolvedReferenceKey 사용)
          if (fkColumn && item[resolvedReferenceKey]) {
            row[fkColumn] = item[resolvedReferenceKey];
          }

          // 모든 컬럼 처리 (순서대로)
          for (const col of config.columns) {
            if (col.isSourceDisplay) {
              // 소스 표시 컬럼: 소스 테이블에서 값 복사 (읽기 전용)
              row[`_display_${col.key}`] = item[col.key] || "";
            } else {
              // 자동 입력 값 적용
              const autoValue = generateAutoFillValueSync(col, currentRowCount + index);
              if (autoValue === null && col.autoFill?.type === "numbering" && col.autoFill.numberingRuleId) {
                // 채번 규칙: 즉시 API 호출
                row[col.key] = await generateNumberingCode(col.autoFill.numberingRuleId);
              } else if (autoValue !== undefined) {
                row[col.key] = autoValue;
              } else if (row[col.key] === undefined) {
                // 입력 컬럼: 빈 값으로 초기화
                row[col.key] = "";
              }
            }
          }

          return row;
        }),
      );

      const newData = [...data, ...newRows];
      handleDataChange(newData);
      setModalOpen(false);
    },
    [
      config.dataSource?.foreignKey,
      resolvedReferenceKey,
      config.columns,
      data,
      handleDataChange,
      generateAutoFillValueSync,
      generateNumberingCode,
    ],
  );

  // 소스 컬럼 목록 (모달용) - 🆕 columns 배열에서 isSourceDisplay인 것만 필터링
  const sourceColumns = useMemo(() => {
    return config.columns
      .filter((col) => col.isSourceDisplay && col.visible !== false)
      .map((col) => col.key)
      .filter((key) => key && key !== "none");
  }, [config.columns]);

  // 🆕 beforeFormSave 이벤트에서 채번 placeholder를 실제 값으로 변환
  const dataRef = useRef(data);
  dataRef.current = data;

  useEffect(() => {
    const handleBeforeFormSave = async (event: Event) => {
      const customEvent = event as CustomEvent;
      const formData = customEvent.detail?.formData;

      if (!formData || !dataRef.current.length) return;

      // 채번 placeholder가 있는 행들을 찾아서 실제 값으로 변환
      const processedData = await Promise.all(
        dataRef.current.map(async (row) => {
          const newRow = { ...row };

          for (const key of Object.keys(newRow)) {
            const value = newRow[key];
            if (typeof value === "string" && value.startsWith("__NUMBERING_RULE__")) {
              // __NUMBERING_RULE__ruleId__ 형식에서 ruleId 추출
              const match = value.match(/__NUMBERING_RULE__(.+)__/);
              if (match) {
                const ruleId = match[1];
                try {
                  // 🆕 사용자가 편집한 값을 전달 (수동 입력 부분 추출용)
                  const result = await allocateNumberingCode(ruleId, undefined, newRow);
                  if (result.success && result.data?.generatedCode) {
                    newRow[key] = result.data.generatedCode;
                  } else {
                    console.error("채번 실패:", result.error);
                    newRow[key] = ""; // 채번 실패 시 빈 값
                  }
                } catch (error) {
                  console.error("채번 API 호출 실패:", error);
                  newRow[key] = "";
                }
              }
            }
          }

          return newRow;
        }),
      );

      // 처리된 데이터를 formData에 추가
      const fieldName = config.fieldName || "repeaterData";
      formData[fieldName] = processedData;
    };

    // V2 EventBus 구독
    const unsubscribe = v2EventBus.subscribe(
      V2_EVENTS.FORM_SAVE_COLLECT,
      async (payload) => {
        // formData 객체가 있으면 데이터 수집
        const fakeEvent = {
          detail: { formData: payload.formData },
        } as CustomEvent;
        await handleBeforeFormSave(fakeEvent);
      },
      { componentId: `unified-repeater-${config.dataSource?.tableName}` },
    );

    // 레거시 이벤트도 계속 지원 (점진적 마이그레이션)
    window.addEventListener("beforeFormSave", handleBeforeFormSave);

    return () => {
      unsubscribe();
      window.removeEventListener("beforeFormSave", handleBeforeFormSave);
    };
  }, [config.fieldName]);

  // 🆕 데이터 전달 이벤트 리스너 (transferData 버튼 액션용)
  useEffect(() => {
    // componentDataTransfer: 특정 컴포넌트 ID로 데이터 전달
    const handleComponentDataTransfer = async (event: Event) => {
      const customEvent = event as CustomEvent;
      const { targetComponentId, data: transferData, mappingRules, mode } = customEvent.detail || {};

      // 이 컴포넌트가 대상인지 확인
      if (targetComponentId !== parentId && targetComponentId !== config.fieldName) {
        return;
      }

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

      // 데이터 매핑 처리
      const mappedData = transferData.map((item: any, index: number) => {
        const newRow: any = { _id: `transfer_${Date.now()}_${index}` };

        if (mappingRules && mappingRules.length > 0) {
          // 매핑 규칙이 있으면 적용
          mappingRules.forEach((rule: any) => {
            newRow[rule.targetField] = item[rule.sourceField];
          });
        } else {
          // 매핑 규칙 없으면 그대로 복사
          Object.assign(newRow, item);
        }

        return newRow;
      });

      // mode에 따라 데이터 처리
      if (mode === "replace") {
        handleDataChange(mappedData);
      } else if (mode === "merge") {
        // 중복 제거 후 병합 (id 기준)
        const existingIds = new Set(data.map((row) => row.id || row._id));
        const newItems = mappedData.filter((row: any) => !existingIds.has(row.id || row._id));
        handleDataChange([...data, ...newItems]);
      } else {
        // 기본: append
        handleDataChange([...data, ...mappedData]);
      }
    };

    // splitPanelDataTransfer: 분할 패널에서 전역 이벤트로 전달
    const handleSplitPanelDataTransfer = async (event: Event) => {
      const customEvent = event as CustomEvent;
      const { data: transferData, mappingRules, mode, sourcePosition } = customEvent.detail || {};

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

      // 데이터 매핑 처리
      const mappedData = transferData.map((item: any, index: number) => {
        const newRow: any = { _id: `transfer_${Date.now()}_${index}` };

        if (mappingRules && mappingRules.length > 0) {
          mappingRules.forEach((rule: any) => {
            newRow[rule.targetField] = item[rule.sourceField];
          });
        } else {
          Object.assign(newRow, item);
        }

        return newRow;
      });

      // mode에 따라 데이터 처리
      if (mode === "replace") {
        handleDataChange(mappedData);
      } else {
        handleDataChange([...data, ...mappedData]);
      }
    };

    // V2 EventBus 구독
    const unsubscribeComponent = v2EventBus.subscribe(
      V2_EVENTS.COMPONENT_DATA_TRANSFER,
      (payload) => {
        const fakeEvent = {
          detail: {
            targetComponentId: payload.targetComponentId,
            transferData: [payload.data],
            mappingRules: [],
            mode: "append",
          },
        } as CustomEvent;
        handleComponentDataTransfer(fakeEvent);
      },
      { componentId: `unified-repeater-${config.dataSource?.tableName}` },
    );

    const unsubscribeSplitPanel = v2EventBus.subscribe(
      V2_EVENTS.SPLIT_PANEL_DATA_TRANSFER,
      (payload) => {
        const fakeEvent = {
          detail: {
            transferData: [payload.data],
            mappingRules: [],
            mode: "append",
          },
        } as CustomEvent;
        handleSplitPanelDataTransfer(fakeEvent);
      },
      { componentId: `unified-repeater-${config.dataSource?.tableName}` },
    );

    // 레거시 이벤트도 계속 지원 (점진적 마이그레이션)
    window.addEventListener("componentDataTransfer", handleComponentDataTransfer as EventListener);
    window.addEventListener("splitPanelDataTransfer", handleSplitPanelDataTransfer as EventListener);

    return () => {
      unsubscribeComponent();
      unsubscribeSplitPanel();
      window.removeEventListener("componentDataTransfer", handleComponentDataTransfer as EventListener);
      window.removeEventListener("splitPanelDataTransfer", handleSplitPanelDataTransfer as EventListener);
    };
  }, [parentId, config.fieldName, data, handleDataChange]);

  return (
    <div className={cn("space-y-4", className)}>
      {/* 헤더 영역 */}
      <div className="flex items-center justify-between">
        <div className="flex items-center gap-2">
          <span className="text-muted-foreground text-sm">
            {data.length > 0 && `${data.length}개 항목`}
            {selectedRows.size > 0 && ` (${selectedRows.size}개 선택됨)`}
          </span>
        </div>
        <div className="flex gap-2">
          {selectedRows.size > 0 && (
            <Button variant="destructive" onClick={handleBulkDelete} className="h-8 text-xs sm:h-10 sm:text-sm">
              선택 삭제 ({selectedRows.size})
            </Button>
          )}
          <Button onClick={handleAddRow} className="h-8 text-xs sm:h-10 sm:text-sm">
            <Plus className="mr-2 h-4 w-4" />
            {isModalMode ? config.modal?.buttonText || "검색" : "추가"}
          </Button>
        </div>
      </div>

      {/* Repeater 테이블 */}
      <RepeaterTable
        columns={repeaterColumns}
        data={data}
        onDataChange={handleDataChange}
        onRowChange={handleRowChange}
        onRowDelete={handleRowDelete}
        activeDataSources={activeDataSources}
        onDataSourceChange={(field, optionId) => {
          setActiveDataSources((prev) => ({ ...prev, [field]: optionId }));
        }}
        selectedRows={selectedRows}
        onSelectionChange={setSelectedRows}
        equalizeWidthsTrigger={autoWidthTrigger}
        categoryColumns={sourceCategoryColumns}
        categoryLabelMap={categoryLabelMap}
      />

      {/* 항목 선택 모달 (modal 모드) - 검색 필드는 표시 컬럼과 동일하게 자동 설정 */}
      {isModalMode && (
        <ItemSelectionModal
          open={modalOpen}
          onOpenChange={setModalOpen}
          sourceTable={resolvedSourceTable}
          sourceColumns={sourceColumns}
          sourceSearchFields={sourceColumns}
          multiSelect={config.features?.multiSelect ?? true}
          modalTitle={config.modal?.title || "항목 검색"}
          alreadySelected={data}
          uniqueField={resolvedReferenceKey}
          onSelect={handleSelectItems}
          columnLabels={sourceColumnLabels}
          categoryColumns={sourceCategoryColumns}
        />
      )}
    </div>
  );
};

UnifiedRepeater.displayName = "UnifiedRepeater";

// V2ErrorBoundary로 래핑된 안전한 버전 export
export const SafeUnifiedRepeater: React.FC<UnifiedRepeaterProps> = (props) => {
  return (
    <V2ErrorBoundary
      componentId={props.parentId || "unified-repeater"}
      componentType="UnifiedRepeater"
      fallbackStyle="compact"
    >
      <UnifiedRepeater {...props} />
    </V2ErrorBoundary>
  );
};

export default UnifiedRepeater;
