import React, { useState, useRef, useCallback, useEffect } from "react";
import { Card, CardContent } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
import { toast } from "sonner";
import { uploadFiles, downloadFile, deleteFile, getComponentFiles, getFileInfoByObjid, getFilePreviewUrl } from "@/lib/api/file";
import { GlobalFileManager } from "@/lib/api/globalFile";
import { formatFileSize, cn } from "@/lib/utils";
import { apiClient } from "@/lib/api/client";
import { FileViewerModal } from "./FileViewerModal";
import { FileManagerModal } from "./FileManagerModal";
import { FileUploadConfig, FileInfo, FileUploadStatus, FileUploadResponse } from "./types";
import { useAuth } from "@/hooks/useAuth";
import {
  Upload,
  File,
  FileText,
  Image,
  Video,
  Music,
  Archive,
  Download,
  Eye,
  Trash2,
  AlertCircle,
  FileImage,
  FileVideo,
  FileAudio,
  Presentation,
} from "lucide-react";

// 파일 아이콘 매핑
const getFileIcon = (extension: string) => {
  const ext = extension.toLowerCase().replace(".", "");

  if (["jpg", "jpeg", "png", "gif", "bmp", "webp", "svg"].includes(ext)) {
    return <FileImage className="h-6 w-6 text-primary" />;
  }
  if (["mp4", "avi", "mov", "wmv", "flv", "webm"].includes(ext)) {
    return <FileVideo className="h-6 w-6 text-purple-500" />;
  }
  if (["mp3", "wav", "flac", "aac", "ogg"].includes(ext)) {
    return <FileAudio className="h-6 w-6 text-emerald-500" />;
  }
  if (["pdf"].includes(ext)) {
    return <FileText className="h-6 w-6 text-destructive" />;
  }
  if (["doc", "docx", "hwp", "hwpx", "pages"].includes(ext)) {
    return <FileText className="h-6 w-6 text-primary" />;
  }
  if (["xls", "xlsx", "hcell", "numbers"].includes(ext)) {
    return <FileText className="h-6 w-6 text-emerald-600" />;
  }
  if (["ppt", "pptx", "hanshow", "keynote"].includes(ext)) {
    return <Presentation className="h-6 w-6 text-amber-500" />;
  }
  if (["zip", "rar", "7z", "tar", "gz"].includes(ext)) {
    return <Archive className="h-6 w-6 text-muted-foreground" />;
  }

  return <File className="h-6 w-6 text-muted-foreground/70" />;
};

export interface FileUploadComponentProps {
  component: any;
  componentConfig: FileUploadConfig;
  componentStyle: React.CSSProperties;
  className: string;
  isInteractive: boolean;
  isDesignMode: boolean;
  formData: any;
  onFormDataChange: (fieldName: string, value: any) => void;
  onClick?: () => void;
  onDragStart?: (e: React.DragEvent) => void;
  onDragEnd?: (e: React.DragEvent) => void;
  onUpdate?: (updates: Partial<any>) => void;
  autoGeneration?: any;
  hidden?: boolean;
  onConfigChange?: (config: any) => void;
}

const FileUploadComponent: React.FC<FileUploadComponentProps> = ({
  component,
  componentConfig,
  componentStyle,
  className,
  isInteractive,
  isDesignMode = false, // 기본값 설정
  formData,
  onFormDataChange,
  onClick,
  onDragStart,
  onDragEnd,
  onUpdate,
}) => {
  // 🔑 인증 정보 가져오기
  const { user } = useAuth();
  
  const [uploadedFiles, setUploadedFiles] = useState<FileInfo[]>([]);
  const [uploadStatus, setUploadStatus] = useState<FileUploadStatus>("idle");
  const [dragOver, setDragOver] = useState(false);
  const [viewerFile, setViewerFile] = useState<FileInfo | null>(null);
  const [isViewerOpen, setIsViewerOpen] = useState(false);
  const [isFileManagerOpen, setIsFileManagerOpen] = useState(false);
  const [forceUpdate, setForceUpdate] = useState(0);
  const [representativeImageUrl, setRepresentativeImageUrl] = useState<string | null>(null);
  const fileInputRef = useRef<HTMLInputElement>(null);
  const containerRef = useRef<HTMLDivElement>(null);
  // objid 기반으로 파일이 로드되었는지 추적 (다른 이펙트가 덮어쓰지 않도록 방지)
  const filesLoadedFromObjidRef = useRef(false);

  // 🔑 레코드 모드 판단: formData에 id가 있으면 특정 행에 연결된 파일 관리
  const isRecordMode = !!(formData?.id && !String(formData.id).startsWith('temp_'));
  const recordTableName = formData?.tableName || component.tableName;
  const recordId = formData?.id;
  // 🔑 컬럼명 결정: component.columnName을 우선 사용 (실제 DB 컬럼명)
  // image, file 등의 웹타입 컬럼에 URL이 저장되어야 함
  const columnName = component.columnName || component.id || 'attachments';
  
  // 🔑 레코드 모드용 targetObjid 생성
  const getRecordTargetObjid = useCallback(() => {
    if (isRecordMode && recordTableName && recordId) {
      return `${recordTableName}:${recordId}:${columnName}`;
    }
    return null;
  }, [isRecordMode, recordTableName, recordId, columnName]);

  // 🔑 레코드별 고유 키 생성 (localStorage, 전역 상태용)
  // 🆕 columnName을 포함하여 같은 화면의 여러 파일 업로드 컴포넌트 구분
  const getUniqueKey = useCallback(() => {
    if (isRecordMode && recordTableName && recordId) {
      // 레코드 모드: 테이블명:레코드ID:컴포넌트ID:컬럼명 형태로 고유 키 생성
      return `fileUpload_${recordTableName}_${recordId}_${component.id}_${columnName}`;
    }
    // 기본 모드: 컴포넌트 ID + 컬럼명 사용
    return `fileUpload_${component.id}_${columnName}`;
  }, [isRecordMode, recordTableName, recordId, component.id, columnName]);


  // 🆕 레코드 ID 변경 시 파일 목록 초기화 및 새로 로드
  const prevRecordIdRef = useRef<any>(null);
  const prevIsRecordModeRef = useRef<boolean | null>(null);
  useEffect(() => {
    const recordIdChanged = prevRecordIdRef.current !== recordId;
    const modeChanged = prevIsRecordModeRef.current !== null && prevIsRecordModeRef.current !== isRecordMode;
    
    if (recordIdChanged || modeChanged) {
      prevRecordIdRef.current = recordId;
      prevIsRecordModeRef.current = isRecordMode;
      
      // 레코드 변경 또는 등록 모드 전환 시 항상 파일 목록 초기화
      setUploadedFiles([]);
      setRepresentativeImageUrl(null);
      filesLoadedFromObjidRef.current = false;
    } else if (prevIsRecordModeRef.current === null) {
      // 초기 마운트 시 모드 저장
      prevIsRecordModeRef.current = isRecordMode;
    }
  }, [recordId, isRecordMode]);

  // 컴포넌트 마운트 시 즉시 localStorage에서 파일 복원
  // 🔑 등록 모드(recordId가 없는 경우)에서는 파일을 복원하지 않음 - 항상 빈 상태로 시작
  useEffect(() => {
    if (!component?.id) return;
    
    // 등록 모드(새 레코드)인 경우 파일 복원 스킵 - 빈 상태 유지
    if (!isRecordMode || !recordId) {
      return;
    }

    try {
      // 🔑 레코드별 고유 키 사용 (수정 모드에서만)
      const backupKey = getUniqueKey();
      const backupFiles = localStorage.getItem(backupKey);
      if (backupFiles) {
        const parsedFiles = JSON.parse(backupFiles);
        if (parsedFiles.length > 0) {
          setUploadedFiles(parsedFiles);

          // 전역 상태에도 복원 (레코드별 고유 키 사용)
          if (typeof window !== "undefined") {
            (window as any).globalFileState = {
              ...(window as any).globalFileState,
              [backupKey]: parsedFiles,
            };
          }
        }
      }
    } catch (e) {
      console.warn("컴포넌트 마운트 시 파일 복원 실패:", e);
    }
  }, [component.id, getUniqueKey, recordId, isRecordMode]); // 레코드별 고유 키 변경 시 재실행

  // 🔑 수정 모드: formData[columnName]에 저장된 objid로 이미지 로드
  // 콤마로 구분된 다중 objid도 처리 (예: "123,456")
  const imageObjidFromFormData = formData?.[columnName];
  
  useEffect(() => {
    if (!imageObjidFromFormData) {
      // formData에서 값이 사라지면 파일 목록도 초기화 (새 등록 시)
      // 단, 모달 내부의 컴포넌트만 초기화 - 일반 화면에서는 저장 후 리셋으로 인한 초기화 방지
      const isInModal = containerRef.current ? !!containerRef.current.closest('[role="dialog"]') : false;
      if (uploadedFiles.length > 0 && !isRecordMode && isInModal) {
        setUploadedFiles([]);
        filesLoadedFromObjidRef.current = false;
      }
      return;
    }

    const rawValue = String(imageObjidFromFormData);
    // 콤마 구분 다중 objid 또는 단일 objid 모두 처리
    const objids = rawValue.split(',').map(s => s.trim()).filter(s => /^\d+$/.test(s));

    if (objids.length === 0) return;

    // 모든 objid가 이미 로드되어 있으면 스킵
    const allLoaded = objids.every(id => uploadedFiles.some(f => String(f.objid) === id));
    if (allLoaded) return;

    (async () => {
      try {
        const loadedFiles: FileInfo[] = [];

        for (const objid of objids) {
          // 이미 로드된 파일은 스킵
          if (uploadedFiles.some(f => String(f.objid) === objid)) continue;

          const fileInfoResponse = await getFileInfoByObjid(objid);
          
          if (fileInfoResponse.success && fileInfoResponse.data) {
            const { realFileName, fileSize, fileExt, regdate, isRepresentative } = fileInfoResponse.data;
            
            loadedFiles.push({
              objid,
              realFileName,
              fileExt,
              fileSize,
              filePath: getFilePreviewUrl(objid),
              regdate,
              isImage: true,
              isRepresentative,
            } as FileInfo);
          } else {
            // 파일 정보 조회 실패 시 최소 정보로 추가
            loadedFiles.push({
              objid,
              realFileName: `file_${objid}`,
              fileExt: '.jpg',
              fileSize: 0,
              filePath: getFilePreviewUrl(objid),
              regdate: new Date().toISOString(),
              isImage: true,
            } as FileInfo);
          }
        }

        if (loadedFiles.length > 0) {
          setUploadedFiles(loadedFiles);
          filesLoadedFromObjidRef.current = true;
        }
      } catch (error) {
        console.error("🖼️ [FileUploadComponent] 파일 정보 조회 오류:", error);
      }
    })();
  }, [imageObjidFromFormData, columnName, component.id]);

  // 🎯 화면설계 모드에서 실제 화면으로의 실시간 동기화 이벤트 리스너
  // 🆕 columnName도 체크하여 같은 화면의 다른 파일 업로드 컴포넌트와 구분
  useEffect(() => {
    const handleDesignModeFileChange = (event: CustomEvent) => {
      const eventColumnName = event.detail.eventColumnName || event.detail.columnName;
      
      // 🆕 고유 키 또는 (컴포넌트ID + 컬럼명) 조합으로 체크
      const isForThisComponent = 
        (event.detail.uniqueKey && event.detail.uniqueKey === currentUniqueKey) ||
        (event.detail.componentId === component.id && eventColumnName === columnName) ||
        (event.detail.componentId === component.id && !eventColumnName); // 이전 호환성

      // 🆕 현재 컴포넌트와 일치하고 화면설계 모드에서 온 이벤트인 경우
      if (isForThisComponent && event.detail.source === "designMode") {
        // 파일 상태 업데이트
        const newFiles = event.detail.files || [];
        setUploadedFiles(newFiles);

        // localStorage 백업 업데이트 (레코드별 고유 키 사용)
        try {
          const backupKey = currentUniqueKey;
          localStorage.setItem(backupKey, JSON.stringify(newFiles));
        } catch (e) {
          console.warn("localStorage 백업 업데이트 실패:", e);
        }

        // 전역 상태 업데이트 (🆕 고유 키 사용)
        if (typeof window !== "undefined") {
          (window as any).globalFileState = {
            ...(window as any).globalFileState,
            [currentUniqueKey]: newFiles,
          };
        }

        // onUpdate 콜백 호출 (부모 컴포넌트에 알림)
        if (onUpdate) {
          onUpdate({
            uploadedFiles: newFiles,
            lastFileUpdate: event.detail.timestamp,
          });
        }
      }
    };

    if (typeof window !== "undefined") {
      window.addEventListener("globalFileStateChanged", handleDesignModeFileChange as EventListener);

      return () => {
        window.removeEventListener("globalFileStateChanged", handleDesignModeFileChange as EventListener);
      };
    }
  }, [component.id, onUpdate]);

  // 템플릿 파일과 데이터 파일을 조회하는 함수
  const loadComponentFiles = useCallback(async () => {
    if (!component?.id) return false;

    // 🔑 등록 모드(새 레코드)인 경우 파일 조회 스킵 - 빈 상태 유지
    if (!isRecordMode || !recordId) {
      return false;
    }

    try {
      // 1. formData에서 screenId 가져오기
      let screenId = formData?.screenId;
      
      // 2. URL에서 screenId 추출 (/screens/:id 패턴)
      if (!screenId && typeof window !== "undefined") {
        const pathname = window.location.pathname;
        const screenMatch = pathname.match(/\/screens\/(\d+)/);
        if (screenMatch) {
          screenId = parseInt(screenMatch[1]);
        }
      }

      // 3. 디자인 모드인 경우 임시 화면 ID 사용
      if (!screenId && isDesignMode) {
        screenId = 999999; // 디자인 모드 임시 ID
      }

      // 4. 화면 ID가 없으면 컴포넌트 ID만으로 조회 시도
      if (!screenId) {
        console.warn("⚠️ 화면 ID 없음, 컴포넌트 ID만으로 파일 조회:", {
          componentId: component.id,
          pathname: window.location.pathname,
          formData: formData,
        });
        // screenId를 0으로 설정하여 컴포넌트 ID로만 조회
        screenId = 0;
      }

      const params = {
        screenId,
        componentId: component.id,
        tableName: recordTableName || formData?.tableName || component.tableName,
        recordId: recordId || formData?.id,
        columnName: columnName, // 🔑 레코드 모드에서 사용하는 columnName
      };

      const response = await getComponentFiles(params);

      if (response.success) {

        // 파일 데이터 형식 통일
        const formattedFiles = response.totalFiles.map((file: any) => ({
          objid: file.objid || file.id,
          savedFileName: file.savedFileName || file.saved_file_name,
          realFileName: file.realFileName || file.real_file_name,
          fileSize: file.fileSize || file.file_size,
          fileExt: file.fileExt || file.file_ext,
          regdate: file.regdate,
          status: file.status || "ACTIVE",
          uploadedAt: file.uploadedAt || new Date().toISOString(),
          ...file,
        }));

        // 서버에서 0개 반환 + objid 기반 로딩이 이미 완료된 경우 덮어쓰지 않음
        if (formattedFiles.length === 0 && filesLoadedFromObjidRef.current) {
          return false;
        }

        // 🔄 localStorage의 기존 파일과 서버 파일 병합 (레코드별 고유 키 사용)
        let finalFiles = formattedFiles;
        const uniqueKey = getUniqueKey();
        try {
          const backupFiles = localStorage.getItem(uniqueKey);
          if (backupFiles) {
            const parsedBackupFiles = JSON.parse(backupFiles);

            // 서버에 없는 localStorage 파일들을 추가 (objid 기준으로 중복 제거)
            const serverObjIds = new Set(formattedFiles.map((f: any) => f.objid));
            const additionalFiles = parsedBackupFiles.filter((f: any) => !serverObjIds.has(f.objid));

            finalFiles = [...formattedFiles, ...additionalFiles];
          }
        } catch (e) {
          console.warn("파일 병합 중 오류:", e);
        }

        setUploadedFiles(finalFiles);

        // 전역 상태에도 저장 (레코드별 고유 키 사용)
        if (typeof window !== "undefined") {
          (window as any).globalFileState = {
            ...(window as any).globalFileState,
            [uniqueKey]: finalFiles,
          };

          // 🌐 전역 파일 저장소에 등록 (페이지 간 공유용)
          GlobalFileManager.registerFiles(finalFiles, {
            uploadPage: window.location.pathname,
            componentId: component.id,
            screenId: formData?.screenId,
            recordId: recordId,
          });

          // localStorage 백업도 병합된 파일로 업데이트 (레코드별 고유 키 사용)
          try {
            localStorage.setItem(uniqueKey, JSON.stringify(finalFiles));
          } catch (e) {
            console.warn("localStorage 백업 업데이트 실패:", e);
          }
        }
        return true; // 새로운 로직 사용됨
      }
    } catch (error) {
      console.error("파일 조회 오류:", error);
    }
    return false; // 기존 로직 사용
  }, [component.id, component.tableName, component.columnName, formData?.screenId, formData?.tableName, formData?.id, getUniqueKey, recordId, isRecordMode, recordTableName, columnName]);

  // 컴포넌트 파일 동기화 (DB 우선, localStorage는 보조)
  useEffect(() => {
    const componentFiles = (component as any)?.uploadedFiles || [];
    const lastUpdate = (component as any)?.lastFileUpdate;

    // 🔒 항상 DB에서 최신 파일 목록을 조회 (멀티테넌시 격리)
    loadComponentFiles().then((dbLoadSuccess) => {
      if (dbLoadSuccess) {
        return; // DB 로드 성공 시 localStorage 무시
      }

      // objid 기반으로 이미 파일이 로드된 경우 빈 데이터로 덮어쓰지 않음
      if (filesLoadedFromObjidRef.current) {
        return;
      }

      // 등록 모드(새 레코드)인 경우 fallback 로드도 스킵 - 항상 빈 상태 유지
      if (!isRecordMode || !recordId) {
        return;
      }

      // DB 로드 실패 시에만 기존 로직 사용 (하위 호환성)

      // 전역 상태에서 최신 파일 정보 가져오기 (고유 키 사용)
      const globalFileState = typeof window !== "undefined" ? (window as any).globalFileState || {} : {};
      const uniqueKeyForFallback = getUniqueKey();
      const globalFiles = globalFileState[uniqueKeyForFallback] || globalFileState[component.id] || [];

      // 최신 파일 정보 사용 (전역 상태 > 컴포넌트 속성)
      const currentFiles = globalFiles.length > 0 ? globalFiles : componentFiles;

      // 빈 데이터로 기존 파일을 덮어쓰지 않음
      if (currentFiles.length === 0) {
        return;
      }

      // 최신 파일과 현재 파일 비교
      if (JSON.stringify(currentFiles) !== JSON.stringify(uploadedFiles)) {
        setUploadedFiles(currentFiles);
        setForceUpdate((prev) => prev + 1);
      }
    });
  }, [loadComponentFiles, component.id, (component as any)?.uploadedFiles, (component as any)?.lastFileUpdate]);

  // 전역 상태 변경 감지 (모든 파일 컴포넌트 동기화 + 화면 복원)
  // 🆕 columnName을 포함한 고유 키로 구분하여 다른 파일 업로드 컴포넌트에 영향 방지
  const currentUniqueKey = getUniqueKey();
  
  useEffect(() => {
    const handleGlobalFileStateChange = (event: CustomEvent) => {
      const { componentId, files, fileCount, timestamp, isRestore, uniqueKey: eventUniqueKey, eventColumnName } = event.detail;

      // 🆕 고유 키 또는 (컴포넌트ID + 컬럼명) 조합으로 체크
      const isForThisComponent = 
        (eventUniqueKey && eventUniqueKey === currentUniqueKey) ||
        (componentId === component.id && eventColumnName === columnName);

      // 🆕 같은 고유 키인 경우에만 업데이트 (componentId + columnName 조합)
      if (isForThisComponent) {

        setUploadedFiles(files);
        setForceUpdate((prev) => prev + 1);

        // localStorage 백업도 업데이트 (레코드별 고유 키 사용)
        try {
          const backupKey = currentUniqueKey;
          localStorage.setItem(backupKey, JSON.stringify(files));
        } catch (e) {
          console.warn("localStorage 백업 실패:", e);
        }
      }
    };

    if (typeof window !== "undefined") {
      window.addEventListener("globalFileStateChanged", handleGlobalFileStateChange as EventListener);

      return () => {
        window.removeEventListener("globalFileStateChanged", handleGlobalFileStateChange as EventListener);
      };
    }
  }, [component.id, columnName, currentUniqueKey, uploadedFiles.length]);

  // 파일 업로드 설정 - componentConfig가 undefined일 수 있으므로 안전하게 처리
  const safeComponentConfig = componentConfig || {};
  const fileConfig = {
    ...safeComponentConfig,
    accept: safeComponentConfig.accept || "*/*",
    multiple: safeComponentConfig.multiple || false,
    maxSize: safeComponentConfig.maxSize || 10 * 1024 * 1024, // 10MB
    maxFiles: safeComponentConfig.maxFiles || 5,
  } as FileUploadConfig;

  // 파일 선택 핸들러
  const handleFileSelect = useCallback(() => {
    if (fileInputRef.current) {
      fileInputRef.current.click();
    }
  }, []);

  const handleInputChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
    const files = Array.from(e.target.files || []);
    if (files.length > 0) {
      handleFileUpload(files);
    }
  }, []);

  // 백엔드 multer 제한에 맞춘 1회 요청당 최대 파일 수
  const CHUNK_SIZE = 10;

  // 파일 업로드 처리 (10개 초과 시 자동 분할 업로드)
  const handleFileUpload = useCallback(
    async (files: File[]) => {
      if (!files.length) return;

      // 중복 파일 체크
      const existingFileNames = uploadedFiles.map((f) => f.realFileName.toLowerCase());
      const duplicates: string[] = [];
      const uniqueFiles: File[] = [];

      files.forEach((file) => {
        const fileName = file.name.toLowerCase();
        if (existingFileNames.includes(fileName)) {
          duplicates.push(file.name);
        } else {
          uniqueFiles.push(file);
        }
      });

      if (duplicates.length > 0) {
        toast.error(`중복된 파일이 있습니다: ${duplicates.join(", ")}`, {
          description: "같은 이름의 파일이 이미 업로드되어 있습니다.",
          duration: 4000,
        });

        if (uniqueFiles.length === 0) {
          return; // 모든 파일이 중복이면 업로드 중단
        }

        // 일부만 중복인 경우 고유한 파일만 업로드
        toast.info(`${uniqueFiles.length}개의 새로운 파일만 업로드합니다.`);
      }

      const filesToUpload = uniqueFiles.length > 0 ? uniqueFiles : files;
      setUploadStatus("uploading");

      // 분할 업로드 여부 판단
      const totalFiles = filesToUpload.length;
      const totalChunks = Math.ceil(totalFiles / CHUNK_SIZE);
      const isChunked = totalChunks > 1;

      if (isChunked) {
        toast.loading(`파일 업로드 준비 중... (총 ${totalFiles}개, ${totalChunks}회 분할)`, { id: "file-upload" });
      } else {
        toast.loading("파일을 업로드하는 중...", { id: "file-upload" });
      }

      try {
        // 🔑 레코드 모드 우선 사용
        const effectiveTableName = recordTableName || formData?.tableName || component.tableName || "default_table";
        const effectiveRecordId = recordId || formData?.id;
        const effectiveColumnName = columnName;
        
        // screenId 추출 (우선순위: formData > URL)
        let screenId = formData?.screenId;
        if (!screenId && typeof window !== "undefined") {
          const pathname = window.location.pathname;
          const screenMatch = pathname.match(/\/screens\/(\d+)/);
          if (screenMatch) {
            screenId = parseInt(screenMatch[1]);
          }
        }

        let targetObjid;
        // 🔑 레코드 모드 판단 개선
        const effectiveIsRecordMode = isRecordMode || (effectiveRecordId && !String(effectiveRecordId).startsWith('temp_'));
        
        if (effectiveIsRecordMode && effectiveTableName && effectiveRecordId) {
          // 🎯 레코드 모드: 특정 행에 파일 연결
          targetObjid = `${effectiveTableName}:${effectiveRecordId}:${effectiveColumnName}`;
        } else if (screenId) {
          // 🔑 템플릿 파일 (백엔드 조회 형식과 동일하게)
          targetObjid = `screen_files:${screenId}:${component.id}:${effectiveColumnName}`;
        } else {
          // 기본값 (화면관리에서 사용)
          targetObjid = `temp_${component.id}`;
        }

        // 🔒 현재 사용자의 회사 코드 가져오기 (멀티테넌시 격리)
        const userCompanyCode = user?.companyCode || (window as any).__user__?.companyCode;
        
        // 🔑 레코드 모드일 때는 effectiveTableName을 우선 사용
        const finalLinkedTable = effectiveIsRecordMode 
          ? effectiveTableName 
          : (formData?.linkedTable || effectiveTableName);
        
        const uploadData = {
          autoLink: formData?.autoLink || true,
          linkedTable: finalLinkedTable,
          recordId: effectiveRecordId || `temp_${component.id}`,
          columnName: effectiveColumnName,
          isVirtualFileColumn: formData?.isVirtualFileColumn || true,
          docType: component.fileConfig?.docType || "DOCUMENT",
          docTypeName: component.fileConfig?.docTypeName || "일반 문서",
          companyCode: userCompanyCode,
          tableName: effectiveTableName,
          fieldName: effectiveColumnName,
          targetObjid: targetObjid,
          isRecordMode: effectiveIsRecordMode,
        };

        // 🔄 파일을 CHUNK_SIZE(10개)씩 나눠서 순차 업로드
        const allNewFiles: any[] = [];
        let failedChunks = 0;

        for (let chunkIndex = 0; chunkIndex < totalChunks; chunkIndex++) {
          const start = chunkIndex * CHUNK_SIZE;
          const end = Math.min(start + CHUNK_SIZE, totalFiles);
          const chunk = filesToUpload.slice(start, end);

          // 분할 업로드 시 진행 상태 토스트 업데이트
          if (isChunked) {
            toast.loading(
              `업로드 중... ${chunkIndex + 1}/${totalChunks} 배치 (${start + 1}~${end}번째 파일)`,
              { id: "file-upload" }
            );
          }

          try {
            const response = await uploadFiles({
              files: chunk,
              ...uploadData,
            });

            if (response.success) {
              const fileData = response.files || (response as any).data || [];
              const chunkFiles = fileData.map((file: any) => ({
                objid: file.objid || file.id,
                savedFileName: file.saved_file_name || file.savedFileName,
                realFileName: file.real_file_name || file.realFileName || file.name,
                fileSize: file.file_size || file.fileSize || file.size,
                fileExt: file.file_ext || file.fileExt || file.extension,
                filePath: file.file_path || file.filePath || file.path,
                docType: file.doc_type || file.docType,
                docTypeName: file.doc_type_name || file.docTypeName,
                targetObjid: file.target_objid || file.targetObjid,
                parentTargetObjid: file.parent_target_objid || file.parentTargetObjid,
                companyCode: file.company_code || file.companyCode,
                writer: file.writer,
                regdate: file.regdate,
                status: file.status || "ACTIVE",
                uploadedAt: new Date().toISOString(),
                ...file,
              }));
              allNewFiles.push(...chunkFiles);
            } else {
              console.error(`❌ ${chunkIndex + 1}번째 배치 업로드 실패:`, response);
              failedChunks++;
            }
          } catch (chunkError) {
            console.error(`❌ ${chunkIndex + 1}번째 배치 업로드 오류:`, chunkError);
            failedChunks++;
          }
        }

        // 모든 배치 처리 완료 후 결과 처리
        if (allNewFiles.length === 0) {
          throw new Error("업로드된 파일 데이터를 받지 못했습니다.");
        }

        const updatedFiles = [...uploadedFiles, ...allNewFiles];

        setUploadedFiles(updatedFiles);
        setUploadStatus("success");

        // localStorage 백업 (레코드별 고유 키 사용)
        try {
          const backupKey = getUniqueKey();
          localStorage.setItem(backupKey, JSON.stringify(updatedFiles));
        } catch (e) {
          console.warn("localStorage 백업 실패:", e);
        }

        // 전역 상태 업데이트 (모든 파일 컴포넌트 동기화)
        if (typeof window !== "undefined") {
          const globalFileState = (window as any).globalFileState || {};
          const uniqueKey = getUniqueKey();
          globalFileState[uniqueKey] = updatedFiles;
          (window as any).globalFileState = globalFileState;

          GlobalFileManager.registerFiles(allNewFiles, {
            uploadPage: window.location.pathname,
            componentId: component.id,
            screenId: formData?.screenId,
            recordId: recordId,
          });

          const syncEvent = new CustomEvent("globalFileStateChanged", {
            detail: {
              componentId: component.id,
              eventColumnName: columnName,
              uniqueKey: uniqueKey,
              recordId: recordId,
              files: updatedFiles,
              fileCount: updatedFiles.length,
              timestamp: Date.now(),
            },
          });
          window.dispatchEvent(syncEvent);
        }

        // 컴포넌트 업데이트
        if (onUpdate) {
          const timestamp = Date.now();
          onUpdate({
            uploadedFiles: updatedFiles,
            lastFileUpdate: timestamp,
          });
        } else {
          console.warn("⚠️ onUpdate 콜백이 없습니다!");
        }

        // 이미지/파일 컬럼에 objid 저장 (formData 업데이트)
        if (onFormDataChange && effectiveColumnName) {
          const fileObjids = updatedFiles.map(file => file.objid);
          const columnValue = fileConfig.multiple 
            ? fileObjids.join(',')
            : (fileObjids[0] || '');
          onFormDataChange(effectiveColumnName, columnValue);
        }

        // 그리드 파일 상태 새로고침 이벤트 발생
        if (typeof window !== "undefined") {
          const refreshEvent = new CustomEvent("refreshFileStatus", {
            detail: {
              tableName: effectiveTableName,
              recordId: effectiveRecordId,
              columnName: effectiveColumnName,
              targetObjid: targetObjid,
              fileCount: updatedFiles.length,
            },
          });
          window.dispatchEvent(refreshEvent);
        }

        // 컴포넌트 설정 콜백
        if (safeComponentConfig.onFileUpload) {
          safeComponentConfig.onFileUpload(allNewFiles);
        }

        // 성공/부분 성공 토스트 처리
        setUploadStatus("idle");
        toast.dismiss("file-upload");

        if (failedChunks > 0) {
          toast.warning(
            `${allNewFiles.length}개 업로드 완료, 일부 파일 실패`,
            { description: "일부 파일이 업로드되지 않았습니다. 다시 시도해주세요." }
          );
        } else {
          toast.success(`${allNewFiles.length}개 파일 업로드 완료`);
        }
      } catch (error) {
        console.error("파일 업로드 오류:", error);
        setUploadStatus("error");
        toast.dismiss("file-upload");
        toast.error(`파일 업로드 오류: ${error instanceof Error ? error.message : "알 수 없는 오류"}`);
      }
    },
    [safeComponentConfig, uploadedFiles, onFormDataChange, component.columnName, component.id, formData],
  );

  // 파일 뷰어 열기
  const handleFileView = useCallback((file: FileInfo) => {
    setViewerFile(file);
    setIsViewerOpen(true);
  }, []);

  // 파일 뷰어 닫기
  const handleViewerClose = useCallback(() => {
    setIsViewerOpen(false);
    setViewerFile(null);
  }, []);

  // 파일 다운로드
  const handleFileDownload = useCallback(async (file: FileInfo) => {
    try {
      await downloadFile({
        fileId: file.objid,
        serverFilename: file.savedFileName,
        originalName: file.realFileName,
      });
      toast.success(`${file.realFileName} 다운로드 완료`);
    } catch (error) {
      console.error("파일 다운로드 오류:", error);
      toast.error("파일 다운로드에 실패했습니다.");
    }
  }, []);

  // 파일 삭제
  const handleFileDelete = useCallback(
    async (file: FileInfo | string) => {
      try {
        const fileId = typeof file === "string" ? file : file.objid;
        const fileName = typeof file === "string" ? "파일" : file.realFileName;

        const serverFilename = typeof file === "string" ? "temp_file" : file.savedFileName;
        await deleteFile(fileId, serverFilename);

        const updatedFiles = uploadedFiles.filter((f) => f.objid !== fileId);
        setUploadedFiles(updatedFiles);

        // localStorage 백업 업데이트 (레코드별 고유 키 사용)
        try {
          const backupKey = getUniqueKey();
          localStorage.setItem(backupKey, JSON.stringify(updatedFiles));
        } catch (e) {
          console.warn("localStorage 백업 업데이트 실패:", e);
        }

        // 전역 상태 업데이트 (모든 파일 컴포넌트 동기화)
        if (typeof window !== "undefined") {
          // 전역 파일 상태 업데이트 (레코드별 고유 키 사용)
          const globalFileState = (window as any).globalFileState || {};
          const uniqueKey = getUniqueKey();
          globalFileState[uniqueKey] = updatedFiles;
          (window as any).globalFileState = globalFileState;

          // 모든 파일 컴포넌트에 동기화 이벤트 발생
          // 🆕 columnName 추가하여 같은 화면의 다른 파일 업로드 컴포넌트와 구분
          const syncEvent = new CustomEvent("globalFileStateChanged", {
            detail: {
              componentId: component.id,
              eventColumnName: columnName, // 🆕 컬럼명 추가
              uniqueKey: uniqueKey, // 🆕 고유 키 추가
              recordId: recordId, // 🆕 레코드 ID 추가
              files: updatedFiles,
              fileCount: updatedFiles.length,
              timestamp: Date.now(),
              source: "realScreen", // 🎯 실제 화면에서 온 이벤트임을 표시
              action: "delete",
            },
          });
          window.dispatchEvent(syncEvent);
        }

        // 컴포넌트 업데이트
        if (onUpdate) {
          const timestamp = Date.now();
          onUpdate({
            uploadedFiles: updatedFiles,
            lastFileUpdate: timestamp,
          });
        }

        // 🆕 파일 삭제 후 컬럼 데이터 동기화
        if (onFormDataChange && columnName) {
          // 🎯 삭제 후 남은 파일들의 objid로 컬럼 값 업데이트
          const fileObjids = updatedFiles.map(f => f.objid);
          const columnValue = fileConfig.multiple 
            ? fileObjids.join(',')
            : (fileObjids[0] || '');
          
          // onFormDataChange를 (fieldName, value) 형태로 호출 (SaveModal 호환)
          onFormDataChange(columnName, columnValue);
        }

        toast.success(`${fileName} 삭제 완료`);
      } catch (error) {
        console.error("파일 삭제 오류:", error);
        toast.error("파일 삭제에 실패했습니다.");
      }
    },
    [uploadedFiles, onUpdate, component.id, isRecordMode, onFormDataChange, recordTableName, recordId, columnName, getUniqueKey],
  );

  // 대표 이미지 Blob URL 로드
  const loadRepresentativeImage = useCallback(
    async (file: FileInfo) => {
      try {
        const isImage = ["jpg", "jpeg", "png", "gif", "bmp", "webp", "svg"].includes(
          file.fileExt.toLowerCase().replace(".", "")
        );

        if (!isImage) {
          setRepresentativeImageUrl(null);
          return;
        }

        // objid가 없거나 유효하지 않으면 로드 중단
        if (!file.objid || file.objid === "0" || file.objid === "") {
          console.warn("⚠️ 대표 이미지 로드 실패: objid가 없음", file);
          setRepresentativeImageUrl(null);
          return;
        }

        // API 클라이언트를 통해 Blob으로 다운로드 (인증 토큰 포함)
        // 🔑 previewUrl 상대 경로 대신 apiClient를 사용하여 Docker 환경에서도 정상 동작
        const response = await apiClient.get(`/files/preview/${file.objid}`, {
          params: {
            serverFilename: file.savedFileName,
          },
          responseType: "blob",
        });

        // Blob URL 생성
        const blob = new Blob([response.data]);
        const url = window.URL.createObjectURL(blob);
        
        // 이전 URL 정리
        if (representativeImageUrl) {
          window.URL.revokeObjectURL(representativeImageUrl);
        }

        setRepresentativeImageUrl(url);
      } catch (error: any) {
        console.error("❌ 대표 이미지 로드 실패:", {
          file: file.realFileName,
          objid: file.objid,
          error: error?.response?.status || error?.message,
        });
        setRepresentativeImageUrl(null);
      }
    },
    [representativeImageUrl],
  );

  // 대표 이미지 설정 핸들러
  const handleSetRepresentative = useCallback(
    async (file: FileInfo) => {
      try {
        // API 호출하여 DB에 대표 파일 설정
        const { setRepresentativeFile } = await import("@/lib/api/file");
        await setRepresentativeFile(file.objid);

        // 상태 업데이트
        const updatedFiles = uploadedFiles.map((f) => ({
          ...f,
          isRepresentative: f.objid === file.objid,
        }));

        setUploadedFiles(updatedFiles);

        // 대표 이미지 로드
        loadRepresentativeImage(file);
      } catch (e) {
        console.error("❌ 대표 파일 설정 실패:", e);
      }
    },
    [uploadedFiles, component.id, loadRepresentativeImage]
  );

  // uploadedFiles 변경 시 대표 이미지 로드
  useEffect(() => {
    const representativeFile = uploadedFiles.find(f => f.isRepresentative) || uploadedFiles[0];
    if (representativeFile) {
      loadRepresentativeImage(representativeFile);
    } else {
      setRepresentativeImageUrl(null);
    }

    // 컴포넌트 언마운트 시 Blob URL 정리
    return () => {
      if (representativeImageUrl) {
        window.URL.revokeObjectURL(representativeImageUrl);
      }
    };
  }, [uploadedFiles]);

  // 드래그 앤 드롭 핸들러
  const handleDragOver = useCallback(
    (e: React.DragEvent) => {
      e.preventDefault();
      e.stopPropagation();
      if (!safeComponentConfig.readonly && !safeComponentConfig.disabled) {
        setDragOver(true);
      }
    },
    [safeComponentConfig.readonly, safeComponentConfig.disabled],
  );

  const handleDragLeave = useCallback((e: React.DragEvent) => {
    e.preventDefault();
    e.stopPropagation();
    setDragOver(false);
  }, []);

  const handleDrop = useCallback(
    (e: React.DragEvent) => {
      e.preventDefault();
      e.stopPropagation();
      setDragOver(false);

      if (!safeComponentConfig.readonly && !safeComponentConfig.disabled) {
        const files = Array.from(e.dataTransfer.files);
        if (files.length > 0) {
          handleFileUpload(files);
        }
      }
    },
    [safeComponentConfig.readonly, safeComponentConfig.disabled, handleFileUpload],
  );

  // 클릭 핸들러
  const handleClick = useCallback(
    (e: React.MouseEvent) => {
      e.preventDefault();
      e.stopPropagation();
      if (!safeComponentConfig.readonly && !safeComponentConfig.disabled) {
        handleFileSelect();
      }
      onClick?.();
    },
    [safeComponentConfig.readonly, safeComponentConfig.disabled, handleFileSelect, onClick],
  );

  // 🔧 커스텀 스타일 감지 (StyleEditor에서 설정한 값)
  const customStyle = component.style || {};
  const hasCustomBorder = !!(customStyle.borderWidth || customStyle.borderColor || customStyle.borderStyle || customStyle.border);
  const hasCustomBackground = !!customStyle.backgroundColor;
  const hasCustomRadius = !!customStyle.borderRadius;

  return (
    <div
      ref={containerRef}
      style={{
        ...componentStyle,
        width: "100%",
        height: "100%",
        border: hasCustomBorder ? undefined : "none",
        boxShadow: "none",
        outline: "none",
        backgroundColor: hasCustomBackground ? undefined : "transparent",
        padding: "0px",
        borderRadius: hasCustomRadius ? undefined : "0px",
        marginBottom: "8px",
      }}
      className={`${className} file-upload-container`}
    >
      {/* 라벨 렌더링 */}
      {component.label && component.style?.labelDisplay !== false && (
        <label
          style={{
            position: "absolute",
            top: "-20px",
            left: "0px",
            fontSize: customStyle.labelFontSize || "12px",
            color: customStyle.labelColor || "rgb(107, 114, 128)",
            fontWeight: customStyle.labelFontWeight || "400",
            background: "transparent",
            border: "none",
            boxShadow: "none",
            outline: "none",
            padding: "0px",
            margin: "0px"
          }}
        >
          {component.label}
          {component.required && (
            <span style={{ color: "#ef4444" }}>*</span>
          )}
        </label>
      )}

      <div
        className={cn(
          "relative flex h-full w-full flex-col overflow-hidden",
          // 커스텀 테두리가 없을 때만 기본 테두리 표시
          !hasCustomBorder && "border-border rounded-lg border",
          // 커스텀 배경이 없을 때만 기본 배경 표시
          !hasCustomBackground && "bg-card",
        )}
      >
        {/* 대표 이미지 전체 화면 표시 */}
        {uploadedFiles.length > 0 ? (() => {
          const representativeFile = uploadedFiles.find(f => f.isRepresentative) || uploadedFiles[0];
          const isImage = representativeFile && ["jpg", "jpeg", "png", "gif", "bmp", "webp", "svg"].includes(
            representativeFile.fileExt.toLowerCase().replace(".", "")
          );

          return (
            <>
              {isImage && representativeImageUrl ? (
                <div className="relative h-full w-full flex items-center justify-center bg-muted/10">
                  <img
                    src={representativeImageUrl}
                    alt={representativeFile.realFileName}
                    className="h-full w-full object-contain"
                  />
                </div>
              ) : isImage && !representativeImageUrl ? (
                <div className="flex h-full w-full flex-col items-center justify-center">
                  <div className="animate-spin rounded-full h-8 w-8 border-b-2 border-primary mb-2"></div>
                  <p className="text-sm text-muted-foreground">이미지 로딩 중...</p>
                </div>
              ) : (
                <div className="flex h-full w-full flex-col items-center justify-center">
                  {getFileIcon(representativeFile.fileExt)}
                  <p className="mt-3 text-sm font-medium text-center px-4">
                    {representativeFile.realFileName}
                  </p>
                  <Badge variant="secondary" className="mt-2">
                    대표 파일
                  </Badge>
                </div>
              )}
              
              {/* 우측 하단 자세히보기 버튼 */}
              <div className="absolute bottom-3 right-3">
                <Button
                  variant="secondary"
                  size="sm"
                  className="h-8 px-3 text-xs shadow-md"
                  onClick={() => setIsFileManagerOpen(true)}
                >
                  자세히보기 ({uploadedFiles.length})
                </Button>
              </div>
            </>
          );
        })() : (
          <div className="flex h-full w-full flex-col items-center justify-center text-muted-foreground">
            <File className="mb-3 h-12 w-12" />
            <p className="text-sm font-medium">업로드된 파일이 없습니다</p>
            <Button
              variant="outline"
              size="sm"
              className="mt-4 h-8 px-3 text-xs"
              onClick={() => setIsFileManagerOpen(true)}
            >
              파일 업로드
            </Button>
          </div>
        )}
      </div>

      {/* 파일뷰어 모달 */}
      <FileViewerModal
        file={viewerFile}
        isOpen={isViewerOpen}
        onClose={handleViewerClose}
        onDownload={safeComponentConfig.allowDownload !== false ? handleFileDownload : undefined}
        onDelete={!isDesignMode && safeComponentConfig.allowDelete !== false ? handleFileDelete : undefined}
      />

      {/* 파일 관리 모달 */}
      <FileManagerModal
        isOpen={isFileManagerOpen}
        onClose={() => setIsFileManagerOpen(false)}
        uploadedFiles={uploadedFiles}
        onFileUpload={handleFileUpload}
        onFileDownload={handleFileDownload}
        onFileDelete={handleFileDelete}
        onFileView={handleFileView}
        onSetRepresentative={handleSetRepresentative}
        config={fileConfig}
        isDesignMode={isDesignMode}
      />
    </div>
  );
};

export { FileUploadComponent };
export default FileUploadComponent;
