"use client";

import React, { useState, useEffect, useRef } from "react";
import { Input } from "@/components/ui/input";
import { Textarea } from "@/components/ui/textarea";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
import { Separator } from "@/components/ui/separator";
import { Badge } from "@/components/ui/badge";
import { Plus, X, GripVertical, ChevronDown, ChevronUp } from "lucide-react";
import {
  RepeaterFieldGroupConfig,
  RepeaterData,
  RepeaterItemData,
  RepeaterFieldDefinition,
  CalculationFormula,
  SubDataState,
} from "@/types/repeater";
import { SubDataLookupPanel } from "@/lib/registry/components/repeater-field-group/SubDataLookupPanel";
import { cn } from "@/lib/utils";
import { useBreakpoint } from "@/hooks/useBreakpoint";
import { usePreviewBreakpoint } from "@/components/screen/ResponsivePreviewModal";

export interface RepeaterInputProps {
  value?: RepeaterData;
  onChange?: (value: RepeaterData) => void;
  config?: RepeaterFieldGroupConfig;
  disabled?: boolean;
  readonly?: boolean;
  className?: string;
  menuObjid?: number; // 카테고리 조회용 메뉴 ID
}

/**
 * 반복 필드 그룹 컴포넌트
 * 여러 필드를 가진 항목들을 동적으로 추가/제거할 수 있는 컴포넌트
 */
export const RepeaterInput: React.FC<RepeaterInputProps> = ({
  value = [],
  onChange,
  config = { fields: [] },
  disabled = false,
  readonly = false,
  className,
  menuObjid,
}) => {
  // 현재 브레이크포인트 감지
  const globalBreakpoint = useBreakpoint();
  const previewBreakpoint = usePreviewBreakpoint();

  // 미리보기 모달 내에서는 previewBreakpoint 우선 사용
  const breakpoint = previewBreakpoint || globalBreakpoint;

  // 카테고리 매핑 데이터 (값 -> {label, color})
  const [categoryMappings, setCategoryMappings] = useState<
    Record<string, Record<string, { label: string; color: string }>>
  >({});

  // 설정 기본값
  const {
    fields = [],
    minItems = 0,
    maxItems = 10,
    addButtonText = "항목 추가",
    allowReorder = true,
    showIndex = true,
    collapsible = false,
    layout = "grid", // 기본값을 grid로 설정
    showDivider = true,
    emptyMessage = "항목이 없습니다. '항목 추가' 버튼을 클릭하세요.",
    subDataLookup,
  } = config;

  // 하위 데이터 조회 상태 관리 (각 항목별)
  const [subDataStates, setSubDataStates] = useState<Map<number, SubDataState>>(new Map());

  // 반응형: 작은 화면(모바일/태블릿)에서는 카드 레이아웃 강제
  const effectiveLayout = breakpoint === "mobile" || breakpoint === "tablet" ? "card" : layout;

  // 로컬 상태 관리
  const [items, setItems] = useState<RepeaterData>(
    value.length > 0
      ? value
      : minItems > 0
        ? Array(minItems)
            .fill(null)
            .map(() => createEmptyItem())
        : [],
  );

  // 접힌 상태 관리 (각 항목별)
  const [collapsedItems, setCollapsedItems] = useState<Set<number>>(new Set());

  // 🆕 초기 계산 완료 여부 추적 (무한 루프 방지)
  const initialCalcDoneRef = useRef(false);

  // 🆕 삭제된 항목 ID 목록 추적 (ref로 관리하여 즉시 반영)
  const deletedItemIdsRef = useRef<string[]>([]);

  // 빈 항목 생성
  function createEmptyItem(): RepeaterItemData {
    const item: RepeaterItemData = {};
    fields.forEach((field) => {
      item[field.name] = "";
    });
    // 🆕 새 항목임을 표시하는 플래그 추가 (백엔드에서 새 레코드로 처리)
    item._isNewItem = true;
    return item;
  }

  // 외부 value 변경 시 동기화 및 초기 계산식 필드 업데이트
  useEffect(() => {
    // 🆕 빈 배열도 처리 (FK 기반 필터링 시 데이터가 없을 수 있음)
    if (value.length === 0) {
      // minItems가 설정되어 있으면 빈 항목 생성, 아니면 빈 배열로 초기화
      if (minItems > 0) {
        const emptyItems = Array(minItems)
          .fill(null)
          .map(() => createEmptyItem());
        setItems(emptyItems);
      } else {
        setItems([]);
      }
      initialCalcDoneRef.current = false; // 다음 데이터 로드 시 계산식 재실행
      return;
    }

    // 🆕 초기 로드 시 계산식 필드 자동 업데이트 (한 번만 실행)
    const calculatedFields = fields.filter((f) => f.type === "calculated");

    if (calculatedFields.length > 0 && !initialCalcDoneRef.current) {
      const updatedValue = value.map((item) => {
        const updatedItem = { ...item };
        let hasChange = false;

        calculatedFields.forEach((calcField) => {
          const calculatedValue = calculateValue(calcField.formula, updatedItem);
          if (calculatedValue !== null && updatedItem[calcField.name] !== calculatedValue) {
            updatedItem[calcField.name] = calculatedValue;
            hasChange = true;
          }
        });

        // 🆕 기존 레코드임을 표시 (id가 있는 경우)
        if (updatedItem.id) {
          updatedItem._existingRecord = true;
        }

        return hasChange ? updatedItem : item;
      });

      setItems(updatedValue);
      initialCalcDoneRef.current = true;

      // 계산된 값이 있으면 onChange 호출 (초기 1회만)
      const dataWithMeta = config.targetTable
        ? updatedValue.map((item) => ({ ...item, _targetTable: config.targetTable }))
        : updatedValue;
      onChange?.(dataWithMeta);
    } else {
      // 🆕 기존 레코드 플래그 추가
      const valueWithFlag = value.map((item) => ({
        ...item,
        _existingRecord: !!item.id,
      }));
      setItems(valueWithFlag);
    }
  }, [value]);

  // 항목 추가
  const handleAddItem = () => {
    if (items.length >= maxItems) {
      return;
    }
    const newItems = [...items, createEmptyItem()];
    setItems(newItems);
    console.log("➕ RepeaterInput 항목 추가, onChange 호출:", newItems);

    // targetTable이 설정된 경우 각 항목에 메타데이터 추가
    const dataWithMeta = config.targetTable
      ? newItems.map((item) => ({ ...item, _targetTable: config.targetTable }))
      : newItems;

    onChange?.(dataWithMeta);
  };

  // 항목 제거
  const handleRemoveItem = (index: number) => {
    // 🆕 항목이 1개 이하일 때도 삭제 가능 (빈 상태 허용)
    // minItems 체크 제거 - 모든 항목 삭제 허용

    // 🆕 삭제되는 항목의 ID 저장 (DB에서 삭제할 때 필요)
    const removedItem = items[index];
    if (removedItem?.id) {
      console.log("🗑️ [RepeaterInput] 삭제할 항목 ID 추가:", removedItem.id);
      deletedItemIdsRef.current = [...deletedItemIdsRef.current, removedItem.id];
    }

    const newItems = items.filter((_, i) => i !== index);
    setItems(newItems);

    // targetTable이 설정된 경우 각 항목에 메타데이터 추가
    // 🆕 삭제된 항목 ID 목록도 함께 전달 (ref에서 최신값 사용)
    const currentDeletedIds = deletedItemIdsRef.current;
    console.log("🗑️ [RepeaterInput] 현재 삭제 목록:", currentDeletedIds);

    const dataWithMeta = config.targetTable
      ? newItems.map((item, idx) => ({
          ...item,
          _targetTable: config.targetTable,
          // 첫 번째 항목에만 삭제 ID 목록 포함
          ...(idx === 0 ? { _deletedItemIds: currentDeletedIds } : {}),
        }))
      : newItems;

    console.log("🗑️ [RepeaterInput] onChange 호출 - dataWithMeta:", dataWithMeta);
    onChange?.(dataWithMeta);

    // 접힌 상태도 업데이트
    const newCollapsed = new Set(collapsedItems);
    newCollapsed.delete(index);
    setCollapsedItems(newCollapsed);
  };

  // 필드 값 변경
  const handleFieldChange = (itemIndex: number, fieldName: string, value: any) => {
    const newItems = [...items];
    newItems[itemIndex] = {
      ...newItems[itemIndex],
      [fieldName]: value,
    };

    // 🆕 계산식 필드 자동 업데이트: 변경된 항목의 모든 계산식 필드 값을 재계산
    const calculatedFields = fields.filter((f) => f.type === "calculated");
    calculatedFields.forEach((calcField) => {
      const calculatedValue = calculateValue(calcField.formula, newItems[itemIndex]);
      if (calculatedValue !== null) {
        newItems[itemIndex][calcField.name] = calculatedValue;
      }
    });

    setItems(newItems);
    console.log("✏️ RepeaterInput 필드 변경, onChange 호출:", {
      itemIndex,
      fieldName,
      value,
      newItems,
    });

    // targetTable이 설정된 경우 각 항목에 메타데이터 추가
    // 🆕 삭제된 항목 ID 목록도 유지
    const currentDeletedIds = deletedItemIdsRef.current;
    const dataWithMeta = config.targetTable
      ? newItems.map((item, idx) => ({
          ...item,
          _targetTable: config.targetTable,
          // 첫 번째 항목에만 삭제 ID 목록 포함 (삭제된 항목이 있는 경우에만)
          ...(idx === 0 && currentDeletedIds.length > 0 ? { _deletedItemIds: currentDeletedIds } : {}),
        }))
      : newItems;

    onChange?.(dataWithMeta);
  };

  // 접기/펼치기 토글
  const toggleCollapse = (index: number) => {
    const newCollapsed = new Set(collapsedItems);
    if (newCollapsed.has(index)) {
      newCollapsed.delete(index);
    } else {
      newCollapsed.add(index);
    }
    setCollapsedItems(newCollapsed);
  };

  // 드래그 앤 드롭 (순서 변경)
  const [draggedIndex, setDraggedIndex] = useState<number | null>(null);

  // 하위 데이터 선택 핸들러
  const handleSubDataSelection = (itemIndex: number, selectedItem: any | null, maxValue: number | null) => {
    console.log("[RepeaterInput] 하위 데이터 선택:", { itemIndex, selectedItem, maxValue });

    // 상태 업데이트
    setSubDataStates((prev) => {
      const newMap = new Map(prev);
      const currentState = newMap.get(itemIndex) || {
        itemIndex,
        data: [],
        selectedItem: null,
        isLoading: false,
        error: null,
        isExpanded: false,
      };
      newMap.set(itemIndex, {
        ...currentState,
        selectedItem,
      });
      return newMap;
    });

    // 선택된 항목 정보를 item에 저장
    if (selectedItem && subDataLookup) {
      const newItems = [...items];
      newItems[itemIndex] = {
        ...newItems[itemIndex],
        _subDataSelection: selectedItem,
        _subDataMaxValue: maxValue,
      };

      // fieldMappings가 설정되어 있으면 매핑에 따라 값 복사
      if (subDataLookup.lookup.fieldMappings && subDataLookup.lookup.fieldMappings.length > 0) {
        subDataLookup.lookup.fieldMappings.forEach((mapping) => {
          if (mapping.targetField && mapping.targetField !== "") {
            // 매핑된 타겟 필드에 소스 컬럼 값 복사
            const sourceValue = selectedItem[mapping.sourceColumn];
            if (sourceValue !== undefined) {
              newItems[itemIndex][mapping.targetField] = sourceValue;
            }
          }
        });
      } else {
        // fieldMappings가 없으면 기존 로직 (하위 호환성)
        // 선택된 하위 데이터의 필드 값을 상위 item에 복사 (설정된 경우)
        // 예: warehouse_code, location_code 등
        if (subDataLookup.lookup.displayColumns) {
          subDataLookup.lookup.displayColumns.forEach((col) => {
            if (selectedItem[col] !== undefined) {
              // 필드가 정의되어 있으면 복사
              const fieldDef = fields.find((f) => f.name === col);
              if (fieldDef || col.includes("_code") || col.includes("_id")) {
                newItems[itemIndex][col] = selectedItem[col];
              }
            }
          });
        }
      }

      setItems(newItems);

      // onChange 호출
      const dataWithMeta = config.targetTable
        ? newItems.map((item) => ({ ...item, _targetTable: config.targetTable }))
        : newItems;
      onChange?.(dataWithMeta);
    }
  };

  // 조건부 입력 활성화 여부 확인
  const isConditionalInputEnabled = (itemIndex: number, fieldName: string): boolean => {
    if (!subDataLookup?.enabled) return true;
    if (subDataLookup.conditionalInput?.targetField !== fieldName) return true;

    const subState = subDataStates.get(itemIndex);
    if (!subState?.selectedItem) return false;

    const { requiredFields, requiredMode = "all" } = subDataLookup.selection;
    if (!requiredFields || requiredFields.length === 0) return true;

    if (requiredMode === "any") {
      return requiredFields.some((field) => {
        const value = subState.selectedItem[field];
        return value !== undefined && value !== null && value !== "";
      });
    } else {
      return requiredFields.every((field) => {
        const value = subState.selectedItem[field];
        return value !== undefined && value !== null && value !== "";
      });
    }
  };

  // 최대값 가져오기
  const getMaxValueForField = (itemIndex: number, fieldName: string): number | null => {
    if (!subDataLookup?.enabled) return null;
    if (subDataLookup.conditionalInput?.targetField !== fieldName) return null;
    if (!subDataLookup.conditionalInput?.maxValueField) return null;

    const subState = subDataStates.get(itemIndex);
    if (!subState?.selectedItem) return null;

    const maxVal = subState.selectedItem[subDataLookup.conditionalInput.maxValueField];
    return typeof maxVal === "number" ? maxVal : parseFloat(maxVal) || null;
  };

  // 경고 임계값 체크
  const checkWarningThreshold = (itemIndex: number, fieldName: string, value: number): boolean => {
    if (!subDataLookup?.enabled) return false;
    if (subDataLookup.conditionalInput?.targetField !== fieldName) return false;

    const maxValue = getMaxValueForField(itemIndex, fieldName);
    if (maxValue === null || maxValue === 0) return false;

    const threshold = subDataLookup.conditionalInput?.warningThreshold ?? 90;
    const percentage = (value / maxValue) * 100;
    return percentage >= threshold;
  };

  const handleDragStart = (index: number) => {
    if (!allowReorder || readonly || disabled) return;
    setDraggedIndex(index);
  };

  const handleDragOver = (e: React.DragEvent, index: number) => {
    if (!allowReorder || readonly || disabled) return;
    e.preventDefault();
  };

  const handleDrop = (e: React.DragEvent, targetIndex: number) => {
    if (!allowReorder || readonly || disabled || draggedIndex === null) return;
    e.preventDefault();

    const newItems = [...items];
    const draggedItem = newItems[draggedIndex];
    newItems.splice(draggedIndex, 1);
    newItems.splice(targetIndex, 0, draggedItem);

    setItems(newItems);
    onChange?.(newItems);
    setDraggedIndex(null);
  };

  const handleDragEnd = () => {
    setDraggedIndex(null);
  };

  /**
   * 계산식 실행
   * @param formula 계산식 정의
   * @param item 현재 항목 데이터
   * @returns 계산 결과
   */
  const calculateValue = (formula: CalculationFormula | undefined, item: RepeaterItemData): number | null => {
    if (!formula || !formula.field1) return null;

    const value1 = parseFloat(item[formula.field1]) || 0;
    const value2 = formula.field2 ? parseFloat(item[formula.field2]) || 0 : (formula.constantValue ?? 0);

    let result: number;

    switch (formula.operator) {
      case "+":
        result = value1 + value2;
        break;
      case "-":
        result = value1 - value2;
        break;
      case "*":
        result = value1 * value2;
        break;
      case "/":
        result = value2 !== 0 ? value1 / value2 : 0;
        break;
      case "%":
        result = value2 !== 0 ? value1 % value2 : 0;
        break;
      case "round":
        const decimalPlaces = formula.decimalPlaces ?? 0;
        const multiplier = Math.pow(10, decimalPlaces);
        result = Math.round(value1 * multiplier) / multiplier;
        break;
      case "floor":
        const floorMultiplier = Math.pow(10, formula.decimalPlaces ?? 0);
        result = Math.floor(value1 * floorMultiplier) / floorMultiplier;
        break;
      case "ceil":
        const ceilMultiplier = Math.pow(10, formula.decimalPlaces ?? 0);
        result = Math.ceil(value1 * ceilMultiplier) / ceilMultiplier;
        break;
      case "abs":
        result = Math.abs(value1);
        break;
      default:
        result = value1;
    }

    return result;
  };

  /**
   * 숫자 포맷팅
   * @param value 숫자 값
   * @param format 포맷 설정
   * @returns 포맷된 문자열
   */
  const formatNumber = (value: number | null, format?: RepeaterFieldDefinition["numberFormat"]): string => {
    if (value === null || isNaN(value)) return "-";

    let formattedValue = value;

    // 소수점 자릿수 적용
    if (format?.decimalPlaces !== undefined) {
      formattedValue = parseFloat(value.toFixed(format.decimalPlaces));
    }

    // 천 단위 구분자
    let result =
      format?.useThousandSeparator !== false
        ? formattedValue.toLocaleString("ko-KR", {
            minimumFractionDigits: format?.minimumFractionDigits ?? 0,
            maximumFractionDigits: format?.maximumFractionDigits ?? format?.decimalPlaces ?? 0,
          })
        : formattedValue.toString();

    // 접두사/접미사 추가
    if (format?.prefix) result = format.prefix + result;
    if (format?.suffix) result = result + format.suffix;

    return result;
  };

  // 개별 필드 렌더링
  const renderField = (field: RepeaterFieldDefinition, itemIndex: number, value: any) => {
    const isReadonly = disabled || readonly || field.readonly;

    // 조건부 입력 비활성화 체크
    const isConditionalDisabled =
      subDataLookup?.enabled &&
      subDataLookup.conditionalInput?.targetField === field.name &&
      !isConditionalInputEnabled(itemIndex, field.name);

    // 최대값 및 경고 체크
    const maxValue = getMaxValueForField(itemIndex, field.name);
    const numValue = parseFloat(value) || 0;
    const showWarning = checkWarningThreshold(itemIndex, field.name, numValue);
    const exceedsMax = maxValue !== null && numValue > maxValue;

    // 🆕 placeholder 기본값: 필드에 설정된 값 > 필드 라벨 기반 자동 생성
    // "id(를) 입력하세요" 같은 잘못된 기본값 방지
    const defaultPlaceholder = field.placeholder || `${field.label || field.name}`;

    const commonProps = {
      value: value || "",
      disabled: isReadonly || isConditionalDisabled,
      placeholder: isConditionalDisabled ? "재고 선택 필요" : defaultPlaceholder,
      required: field.required,
    };

    // 계산식 필드: 자동으로 계산된 값을 표시 (읽기 전용)
    if (field.type === "calculated") {
      const item = items[itemIndex];
      const calculatedValue = calculateValue(field.formula, item);
      const formattedValue = formatNumber(calculatedValue, field.numberFormat);

      return <span className="inline-block min-w-[80px] text-sm font-medium text-primary">{formattedValue}</span>;
    }

    // 카테고리 타입은 항상 배지로 표시 (카테고리 관리에서 설정한 색상 적용)
    if (field.type === "category") {
      if (!value) return <span className="text-muted-foreground text-sm">-</span>;

      // field.name을 키로 사용 (테이블 리스트와 동일)
      const mapping = categoryMappings[field.name];
      const valueStr = String(value); // 값을 문자열로 변환
      const categoryData = mapping?.[valueStr];
      const displayLabel = categoryData?.label || valueStr;
      const displayColor = categoryData?.color;

      console.log(`🏷️ [RepeaterInput] 카테고리 배지 렌더링:`, {
        fieldName: field.name,
        value: valueStr,
        mapping,
        categoryData,
        displayLabel,
        displayColor,
      });

      // 배지 없음 옵션: color가 없거나, "none"이거나, 매핑 데이터가 없으면 텍스트만 표시
      if (!displayColor || displayColor === "none" || !categoryData) {
        return <span className="text-sm">{displayLabel}</span>;
      }

      return (
        <Badge
          style={{
            backgroundColor: displayColor,
            borderColor: displayColor,
          }}
          className="text-white"
        >
          {displayLabel}
        </Badge>
      );
    }

    // 읽기 전용 모드: 텍스트로 표시
    // displayMode가 "readonly"이면 isReadonly 여부와 관계없이 텍스트로 표시
    if (field.displayMode === "readonly") {
      // select 타입인 경우 옵션에서 라벨 찾기
      if (field.type === "select" && value && field.options) {
        const option = field.options.find((opt) => opt.value === value);
        return <span className="text-sm">{option?.label || value}</span>;
      }

      // 🆕 카테고리 매핑이 있는 경우 라벨로 변환 (조인된 테이블의 카테고리 필드)
      const mapping = categoryMappings[field.name];
      if (mapping && value) {
        const valueStr = String(value);
        const categoryData = mapping[valueStr];
        if (categoryData) {
          // 색상이 있으면 배지로 표시
          if (categoryData.color && categoryData.color !== "none" && categoryData.color !== "#64748b") {
            return (
              <Badge
                style={{
                  backgroundColor: categoryData.color,
                  borderColor: categoryData.color,
                }}
                className="text-white"
              >
                {categoryData.label}
              </Badge>
            );
          }
          // 색상이 없으면 텍스트로 표시
          return <span className="text-foreground text-sm">{categoryData.label}</span>;
        }
      }

      // 일반 텍스트
      return <span className="text-foreground text-sm">{value || "-"}</span>;
    }

    switch (field.type) {
      case "select":
        return (
          <Select
            value={value || ""}
            onValueChange={(val) => handleFieldChange(itemIndex, field.name, val)}
            disabled={isReadonly}
          >
            <SelectTrigger className="w-full min-w-[80px]">
              <SelectValue placeholder={field.placeholder || "선택하세요"} />
            </SelectTrigger>
            <SelectContent>
              {field.options?.map((option) => (
                <SelectItem key={option.value} value={option.value}>
                  {option.label}
                </SelectItem>
              ))}
            </SelectContent>
          </Select>
        );

      case "textarea":
        return (
          <Textarea
            {...commonProps}
            onChange={(e) => handleFieldChange(itemIndex, field.name, e.target.value)}
            rows={3}
            className="min-w-[100px] resize-none"
          />
        );

      case "date": {
        // 날짜 값 정규화: ISO 형식이면 YYYY-MM-DD로 변환 (타임존 이슈 해결)
        let dateValue = value || "";
        if (dateValue && typeof dateValue === "string") {
          // ISO 형식(YYYY-MM-DDTHH:mm:ss)이면 로컬 시간으로 변환하여 날짜 추출
          if (dateValue.includes("T")) {
            const date = new Date(dateValue);
            if (!isNaN(date.getTime())) {
              const year = date.getFullYear();
              const month = String(date.getMonth() + 1).padStart(2, "0");
              const day = String(date.getDate()).padStart(2, "0");
              dateValue = `${year}-${month}-${day}`;
            } else {
              dateValue = "";
            }
          } else {
            // 유효한 날짜인지 확인
            const parsedDate = new Date(dateValue);
            if (isNaN(parsedDate.getTime())) {
              dateValue = ""; // 유효하지 않은 날짜면 빈 값
            }
          }
        }
        return (
          <Input
            {...commonProps}
            value={dateValue}
            type="date"
            onChange={(e) => handleFieldChange(itemIndex, field.name, e.target.value || null)}
            className="min-w-[120px]"
          />
        );
      }

      case "number":
        // 숫자 포맷이 설정된 경우 포맷팅된 텍스트로 표시
        if (field.numberFormat?.useThousandSeparator || field.numberFormat?.prefix || field.numberFormat?.suffix) {
          const numValue = parseFloat(value) || 0;
          const formattedDisplay = formatNumber(numValue, field.numberFormat);

          // 읽기 전용이면 포맷팅된 텍스트만 표시
          if (isReadonly) {
            return <span className="inline-block min-w-[80px] text-sm">{formattedDisplay}</span>;
          }

          // 편집 가능: 입력은 숫자로, 표시는 포맷팅
          return (
            <div className="relative min-w-[80px]">
              <Input
                {...commonProps}
                type="number"
                onChange={(e) => handleFieldChange(itemIndex, field.name, e.target.value)}
                min={field.validation?.min}
                max={maxValue !== null ? maxValue : field.validation?.max}
                className={cn("pr-1", exceedsMax && "border-destructive", showWarning && !exceedsMax && "border-amber-500")}
              />
              {value && <div className="text-muted-foreground mt-0.5 text-[10px]">{formattedDisplay}</div>}
              {exceedsMax && (
                <div className="mt-0.5 text-[10px] text-destructive">최대 {maxValue}까지 입력 가능</div>
              )}
              {showWarning && !exceedsMax && (
                <div className="mt-0.5 text-[10px] text-amber-600">재고의 {subDataLookup?.conditionalInput?.warningThreshold ?? 90}% 이상</div>
              )}
            </div>
          );
        }

        return (
          <div className="relative min-w-[80px]">
            <Input
              {...commonProps}
              type="number"
              onChange={(e) => handleFieldChange(itemIndex, field.name, e.target.value)}
              min={field.validation?.min}
              max={maxValue !== null ? maxValue : field.validation?.max}
              className={cn(exceedsMax && "border-destructive", showWarning && !exceedsMax && "border-amber-500")}
            />
            {exceedsMax && (
              <div className="mt-0.5 text-[10px] text-destructive">최대 {maxValue}까지 입력 가능</div>
            )}
            {showWarning && !exceedsMax && (
              <div className="mt-0.5 text-[10px] text-amber-600">재고의 {subDataLookup?.conditionalInput?.warningThreshold ?? 90}% 이상</div>
            )}
          </div>
        );

      case "email":
        return (
          <Input
            {...commonProps}
            type="email"
            onChange={(e) => handleFieldChange(itemIndex, field.name, e.target.value)}
            className="min-w-[120px]"
          />
        );

      case "tel":
        return (
          <Input
            {...commonProps}
            type="tel"
            onChange={(e) => handleFieldChange(itemIndex, field.name, e.target.value)}
            className="min-w-[100px]"
          />
        );

      default: // text
        return (
          <Input
            {...commonProps}
            type="text"
            onChange={(e) => handleFieldChange(itemIndex, field.name, e.target.value)}
            maxLength={field.validation?.maxLength}
            className="min-w-[80px]"
          />
        );
    }
  };

  // 카테고리 매핑 로드 (카테고리 필드 + readonly 필드에 대해 자동 로드)
  // 테이블 리스트와 동일한 API 사용: /table-categories/{tableName}/{columnName}/values
  useEffect(() => {
    // 카테고리 타입 필드 + readonly 필드 (조인된 테이블에서 온 데이터일 가능성)
    const categoryFields = fields.filter((f) => f.type === "category");
    const readonlyFields = fields.filter((f) => f.displayMode === "readonly" && f.type === "text");

    if (categoryFields.length === 0 && readonlyFields.length === 0) return;

    const loadCategoryMappings = async () => {
      const apiClient = (await import("@/lib/api/client")).apiClient;

      // 1. 카테고리 타입 필드 매핑 로드
      for (const field of categoryFields) {
        const columnName = field.name;

        if (categoryMappings[columnName]) continue;

        try {
          const tableName = config.targetTable;
          if (!tableName) continue;

          console.log(`📡 [RepeaterInput] 카테고리 매핑 로드: ${tableName}/${columnName}`);

          const response = await apiClient.get(`/table-categories/${tableName}/${columnName}/values?includeInactive=true`);

          if (response.data.success && response.data.data && Array.isArray(response.data.data)) {
            const mapping: Record<string, { label: string; color: string }> = {};

            response.data.data.forEach((item: any) => {
              const key = String(item.valueCode);
              mapping[key] = {
                label: item.valueLabel || key,
                color: item.color || "#64748b",
              };
            });

            console.log(`✅ [RepeaterInput] 카테고리 매핑 로드 완료 [${columnName}]:`, mapping);

            setCategoryMappings((prev) => ({
              ...prev,
              [columnName]: mapping,
            }));
          }
        } catch (error) {
          console.error(`❌ [RepeaterInput] 카테고리 매핑 로드 실패 (${columnName}):`, error);
        }
      }

      // 2. 🆕 readonly 필드에 대해 조인된 테이블 (item_info)에서 카테고리 매핑 로드
      // material, division 등 조인된 테이블의 카테고리 필드
      const joinedTableFields = ["material", "division", "status", "currency_code"];
      const fieldsToLoadFromJoinedTable = readonlyFields.filter((f) => joinedTableFields.includes(f.name));

      if (fieldsToLoadFromJoinedTable.length > 0) {
        // item_info 테이블에서 카테고리 매핑 로드
        const joinedTableName = "item_info";

        for (const field of fieldsToLoadFromJoinedTable) {
          const columnName = field.name;

          if (categoryMappings[columnName]) continue;

          try {
            console.log(`📡 [RepeaterInput] 조인 테이블 카테고리 매핑 로드: ${joinedTableName}/${columnName}`);

            const response = await apiClient.get(`/table-categories/${joinedTableName}/${columnName}/values?includeInactive=true`);

            if (response.data.success && response.data.data && Array.isArray(response.data.data)) {
              const mapping: Record<string, { label: string; color: string }> = {};

              response.data.data.forEach((item: any) => {
                const key = String(item.valueCode);
                mapping[key] = {
                  label: item.valueLabel || key,
                  color: item.color || "#64748b",
                };
              });

              console.log(`✅ [RepeaterInput] 조인 테이블 카테고리 매핑 로드 완료 [${columnName}]:`, mapping);

              setCategoryMappings((prev) => ({
                ...prev,
                [columnName]: mapping,
              }));
            }
          } catch (error) {
            // 카테고리가 없는 필드는 무시
            console.log(`ℹ️ [RepeaterInput] 조인 테이블 카테고리 없음 (${columnName})`);
          }
        }
      }
    };

    loadCategoryMappings();
  }, [fields, config.targetTable]);

  // 필드가 정의되지 않았을 때
  if (fields.length === 0) {
    return (
      <div className={cn("space-y-4", className)}>
        <div className="border-destructive/30 bg-destructive/5 flex flex-col items-center justify-center rounded-lg border-2 border-dashed p-8 text-center">
          <p className="text-destructive text-sm font-medium">필드가 정의되지 않았습니다</p>
          <p className="text-muted-foreground mt-2 text-xs">속성 패널에서 필드를 추가하세요.</p>
        </div>
      </div>
    );
  }

  // 빈 상태 렌더링
  if (items.length === 0) {
    return (
      <div className={cn("space-y-4", className)}>
        <div className="border-border bg-muted/30 flex flex-col items-center justify-center rounded-lg border-2 border-dashed p-8 text-center">
          <p className="text-muted-foreground mb-4 text-sm">{emptyMessage}</p>
          {!readonly && !disabled && items.length < maxItems && (
            <Button type="button" onClick={handleAddItem} size="sm">
              <Plus className="mr-2 h-4 w-4" />
              {addButtonText}
            </Button>
          )}
        </div>
      </div>
    );
  }

  // 카드 레이아웃일 때 필드 배치 (세로로 나열)
  const getFieldsLayoutClass = () => {
    return "space-y-3";
  };

  // 그리드/테이블 형식 렌더링
  const renderGridLayout = () => {
    // 하위 데이터 조회 설정이 있으면 연결 컬럼 찾기
    const linkColumn = subDataLookup?.lookup?.linkColumn;
    
    // hidden이 아닌 필드만 표시
    // isHidden이 true이거나 displayMode가 hidden인 필드는 제외 (하위 호환성 유지)
    const visibleFields = fields.filter((f) => !f.isHidden && f.displayMode !== "hidden");

    return (
      <div className="bg-card">
        <Table>
          <TableHeader>
            <TableRow className="bg-background">
              {showIndex && (
                <TableHead className="h-10 w-10 px-2.5 py-2 text-center text-sm font-semibold">#</TableHead>
              )}
              {allowReorder && (
                <TableHead className="h-10 w-10 px-2.5 py-2 text-center text-sm font-semibold"></TableHead>
              )}
              {visibleFields.map((field) => (
                <TableHead key={field.name} className="h-10 px-2.5 py-2 text-sm font-semibold">
                  {field.label}
                  {field.required && <span className="text-destructive ml-1">*</span>}
                </TableHead>
              ))}
              <TableHead className="h-10 w-14 px-2.5 py-2 text-center text-sm font-semibold">작업</TableHead>
            </TableRow>
          </TableHeader>
          <TableBody>
            {items.map((item, itemIndex) => {
              // 하위 데이터 조회용 연결 값
              const linkValue = linkColumn ? item[linkColumn] : null;

              return (
                <React.Fragment key={itemIndex}>
                  <TableRow
                    className={cn(
                      "bg-background hover:bg-muted/50 transition-colors",
                      draggedIndex === itemIndex && "opacity-50",
                    )}
                    draggable={allowReorder && !readonly && !disabled}
                    onDragStart={() => handleDragStart(itemIndex)}
                    onDragOver={(e) => handleDragOver(e, itemIndex)}
                    onDrop={(e) => handleDrop(e, itemIndex)}
                    onDragEnd={handleDragEnd}
                  >
                    {/* 인덱스 번호 */}
                    {showIndex && (
                      <TableCell className="h-12 px-2.5 py-2 text-center text-sm font-medium">{itemIndex + 1}</TableCell>
                    )}

                    {/* 드래그 핸들 */}
                    {allowReorder && !readonly && !disabled && (
                      <TableCell className="h-12 px-2.5 py-2 text-center">
                        <GripVertical className="text-muted-foreground h-4 w-4 cursor-move" />
                      </TableCell>
                    )}

                    {/* 필드들 (hidden 제외) */}
                    {visibleFields.map((field) => (
                      <TableCell key={field.name} className="h-12 px-2.5 py-2">
                        {renderField(field, itemIndex, item[field.name])}
                      </TableCell>
                    ))}

                    {/* 삭제 버튼 */}
                    <TableCell className="h-12 px-2.5 py-2 text-center">
                      {!readonly && !disabled && (
                        <Button
                          type="button"
                          variant="ghost"
                          size="icon"
                          onClick={() => handleRemoveItem(itemIndex)}
                          className="text-destructive hover:bg-destructive/10 hover:text-destructive h-8 w-8"
                          title="항목 제거"
                        >
                          <X className="h-4 w-4" />
                        </Button>
                      )}
                    </TableCell>
                  </TableRow>

                  {/* 하위 데이터 조회 패널 (인라인) */}
                  {subDataLookup?.enabled && linkValue && (
                    <TableRow className="bg-muted/50">
                      <TableCell
                        colSpan={
                          visibleFields.length + (showIndex ? 1 : 0) + (allowReorder && !readonly && !disabled ? 1 : 0) + 1
                        }
                        className="px-2.5 py-2"
                      >
                        <SubDataLookupPanel
                          config={subDataLookup}
                          linkValue={linkValue}
                          itemIndex={itemIndex}
                          onSelectionChange={(selectedItem, maxValue) =>
                            handleSubDataSelection(itemIndex, selectedItem, maxValue)
                          }
                          disabled={readonly || disabled}
                        />
                      </TableCell>
                    </TableRow>
                  )}
                </React.Fragment>
              );
            })}
          </TableBody>
        </Table>
      </div>
    );
  };

  // 카드 형식 렌더링 (기존 방식)
  const renderCardLayout = () => {
    // 하위 데이터 조회 설정이 있으면 연결 컬럼 찾기
    const linkColumn = subDataLookup?.lookup?.linkColumn;
    
    // hidden이 아닌 필드만 표시
    // isHidden이 true이거나 displayMode가 hidden인 필드는 제외 (하위 호환성 유지)
    const visibleFields = fields.filter((f) => !f.isHidden && f.displayMode !== "hidden");

    return (
      <>
        {items.map((item, itemIndex) => {
          const isCollapsed = collapsible && collapsedItems.has(itemIndex);
          // 하위 데이터 조회용 연결 값
          const linkValue = linkColumn ? item[linkColumn] : null;

          return (
            <Card
              key={itemIndex}
              className={cn(
                "relative transition-all",
                draggedIndex === itemIndex && "opacity-50",
                isCollapsed && "shadow-sm",
              )}
              draggable={allowReorder && !readonly && !disabled}
              onDragStart={() => handleDragStart(itemIndex)}
              onDragOver={(e) => handleDragOver(e, itemIndex)}
              onDrop={(e) => handleDrop(e, itemIndex)}
              onDragEnd={handleDragEnd}
            >
              <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-3">
                <div className="flex items-center gap-2">
                  {/* 드래그 핸들 */}
                  {allowReorder && !readonly && !disabled && (
                    <GripVertical className="text-muted-foreground h-4 w-4 flex-shrink-0 cursor-move" />
                  )}

                  {/* 인덱스 번호 */}
                  {showIndex && (
                    <CardTitle className="text-foreground text-sm font-semibold">항목 {itemIndex + 1}</CardTitle>
                  )}
                </div>

                <div className="flex items-center gap-2">
                  {/* 접기/펼치기 버튼 */}
                  {collapsible && (
                    <Button
                      type="button"
                      variant="ghost"
                      size="icon"
                      onClick={() => toggleCollapse(itemIndex)}
                      className="h-8 w-8"
                    >
                      {isCollapsed ? <ChevronDown className="h-4 w-4" /> : <ChevronUp className="h-4 w-4" />}
                    </Button>
                  )}

                  {/* 삭제 버튼 */}
                  {!readonly && !disabled && (
                    <Button
                      type="button"
                      variant="ghost"
                      size="icon"
                      onClick={() => handleRemoveItem(itemIndex)}
                      className="text-destructive hover:bg-destructive/10 hover:text-destructive h-8 w-8"
                      title="항목 제거"
                    >
                      <X className="h-4 w-4" />
                    </Button>
                  )}
                </div>
              </CardHeader>

              {!isCollapsed && (
                <CardContent>
                  <div className={getFieldsLayoutClass()}>
                    {visibleFields.map((field) => (
                      <div key={field.name} className="space-y-1" style={{ width: field.width }}>
                        <label className="text-foreground text-sm font-medium">
                          {field.label}
                          {field.required && <span className="text-destructive ml-1">*</span>}
                        </label>
                        {renderField(field, itemIndex, item[field.name])}
                      </div>
                    ))}
                  </div>

                  {/* 하위 데이터 조회 패널 (인라인) */}
                  {subDataLookup?.enabled && linkValue && (
                    <div className="mt-3 border-t pt-3">
                      <SubDataLookupPanel
                        config={subDataLookup}
                        linkValue={linkValue}
                        itemIndex={itemIndex}
                        onSelectionChange={(selectedItem, maxValue) =>
                          handleSubDataSelection(itemIndex, selectedItem, maxValue)
                        }
                        disabled={readonly || disabled}
                      />
                    </div>
                  )}
                </CardContent>
              )}

              {showDivider && itemIndex < items.length - 1 && <Separator className="mt-4" />}
            </Card>
          );
        })}
      </>
    );
  };

  return (
    <div className={cn("space-y-4", className)}>
      {/* 레이아웃에 따라 렌더링 방식 선택 (반응형 고려) */}
      {effectiveLayout === "grid" ? renderGridLayout() : renderCardLayout()}

      {/* 추가 버튼 */}
      {!readonly && !disabled && items.length < maxItems && (
        <Button type="button" variant="outline" size="sm" onClick={handleAddItem} className="w-full border-dashed">
          <Plus className="mr-2 h-4 w-4" />
          {addButtonText}
        </Button>
      )}

      {/* 제한 안내 */}
      <div className="text-muted-foreground flex justify-between text-xs">
        <span>현재: {items.length}개 항목</span>
        <span>
          (최소: {minItems}, 최대: {maxItems})
        </span>
      </div>
    </div>
  );
};

RepeaterInput.displayName = "RepeaterInput";
