"use client";

import { getAdaptiveLabelColor } from "@/lib/utils/darkModeColor";

/**
 * V2Input
 *
 * 통합 입력 컴포넌트
 * - text: 텍스트 입력
 * - number: 숫자 입력
 * - password: 비밀번호 입력
 * - slider: 슬라이더 입력
 * - color: 색상 선택
 * - button: 버튼 (입력이 아닌 액션)
 */

import React, { forwardRef, useCallback, useEffect, useMemo, useRef, useState } from "react";
import { Input } from "@/components/ui/input";
import { Slider } from "@/components/ui/slider";
import { Label } from "@/components/ui/label";
import { formatNumber as centralFormatNumber } from "@/lib/formatting";
import { cn } from "@/lib/utils";
import { V2InputProps, V2InputConfig, V2InputFormat } from "@/types/v2-components";
import { AutoGenerationUtils } from "@/lib/utils/autoGeneration";
import { AutoGenerationConfig } from "@/types/screen";
import { previewNumberingCode } from "@/lib/api/numberingRule";

// 형식별 입력 마스크 및 검증 패턴
const FORMAT_PATTERNS: Record<V2InputFormat, { pattern: RegExp; placeholder: string; errorMessage: string }> = {
  none: { pattern: /.*/, placeholder: "", errorMessage: "" },
  email: {
    pattern: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
    placeholder: "example@email.com",
    errorMessage: "올바른 이메일 형식이 아닙니다",
  },
  tel: {
    pattern:
      /^(01[016789]|02|0[3-7]1|0[3-6][2-5]|050[2-8]|070|080)-\d{3,4}-\d{4}$|^(15|16|18)\d{2}-\d{4}$/,
    placeholder: "010-1234-5678",
    errorMessage: "올바른 전화번호 형식이 아닙니다",
  },
  url: {
    pattern: /^https?:\/\/.+/,
    placeholder: "https://example.com",
    errorMessage: "올바른 URL 형식이 아닙니다 (https://로 시작)",
  },
  currency: { pattern: /^[\d,]+$/, placeholder: "1,000,000", errorMessage: "숫자만 입력 가능합니다" },
  biz_no: {
    pattern: /^\d{3}-\d{2}-\d{5}$/,
    placeholder: "123-45-67890",
    errorMessage: "올바른 사업자번호 형식이 아닙니다",
  },
};

// 형식 검증 함수 (외부에서도 사용 가능)
export function validateInputFormat(value: string, format: V2InputFormat): { isValid: boolean; errorMessage: string } {
  if (!value || value.trim() === "" || format === "none") {
    return { isValid: true, errorMessage: "" };
  }
  const formatConfig = FORMAT_PATTERNS[format];
  if (!formatConfig) return { isValid: true, errorMessage: "" };
  const isValid = formatConfig.pattern.test(value);
  return { isValid, errorMessage: isValid ? "" : formatConfig.errorMessage };
}

// 통화 형식 변환 (공통 formatNumber 사용)
function formatCurrency(value: string | number): string {
  const num = typeof value === "string" ? parseFloat(value.replace(/,/g, "")) : value;
  if (isNaN(num)) return "";
  return centralFormatNumber(num);
}

// 사업자번호 형식 변환
function formatBizNo(value: string): string {
  const digits = value.replace(/\D/g, "");
  if (digits.length <= 3) return digits;
  if (digits.length <= 5) return `${digits.slice(0, 3)}-${digits.slice(3)}`;
  return `${digits.slice(0, 3)}-${digits.slice(3, 5)}-${digits.slice(5, 10)}`;
}

// 전화번호 형식 변환
function formatTel(value: string): string {
  const digits = value.replace(/\D/g, "");
  if (digits.length === 0) return "";

  // 대표번호: 15xx, 16xx, 18xx → 4-4
  if (/^(15|16|18)/.test(digits)) {
    if (digits.length <= 4) return digits;
    return `${digits.slice(0, 4)}-${digits.slice(4, 8)}`;
  }

  // 서울: 02 → 9자리 2-3-4, 10자리 2-4-4
  if (digits.startsWith("02")) {
    if (digits.length <= 2) return digits;
    if (digits.length <= 5) return `${digits.slice(0, 2)}-${digits.slice(2)}`;
    const mid = digits.length >= 10 ? 4 : 3;
    return `${digits.slice(0, 2)}-${digits.slice(2, 2 + mid)}-${digits.slice(2 + mid, 2 + mid + 4)}`;
  }

  // 안심번호: 050x → 4-4-4
  if (/^050[2-8]/.test(digits)) {
    if (digits.length <= 4) return digits;
    if (digits.length <= 8) return `${digits.slice(0, 4)}-${digits.slice(4)}`;
    if (digits.length <= 12) return `${digits.slice(0, 4)}-${digits.slice(4, 8)}-${digits.slice(8)}`;
    return `${digits.slice(0, 4)}-${digits.slice(4, 8)}-${digits.slice(8, 12)}`;
  }

  // 나머지 (010, 031, 070, 080 등)
  if (digits.length <= 3) return digits;
  if (digits.length <= 7) return `${digits.slice(0, 3)}-${digits.slice(3)}`;
  if (digits.length === 10) return `${digits.slice(0, 3)}-${digits.slice(3, 6)}-${digits.slice(6)}`;
  if (digits.length <= 11) return `${digits.slice(0, 3)}-${digits.slice(3, 7)}-${digits.slice(7)}`;
  return `${digits.slice(0, 3)}-${digits.slice(3, 7)}-${digits.slice(7, 11)}`;
}

/**
 * 텍스트 입력 컴포넌트
 */
const TextInput = forwardRef<
  HTMLInputElement,
  {
    value?: string | number;
    onChange?: (value: string) => void;
    format?: V2InputFormat;
    mask?: string;
    placeholder?: string;
    readonly?: boolean;
    disabled?: boolean;
    className?: string;
    columnName?: string;
    inputStyle?: React.CSSProperties;
  }
>(({ value, onChange, format = "none", placeholder, readonly, disabled, className, columnName, inputStyle }, ref) => {
  // 검증 상태
  const [hasBlurred, setHasBlurred] = useState(false);
  const [validationError, setValidationError] = useState<string>("");

  // 커서 위치 보존을 위한 내부 ref
  const innerRef = useRef<HTMLInputElement>(null);
  const combinedRef = (node: HTMLInputElement | null) => {
    (innerRef as React.MutableRefObject<HTMLInputElement | null>).current = node;
    if (typeof ref === "function") ref(node);
    else if (ref) (ref as React.MutableRefObject<HTMLInputElement | null>).current = node;
  };

  // 형식에 따른 값 포맷팅
  const formatValue = useCallback(
    (val: string): string => {
      switch (format) {
        case "currency":
          return formatCurrency(val);
        case "biz_no":
          return formatBizNo(val);
        case "tel":
          return formatTel(val);
        default:
          return val;
      }
    },
    [format],
  );

  const handleChange = useCallback(
    (e: React.ChangeEvent<HTMLInputElement>) => {
      const input = e.target;
      const cursorPos = input.selectionStart ?? 0;
      let newValue = e.target.value;
      const oldValue = input.value;

      const needsCursorFix = format === "biz_no" || format === "tel" || format === "currency";

      // 형식에 따른 자동 포맷팅
      if (format === "currency") {
        newValue = newValue.replace(/[^\d,]/g, "");
        newValue = formatCurrency(newValue);
      } else if (format === "biz_no") {
        newValue = formatBizNo(newValue);
      } else if (format === "tel") {
        newValue = formatTel(newValue);
      }

      // 포맷팅 후 커서 위치 보정 (하이픈/쉼표 개수 차이 기반)
      if (needsCursorFix) {
        const separator = format === "currency" ? /,/g : /-/g;
        const oldSeps = (oldValue.slice(0, cursorPos).match(separator) || []).length;
        const newSeps = (newValue.slice(0, cursorPos).match(separator) || []).length;
        const adjustedCursor = Math.min(cursorPos + (newSeps - oldSeps), newValue.length);

        requestAnimationFrame(() => {
          if (innerRef.current) {
            innerRef.current.setSelectionRange(adjustedCursor, adjustedCursor);
          }
        });
      }

      // 입력 중 에러 표시 해제 (입력 중에는 관대하게)
      if (hasBlurred && validationError) {
        const { isValid } = validateInputFormat(newValue, format);
        if (isValid) {
          setValidationError("");
        }
      }

      onChange?.(newValue);
    },
    [format, onChange, hasBlurred, validationError],
  );

  // blur 시 형식 검증
  const handleBlur = useCallback(() => {
    setHasBlurred(true);
    const currentValue = value !== undefined && value !== null ? String(value) : "";
    if (currentValue && format !== "none") {
      const { isValid, errorMessage } = validateInputFormat(currentValue, format);
      setValidationError(isValid ? "" : errorMessage);
    } else {
      setValidationError("");
    }
  }, [value, format]);

  // 값 변경 시 검증 상태 업데이트
  useEffect(() => {
    if (hasBlurred) {
      const currentValue = value !== undefined && value !== null ? String(value) : "";
      if (currentValue && format !== "none") {
        const { isValid, errorMessage } = validateInputFormat(currentValue, format);
        setValidationError(isValid ? "" : errorMessage);
      } else {
        setValidationError("");
      }
    }
  }, [value, format, hasBlurred]);

  // 글로벌 폼 검증 이벤트 리스너 (저장 시 호출)
  useEffect(() => {
    if (format === "none" || !columnName) return;

    const handleValidateForm = (event: CustomEvent) => {
      const currentValue = value !== undefined && value !== null ? String(value) : "";
      if (currentValue) {
        const { isValid, errorMessage } = validateInputFormat(currentValue, format);
        if (!isValid) {
          setHasBlurred(true);
          setValidationError(errorMessage);
          // 검증 결과를 이벤트에 기록
          if (event.detail?.errors) {
            event.detail.errors.push({
              columnName,
              message: errorMessage,
            });
          }
        }
      }
    };

    window.addEventListener("validateFormInputs", handleValidateForm as EventListener);
    return () => {
      window.removeEventListener("validateFormInputs", handleValidateForm as EventListener);
    };
  }, [format, value, columnName]);

  const displayValue = useMemo(() => {
    if (value === undefined || value === null) return "";
    return formatValue(String(value));
  }, [value, formatValue]);

  const inputPlaceholder = placeholder || FORMAT_PATTERNS[format].placeholder;
  const hasError = hasBlurred && !!validationError;

  return (
    <div className="relative h-full w-full">
      <Input
        ref={combinedRef}
        type="text"
        value={displayValue}
        onChange={handleChange}
        onBlur={handleBlur}
        placeholder={inputPlaceholder}
        readOnly={readonly}
        disabled={disabled}
        className={cn("h-full w-full", hasError && "border-destructive focus-visible:ring-destructive", className)}
        style={inputStyle}
      />
      {hasError && <p className="text-destructive absolute top-full left-0 mt-0.5 text-[11px]">{validationError}</p>}
    </div>
  );
});
TextInput.displayName = "TextInput";

/**
 * 숫자를 콤마 포맷 문자열로 변환 (입력 중 실시간 표시용)
 * 소수점 입력 중인 경우(끝이 "."이거나 ".0" 등)를 보존
 */
function toCommaDisplay(raw: string): string {
  if (raw === "" || raw === "-") return raw;
  const negative = raw.startsWith("-");
  const abs = negative ? raw.slice(1) : raw;
  const dotIdx = abs.indexOf(".");
  const intPart = dotIdx >= 0 ? abs.slice(0, dotIdx) : abs;
  const decPart = dotIdx >= 0 ? abs.slice(dotIdx) : "";
  const formatted = intPart.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
  return (negative ? "-" : "") + formatted + decPart;
}

/**
 * 숫자 입력 컴포넌트 - 입력 중에도 실시간 천단위 콤마 표시
 */
const NumberInput = forwardRef<
  HTMLInputElement,
  {
    value?: number;
    onChange?: (value: number | undefined) => void;
    min?: number;
    max?: number;
    step?: number;
    placeholder?: string;
    readonly?: boolean;
    disabled?: boolean;
    className?: string;
    inputStyle?: React.CSSProperties;
  }
>(({ value, onChange, min, max, placeholder, readonly, disabled, className, inputStyle }, ref) => {
  const innerRef = useRef<HTMLInputElement>(null);
  const combinedRef = (node: HTMLInputElement | null) => {
    (innerRef as React.MutableRefObject<HTMLInputElement | null>).current = node;
    if (typeof ref === "function") ref(node);
    else if (ref) (ref as React.MutableRefObject<HTMLInputElement | null>).current = node;
  };

  // 콤마 포함된 표시 문자열을 내부 상태로 관리
  const [displayValue, setDisplayValue] = useState(() => {
    if (value === undefined || value === null) return "";
    return centralFormatNumber(value);
  });

  // 외부 value가 변경되면 표시 값 동기화 (포커스 아닐 때만)
  const isFocusedRef = useRef(false);
  useEffect(() => {
    if (isFocusedRef.current) return;
    if (value === undefined || value === null) {
      setDisplayValue("");
    } else {
      setDisplayValue(centralFormatNumber(value));
    }
  }, [value]);

  const handleChange = useCallback(
    (e: React.ChangeEvent<HTMLInputElement>) => {
      const input = e.target;
      const cursorPos = input.selectionStart ?? 0;
      const oldVal = displayValue;
      const rawInput = e.target.value;

      // 콤마 제거하여 순수 숫자 문자열 추출
      const stripped = rawInput.replace(/,/g, "");

      // 빈 값 처리
      if (stripped === "" || stripped === "-") {
        setDisplayValue(stripped);
        onChange?.(undefined);
        return;
      }

      // 숫자 + 소수점만 허용 (입력 중 "123." 같은 중간 상태도 허용)
      if (!/^-?\d*\.?\d*$/.test(stripped)) return;

      // 새 콤마 포맷 생성
      const newDisplay = toCommaDisplay(stripped);
      setDisplayValue(newDisplay);

      // 콤마 개수 차이로 커서 위치 보정
      const oldCommas = (oldVal.slice(0, cursorPos).match(/,/g) || []).length;
      const newCommas = (newDisplay.slice(0, cursorPos).match(/,/g) || []).length;
      const adjustedCursor = cursorPos + (newCommas - oldCommas);

      requestAnimationFrame(() => {
        if (innerRef.current) {
          innerRef.current.setSelectionRange(adjustedCursor, adjustedCursor);
        }
      });

      // 실제 숫자 값 전달 (소수점 입력 중이면 아직 전달하지 않음)
      if (stripped.endsWith(".") || stripped.endsWith("-")) return;
      let num = parseFloat(stripped);
      if (isNaN(num)) return;

      if (min !== undefined && num < min) num = min;
      if (max !== undefined && num > max) num = max;

      onChange?.(num);
    },
    [min, max, onChange, displayValue],
  );

  const handleFocus = useCallback(() => {
    isFocusedRef.current = true;
  }, []);

  const handleBlur = useCallback(() => {
    isFocusedRef.current = false;
    // 블러 시 최종 포맷 정리
    const stripped = displayValue.replace(/,/g, "");
    if (stripped === "" || stripped === "-" || stripped === ".") {
      setDisplayValue("");
      onChange?.(undefined);
      return;
    }
    const num = parseFloat(stripped);
    if (!isNaN(num)) {
      setDisplayValue(centralFormatNumber(num));
    }
  }, [displayValue, onChange]);

  return (
    <Input
      ref={combinedRef}
      type="text"
      inputMode="decimal"
      value={displayValue}
      onChange={handleChange}
      onFocus={handleFocus}
      onBlur={handleBlur}
      placeholder={placeholder || "숫자 입력"}
      readOnly={readonly}
      disabled={disabled}
      className={cn("h-full w-full", className)}
      style={{ ...inputStyle, textAlign: "right" }}
    />
  );
});
NumberInput.displayName = "NumberInput";

/**
 * 비밀번호 입력 컴포넌트
 */
const PasswordInput = forwardRef<
  HTMLInputElement,
  {
    value?: string;
    onChange?: (value: string) => void;
    placeholder?: string;
    readonly?: boolean;
    disabled?: boolean;
    className?: string;
    inputStyle?: React.CSSProperties;
  }
>(({ value, onChange, placeholder, readonly, disabled, className, inputStyle }, ref) => {
  const [showPassword, setShowPassword] = useState(false);

  return (
    <div className="relative">
      <Input
        ref={ref}
        type={showPassword ? "text" : "password"}
        value={value ?? ""}
        onChange={(e) => onChange?.(e.target.value)}
        placeholder={placeholder || "비밀번호 입력"}
        readOnly={readonly}
        disabled={disabled}
        className={cn("h-full w-full pr-10", className)}
        style={inputStyle}
      />
      <button
        type="button"
        onClick={() => setShowPassword(!showPassword)}
        className="text-muted-foreground hover:text-foreground absolute top-1/2 right-2 -translate-y-1/2 text-xs"
      >
        {showPassword ? "숨김" : "보기"}
      </button>
    </div>
  );
});
PasswordInput.displayName = "PasswordInput";

/**
 * 슬라이더 입력 컴포넌트
 */
const SliderInput = forwardRef<
  HTMLDivElement,
  {
    value?: number;
    onChange?: (value: number) => void;
    min?: number;
    max?: number;
    step?: number;
    disabled?: boolean;
    className?: string;
  }
>(({ value, onChange, min = 0, max = 100, step = 1, disabled, className }, ref) => {
  return (
    <div ref={ref} className={cn("flex items-center gap-4", className)}>
      <Slider
        value={[value ?? min]}
        onValueChange={(values) => onChange?.(values[0])}
        min={min}
        max={max}
        step={step}
        disabled={disabled}
        className="flex-1"
      />
      <span className="w-12 text-right text-sm font-medium">{value ?? min}</span>
    </div>
  );
});
SliderInput.displayName = "SliderInput";

/**
 * 색상 선택 컴포넌트
 */
const ColorInput = forwardRef<
  HTMLInputElement,
  {
    value?: string;
    onChange?: (value: string) => void;
    disabled?: boolean;
    className?: string;
  }
>(({ value, onChange, disabled, className }, ref) => {
  return (
    <div className={cn("flex items-center gap-2", className)}>
      <Input
        ref={ref}
        type="color"
        value={value || "#000000"}
        onChange={(e) => onChange?.(e.target.value)}
        disabled={disabled}
        className="h-full w-12 cursor-pointer p-1"
      />
      <Input
        type="text"
        value={value || "#000000"}
        onChange={(e) => onChange?.(e.target.value)}
        disabled={disabled}
        className="h-full flex-1 uppercase"
        maxLength={7}
      />
    </div>
  );
});
ColorInput.displayName = "ColorInput";

/**
 * 여러 줄 텍스트 입력 컴포넌트
 */
const TextareaInput = forwardRef<
  HTMLTextAreaElement,
  {
    value?: string;
    onChange?: (value: string) => void;
    placeholder?: string;
    rows?: number;
    readonly?: boolean;
    disabled?: boolean;
    className?: string;
    inputStyle?: React.CSSProperties;
  }
>(({ value = "", onChange, placeholder, rows = 3, readonly, disabled, className, inputStyle }, ref) => {
  return (
    <textarea
      ref={ref}
      value={value}
      onChange={(e) => onChange?.(e.target.value)}
      placeholder={placeholder}
      rows={rows}
      readOnly={readonly}
      disabled={disabled}
      className={cn(
        "border-input placeholder:text-muted-foreground focus-visible:ring-ring flex h-full w-full rounded-md border bg-transparent px-3 py-2 text-sm shadow-sm focus-visible:ring-1 focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50",
        className,
      )}
      style={inputStyle}
    />
  );
});
TextareaInput.displayName = "TextareaInput";

/**
 * 메인 V2Input 컴포넌트
 */
export const V2Input = forwardRef<HTMLDivElement, V2InputProps>((props, ref) => {
  const { id, label, required, readonly, disabled, style, size, config: configProp, value, onChange } = props;

  // formData 추출 (채번규칙 날짜 컬럼 기준 생성 시 사용)
  const formData = (props as any).formData || {};
  const columnName = (props as any).columnName;
  // onFormDataChange 추출 (채번 규칙 ID를 formData에 저장하기 위함)
  const onFormDataChange = (props as any).onFormDataChange;

  // config가 없으면 기본값 사용
  const config = (configProp || { type: "text" }) as V2InputConfig & {
    inputType?: string;
    rows?: number;
    autoGeneration?: AutoGenerationConfig;
  };

  // 자동생성 설정 추출
  const autoGeneration: AutoGenerationConfig = (props as any).autoGeneration ||
    (config as any).autoGeneration || {
      type: "none",
      enabled: false,
    };

  // 자동생성 상태 관리
  const [autoGeneratedValue, setAutoGeneratedValue] = useState<string | null>(null);
  const isGeneratingRef = useRef(false);
  const hasGeneratedRef = useRef(false);
  const lastFormDataRef = useRef<string>(""); // 마지막 formData 추적 (채번 규칙용)

  // 채번 타입 자동생성 상태
  const [isGeneratingNumbering, setIsGeneratingNumbering] = useState(false);
  const hasGeneratedNumberingRef = useRef(false);

  // formData를 ref로 관리하여 closure 문제 해결 (채번 코드 생성 시 최신 값 사용)
  const formDataRef = useRef(formData);
  formDataRef.current = formData;

  // tableName 추출 (여러 소스에서 확인)
  // 1. props에서 직접 전달받은 값
  // 2. config에서 설정된 값
  // 3. 컴포넌트 overrides에서 설정된 값 (V2 레이아웃)
  // 4. screenInfo에서 화면 테이블명
  const tableName =
    (props as any).tableName ||
    (config as any).tableName ||
    (props as any).component?.tableName ||
    (props as any).component?.overrides?.tableName ||
    (props as any).screenInfo?.tableName;

  // 수정 모드 여부 확인
  const originalData = (props as any).originalData || (props as any)._originalData;
  const isEditMode = originalData && Object.keys(originalData).length > 0;

  // 채번 규칙인 경우 formData 변경 감지 (자기 자신 필드 제외)
  const formDataForNumbering = useMemo(() => {
    if (autoGeneration.type !== "numbering_rule") return "";
    // 자기 자신의 값은 제외 (무한 루프 방지)
    const { [columnName]: _, ...rest } = formData;
    return JSON.stringify(rest);
  }, [autoGeneration.type, formData, columnName]);

  // 자동생성 로직
  useEffect(() => {
    const generateValue = async () => {
      // 자동생성 비활성화 또는 생성 중
      if (!autoGeneration.enabled || isGeneratingRef.current) {
        return;
      }

      // 수정 모드에서는 자동생성 안함
      if (isEditMode) {
        return;
      }

      // 채번 규칙인 경우: formData가 변경되었는지 확인
      const isNumberingRule = autoGeneration.type === "numbering_rule";
      const formDataChanged =
        isNumberingRule && formDataForNumbering !== lastFormDataRef.current && lastFormDataRef.current !== "";

      // 이미 생성되었고, formData 변경이 아닌 경우 스킵
      if (hasGeneratedRef.current && !formDataChanged) {
        return;
      }

      // 첫 생성 시: 값이 이미 있으면 스킵 (formData 변경 시에는 강제 재생성)
      if (!formDataChanged && value !== undefined && value !== null && value !== "") {
        return;
      }

      isGeneratingRef.current = true;

      try {
        // formData를 전달하여 날짜 컬럼 기준 생성 지원
        const generatedValue = await AutoGenerationUtils.generateValue(autoGeneration, columnName, formData);

        if (generatedValue !== null && generatedValue !== undefined) {
          setAutoGeneratedValue(generatedValue);
          onChange?.(generatedValue);
          hasGeneratedRef.current = true;

          // formData 기록
          if (isNumberingRule) {
            lastFormDataRef.current = formDataForNumbering;
          }
        }
      } catch (error) {
        console.error("자동생성 실패:", error);
      } finally {
        isGeneratingRef.current = false;
      }
    };

    generateValue();
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [autoGeneration.enabled, autoGeneration.type, isEditMode, formDataForNumbering]);

  // 채번 규칙 ID 캐싱
  const numberingRuleIdRef = useRef<string | null>(null);
  const lastCategoryValuesRef = useRef<string>("");
  // 사용자가 직접 입력 중인지 추적 (재생성 방지)
  const userEditedNumberingRef = useRef<boolean>(false);
  // 원래 수동 입력 부분이 있었는지 추적 (____가 있었으면 계속 편집 가능)
  const hadManualPartRef = useRef<boolean>(false);
  // 채번 템플릿 저장 (____가 포함된 원본 형태)
  const numberingTemplateRef = useRef<string>("");
  // 사용자가 수동 입력한 값 저장
  const [manualInputValue, setManualInputValue] = useState<string>("");

  // formData에서 카테고리 관련 값 추출 (채번 파트에서 카테고리 사용 시)
  // 채번 필드 자체의 값은 제외해야 함 (무한 루프 방지)
  // inputType을 여러 소스에서 확인
  const propsInputType = (props as any).inputType;
  const categoryValuesForNumbering = useMemo(() => {
    const inputType = propsInputType || config.inputType || config.type || "text";
    if (inputType !== "numbering") return "";
    // formData에서 category 타입 필드 값들을 추출 (채번 필드 자체는 제외)
    const categoryFields: Record<string, string> = {};
    for (const [key, val] of Object.entries(formData)) {
      // 현재 채번 필드(columnName)는 제외
      if (key === columnName) continue;
      if (typeof val === "string" && val) {
        categoryFields[key] = val;
      }
    }
    return JSON.stringify(categoryFields);
  }, [propsInputType, config.inputType, config.type, formData, columnName]);

  // 채번 타입 자동생성 로직 (테이블 관리에서 설정된 numberingRuleId 사용)
  useEffect(() => {
    const generateNumberingCode = async () => {
      // inputType을 여러 소스에서 확인 (props에서 직접 전달받거나 config에서)
      const inputType = (props as any).inputType || config.inputType || config.type || "text";

      // numbering 타입이 아니면 스킵
      if (inputType !== "numbering") {
        return;
      }

      // 수정 모드에서는 자동생성 안함
      if (isEditMode) {
        return;
      }

      // 생성 중이면 스킵
      if (isGeneratingNumbering) {
        return;
      }

      // 사용자가 직접 편집한 경우 재생성 안함 (단, 카테고리 변경 시에는 재생성)
      const categoryChanged = categoryValuesForNumbering !== lastCategoryValuesRef.current;
      if (userEditedNumberingRef.current && !categoryChanged) {
        return;
      }

      // 이미 생성되었고 카테고리 값이 변경되지 않았으면 스킵
      if (hasGeneratedNumberingRef.current && !categoryChanged) {
        return;
      }

      // 첫 생성 시: 값이 이미 있고 카테고리 변경이 아니면 스킵
      if (!categoryChanged && value !== undefined && value !== null && value !== "") {
        return;
      }

      // tableName과 columnName이 필요
      if (!tableName || !columnName) {
        console.warn("채번 타입: tableName 또는 columnName이 없습니다", { tableName, columnName });
        return;
      }

      setIsGeneratingNumbering(true);

      try {
        // 채번 규칙 ID 캐싱 (한 번만 조회)
        if (!numberingRuleIdRef.current) {
          // table_name + column_name 기반으로 채번 규칙 조회
          try {
            const { apiClient } = await import("@/lib/api/client");
            const ruleResponse = await apiClient.get(`/numbering-rules/by-column/${tableName}/${columnName}`);
            if (ruleResponse.data?.success && ruleResponse.data?.data?.ruleId) {
              numberingRuleIdRef.current = ruleResponse.data.data.ruleId;

              if (onFormDataChange && columnName) {
                onFormDataChange(`${columnName}_numberingRuleId`, ruleResponse.data.data.ruleId);
              }
            }
          } catch {
            // by-column 조회 실패 시 detailSettings fallback
            try {
              const { getTableColumns } = await import("@/lib/api/tableManagement");
              const columnsResponse = await getTableColumns(tableName);
              if (columnsResponse.success && columnsResponse.data) {
                const columns = columnsResponse.data.columns || columnsResponse.data;
                const targetColumn = columns.find((col: { columnName: string }) => col.columnName === columnName);
                if (targetColumn?.detailSettings) {
                  const parsed =
                    typeof targetColumn.detailSettings === "string"
                      ? JSON.parse(targetColumn.detailSettings)
                      : targetColumn.detailSettings;
                  numberingRuleIdRef.current = parsed.numberingRuleId || null;
                }
              }
            } catch {
              /* ignore */
            }
          }
        }

        const numberingRuleId = numberingRuleIdRef.current;

        if (!numberingRuleId) {
          console.warn("채번 규칙을 찾을 수 없습니다. 옵션설정 > 채번설정에서 규칙을 생성하세요.", {
            tableName,
            columnName,
          });
          return;
        }

        // 채번 코드 생성 (formDataRef.current 사용하여 최신 formData 전달)
        const currentFormData = formDataRef.current;
        const previewResponse = await previewNumberingCode(numberingRuleId, currentFormData, manualInputValue || undefined);

        if (previewResponse.success && previewResponse.data?.generatedCode) {
          const generatedCode = previewResponse.data.generatedCode;
          hasGeneratedNumberingRef.current = true;
          lastCategoryValuesRef.current = categoryValuesForNumbering;

          // 수동 입력 부분이 있는 경우
          if (generatedCode.includes("____")) {
            hadManualPartRef.current = true;
            const oldTemplate = numberingTemplateRef.current;
            numberingTemplateRef.current = generatedCode;

            // 카테고리 변경으로 템플릿이 바뀌었을 때 기존 사용자 입력값 유지
            if (oldTemplate && oldTemplate !== generatedCode) {
              // 템플릿이 변경되었지만 사용자 입력값은 유지
              const templateParts = generatedCode.split("____");
              const templatePrefix = templateParts[0] || "";
              const templateSuffix = templateParts.length > 1 ? templateParts.slice(1).join("") : "";

              // 기존 manualInputValue를 사용하여 새 값 조합 (상태는 유지)
              // 참고: setManualInputValue는 호출하지 않음 (기존 값 유지)
              const finalValue = templatePrefix + (userEditedNumberingRef.current ? "" : "") + templateSuffix;

              // 사용자가 입력한 적이 없으면 템플릿 그대로
              if (!userEditedNumberingRef.current) {
                setAutoGeneratedValue(generatedCode);
                onChange?.(generatedCode);
              }
              // 사용자가 입력한 적이 있으면 입력값 유지하며 템플릿만 변경
              // (manualInputValue 상태는 유지되므로 UI에서 자동 반영)
            } else {
              // 첫 생성
              setAutoGeneratedValue(generatedCode);
              onChange?.(generatedCode);
              userEditedNumberingRef.current = false;
            }
          } else {
            // 수동 입력 부분 없음
            setAutoGeneratedValue(generatedCode);
            onChange?.(generatedCode);
            userEditedNumberingRef.current = false;
          }

          // 채번 코드 생성 성공
        } else {
          console.warn("채번 코드 생성 실패:", previewResponse);
        }
      } catch (error) {
        console.error("채번 자동생성 오류:", error);
      } finally {
        setIsGeneratingNumbering(false);
      }
    };

    generateNumberingCode();
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [tableName, columnName, isEditMode, categoryValuesForNumbering]);

  // 🆕 beforeFormSave 이벤트 리스너 - 저장 직전에 현재 조합된 값을 formData에 주입
  useEffect(() => {
    const inputType = propsInputType || config.inputType || config.type || "text";
    if (inputType !== "numbering" || !columnName) return;

    const handleBeforeFormSave = (event: CustomEvent) => {
      const template = numberingTemplateRef.current;
      if (!template || !template.includes("____")) return;

      // 템플릿에서 prefix와 suffix 추출
      const templateParts = template.split("____");
      const templatePrefix = templateParts[0] || "";
      const templateSuffix = templateParts.length > 1 ? templateParts.slice(1).join("") : "";

      // 현재 조합된 값 생성
      const currentValue = templatePrefix + manualInputValue + templateSuffix;

      // formData에 직접 주입
      if (event.detail?.formData && columnName) {
        event.detail.formData[columnName] = currentValue;
      }
    };

    window.addEventListener("beforeFormSave", handleBeforeFormSave as EventListener);
    return () => {
      window.removeEventListener("beforeFormSave", handleBeforeFormSave as EventListener);
    };
  }, [columnName, manualInputValue, propsInputType, config.inputType, config.type]);

  // 수동 입력값 변경 시 디바운스로 순번 미리보기 갱신
  useEffect(() => {
    const inputType = propsInputType || config.inputType || config.type || "text";
    if (inputType !== "numbering") return;
    if (!numberingTemplateRef.current?.includes("____")) return;

    const ruleId = numberingRuleIdRef.current;
    if (!ruleId) return;

    // 사용자가 한 번도 입력하지 않은 초기 상태면 스킵
    if (!userEditedNumberingRef.current) return;

    const debounceTimer = setTimeout(async () => {
      try {
        const currentFormData = formDataRef.current;
        const resp = await previewNumberingCode(ruleId, currentFormData, manualInputValue || undefined);

        if (resp.success && resp.data?.generatedCode) {
          const newTemplate = resp.data.generatedCode;
          if (newTemplate.includes("____")) {
            numberingTemplateRef.current = newTemplate;

            const parts = newTemplate.split("____");
            const prefix = parts[0] || "";
            const suffix = parts.length > 1 ? parts.slice(1).join("") : "";
            const combined = prefix + manualInputValue + suffix;

            setAutoGeneratedValue(combined);
            onChange?.(combined);
            if (onFormDataChange && columnName) {
              onFormDataChange(columnName, combined);
            }
          }
        }
      } catch {
        /* 미리보기 실패 시 기존 suffix 유지 */
      }
    }, 300);

    return () => clearTimeout(debounceTimer);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [manualInputValue]);

  // 실제 표시할 값 (자동생성 값 또는 props value)
  const displayValue = autoGeneratedValue ?? value;

  // 조건부 렌더링 체크
  // TODO: conditional 처리 로직 추가

  // 타입별 입력 컴포넌트 렌더링
  const renderInput = () => {
    const inputType = propsInputType || config.inputType || config.type || "text";
    switch (inputType) {
      case "text":
        return (
          <TextInput
            value={displayValue}
            onChange={(v) => {
              setAutoGeneratedValue(null); // 사용자 입력 시 자동생성 값 초기화
              onChange?.(v);
            }}
            format={config.format}
            mask={config.mask}
            placeholder={config.placeholder}
            readonly={readonly || (autoGeneration.enabled && hasGeneratedRef.current)}
            disabled={disabled}
            columnName={columnName}
            inputStyle={inputTextStyle}
          />
        );

      case "number":
        // DB에서 문자열("325")로 반환되는 경우도 숫자로 변환하여 표시
        const numValue =
          typeof displayValue === "number"
            ? displayValue
            : displayValue !== undefined && displayValue !== null && displayValue !== "" && !isNaN(Number(displayValue))
              ? Number(displayValue)
              : undefined;
        return (
          <NumberInput
            value={numValue}
            onChange={(v) => {
              setAutoGeneratedValue(null);
              onChange?.(v ?? 0);
            }}
            min={config.min}
            max={config.max}
            step={config.step}
            placeholder={config.placeholder}
            readonly={readonly}
            disabled={disabled}
            inputStyle={inputTextStyle}
          />
        );

      case "password":
        return (
          <PasswordInput
            value={typeof displayValue === "string" ? displayValue : ""}
            onChange={(v) => {
              setAutoGeneratedValue(null);
              onChange?.(v);
            }}
            placeholder={config.placeholder}
            readonly={readonly}
            disabled={disabled}
            inputStyle={inputTextStyle}
          />
        );

      case "slider":
        // DB에서 문자열로 반환되는 경우도 숫자로 변환
        const sliderValue =
          typeof displayValue === "number"
            ? displayValue
            : displayValue !== undefined && displayValue !== null && displayValue !== "" && !isNaN(Number(displayValue))
              ? Number(displayValue)
              : (config.min ?? 0);
        return (
          <SliderInput
            value={sliderValue}
            onChange={(v) => {
              setAutoGeneratedValue(null);
              onChange?.(v);
            }}
            min={config.min}
            max={config.max}
            step={config.step}
            disabled={disabled}
          />
        );

      case "color":
        return (
          <ColorInput
            value={typeof displayValue === "string" ? displayValue : "#000000"}
            onChange={(v) => {
              setAutoGeneratedValue(null);
              onChange?.(v);
            }}
            disabled={disabled}
          />
        );

      case "textarea":
        return (
          <TextareaInput
            value={displayValue as string}
            onChange={(v) => {
              setAutoGeneratedValue(null);
              onChange?.(v);
            }}
            placeholder={config.placeholder}
            rows={config.rows}
            readonly={readonly}
            disabled={disabled}
            inputStyle={inputTextStyle}
          />
        );

      case "numbering": {
        // 채번 타입: ____ 부분만 편집 가능하게 처리
        const template = numberingTemplateRef.current;
        const canEdit = hadManualPartRef.current && template;

        // 채번 필드 렌더링

        // 템플릿이 없으면 읽기 전용 (아직 생성 전이거나 수동 입력 부분 없음)
        if (!canEdit) {
          return (
            <TextInput
              value={displayValue || ""}
              onChange={() => {}}
              placeholder={isGeneratingNumbering ? "생성 중..." : "자동 생성됩니다"}
              readonly={true}
              disabled={disabled || isGeneratingNumbering}
              inputStyle={inputTextStyle}
            />
          );
        }

        // 템플릿에서 prefix와 suffix 추출
        const templateParts = template.split("____");
        const templatePrefix = templateParts[0] || "";
        const templateSuffix = templateParts.length > 1 ? templateParts.slice(1).join("") : "";

        return (
          <div className="numbering-segment border-input flex h-full items-center rounded-md border outline-none transition-[color,box-shadow]">
            {/* 고정 접두어 */}
            {templatePrefix && (
              <span className="text-muted-foreground bg-muted flex h-full items-center rounded-l-[5px] px-2 text-sm">
                {templatePrefix}
              </span>
            )}
            {/* 편집 가능한 부분 */}
            <input
              type="text"
              value={manualInputValue}
              onChange={(e) => {
                const newUserInput = e.target.value;
                setManualInputValue(newUserInput);

                // 전체 값 조합
                const newValue = templatePrefix + newUserInput + templateSuffix;
                userEditedNumberingRef.current = true;
                setAutoGeneratedValue(newValue);

                // 모든 방법으로 formData 업데이트 시도
                onChange?.(newValue);
                if (onFormDataChange && columnName) {
                  onFormDataChange(columnName, newValue);
                }

                // 커스텀 이벤트로도 전달 (최후의 보루)
                if (typeof window !== "undefined" && columnName) {
                  window.dispatchEvent(
                    new CustomEvent("numberingValueChanged", {
                      detail: { columnName, value: newValue },
                    }),
                  );
                }
              }}
              placeholder="입력"
              className="h-full min-w-[60px] flex-1 border-0 bg-transparent px-2 text-sm ring-0"
              disabled={disabled || isGeneratingNumbering}
              style={{ ...inputTextStyle, outline: 'none' }}
            />
            {/* 고정 접미어 */}
            {templateSuffix && (
              <span className="text-muted-foreground bg-muted flex h-full items-center rounded-r-[5px] px-2 text-sm">
                {templateSuffix}
              </span>
            )}
          </div>
        );
      }

      default:
        return (
          <TextInput
            value={displayValue}
            onChange={(v) => {
              setAutoGeneratedValue(null);
              onChange?.(v);
            }}
            format={config.format}
            placeholder={config.placeholder}
            readonly={readonly}
            disabled={disabled}
            columnName={columnName}
            inputStyle={inputTextStyle}
          />
        );
    }
  };

  const actualLabel = label || style?.labelText;
  const showLabel = actualLabel && style?.labelDisplay !== false;
  const componentWidth = size?.width || style?.width;
  const componentHeight = size?.height || style?.height;

  // 라벨 위치 및 높이 계산
  const labelPos = style?.labelPosition || "top";
  const isHorizLabel = labelPos === "left" || labelPos === "right";
  const labelFontSize = style?.labelFontSize ? parseInt(String(style.labelFontSize)) : 14;
  const labelMarginBottom = style?.labelMarginBottom ? parseInt(String(style.labelMarginBottom)) : 4;
  const estimatedLabelHeight = labelFontSize + labelMarginBottom + 2;
  const labelGapValue = style?.labelGap || "8px";

  // 커스텀 스타일 감지
  const hasCustomBorder = !!(style?.borderWidth || style?.borderColor || style?.borderStyle || style?.border);
  const hasCustomBackground = !!style?.backgroundColor;
  const hasCustomRadius = !!style?.borderRadius;

  const customTextStyle: React.CSSProperties = {};
  if (style?.color) customTextStyle.color = getAdaptiveLabelColor(style.color);
  if (style?.fontSize) customTextStyle.fontSize = style.fontSize;
  if (style?.fontWeight) customTextStyle.fontWeight = style.fontWeight;
  if (style?.textAlign) customTextStyle.textAlign = style.textAlign as React.CSSProperties["textAlign"];
  const hasCustomText = Object.keys(customTextStyle).length > 0;
  const inputTextStyle: React.CSSProperties | undefined = hasCustomText ? customTextStyle : undefined;

  const labelElement = showLabel ? (
    <Label
      htmlFor={id}
      style={{
        ...(labelPos === "top" ? { position: "absolute" as const, top: `-${estimatedLabelHeight}px`, left: 0 } : {}),
        ...(labelPos === "bottom"
          ? { position: "absolute" as const, bottom: `-${estimatedLabelHeight}px`, left: 0 }
          : {}),
        fontSize: style?.labelFontSize || "14px",
        color: getAdaptiveLabelColor(style?.labelColor),
        fontWeight: style?.labelFontWeight || "500",
      }}
      className="text-sm font-medium whitespace-nowrap"
    >
      {actualLabel}
      {required && <span className="ml-0.5 text-amber-500">*</span>}
    </Label>
  ) : null;

  const inputContent = (
    <div
      className={cn(
        isHorizLabel ? "min-w-0 flex-1" : "h-full w-full",
        hasCustomBorder && "[&_.border]:border-0! [&_input]:border-0! [&_textarea]:border-0!",
        (hasCustomBorder || hasCustomRadius) &&
          "[&_.rounded-md]:rounded-none! [&_input]:rounded-none! [&_textarea]:rounded-none!",
        hasCustomBackground && "[&_input]:bg-transparent! [&_textarea]:bg-transparent!",
      )}
      style={{ ...(hasCustomText ? customTextStyle : {}), ...(isHorizLabel ? { height: "100%" } : {}) }}
    >
      {renderInput()}
    </div>
  );

  if (isHorizLabel && showLabel) {
    return (
      <div
        ref={ref}
        id={id}
        className={cn(
          "flex",
          labelPos === "left" ? "flex-row items-center" : "flex-row-reverse items-center",
        )}
        style={{
          width: componentWidth,
          height: componentHeight,
          gap: labelGapValue,
        }}
      >
        <Label
          htmlFor={id}
          style={{
            fontSize: style?.labelFontSize || "14px",
            color: getAdaptiveLabelColor(style?.labelColor),
            fontWeight: style?.labelFontWeight || "500",
          }}
          className="text-sm font-medium whitespace-nowrap w-[120px] shrink-0"
        >
          {actualLabel}
          {required && <span className="ml-0.5 text-amber-500">*</span>}
        </Label>
        <div
          className={cn(
            "w-full min-w-0 flex-1",
            hasCustomBorder && "[&_.border]:border-0! [&_input]:border-0! [&_textarea]:border-0!",
            (hasCustomBorder || hasCustomRadius) &&
              "[&_.rounded-md]:rounded-none! [&_input]:rounded-none! [&_textarea]:rounded-none!",
            hasCustomBackground && "[&_input]:bg-transparent! [&_textarea]:bg-transparent!",
          )}
          style={{ ...(hasCustomText ? customTextStyle : {}) }}
        >
          {renderInput()}
        </div>
      </div>
    );
  }

  return (
    <div
      ref={ref}
      id={id}
      className="relative"
      style={{
        width: componentWidth,
        height: componentHeight,
      }}
    >
      {labelElement}
      {inputContent}
    </div>
  );
});

V2Input.displayName = "V2Input";

export default V2Input;
