"use client";

import { useRef, useState, useEffect } from "react";
import { ComponentConfig } from "@/types/report";
import { useReportDesigner } from "@/contexts/ReportDesignerContext";
import { getFullImageUrl } from "@/lib/api/client";
import JsBarcode from "jsbarcode";
import QRCode from "qrcode";

// 고정 스케일 팩터 (화면 해상도와 무관)
const MM_TO_PX = 4;

// 1D 바코드 렌더러 컴포넌트
interface BarcodeRendererProps {
  value: string;
  format: string;
  width: number;
  height: number;
  displayValue: boolean;
  lineColor: string;
  background: string;
  margin: number;
}

function BarcodeRenderer({
  value,
  format,
  width,
  height,
  displayValue,
  lineColor,
  background,
  margin,
}: BarcodeRendererProps) {
  const svgRef = useRef<SVGSVGElement>(null);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    if (!svgRef.current || !value) return;

    // 매번 에러 상태 초기화 후 재검사
    setError(null);

    try {
      // 바코드 형식에 따른 유효성 검사
      let isValid = true;
      let errorMsg = "";
      const trimmedValue = value.trim();

      if (format === "EAN13" && !/^\d{12,13}$/.test(trimmedValue)) {
        isValid = false;
        errorMsg = "EAN-13: 12~13자리 숫자 필요";
      } else if (format === "EAN8" && !/^\d{7,8}$/.test(trimmedValue)) {
        isValid = false;
        errorMsg = "EAN-8: 7~8자리 숫자 필요";
      } else if (format === "UPC" && !/^\d{11,12}$/.test(trimmedValue)) {
        isValid = false;
        errorMsg = "UPC: 11~12자리 숫자 필요";
      }

      if (!isValid) {
        setError(errorMsg);
        return;
      }

      // JsBarcode는 format을 소문자로 받음
      const barcodeFormat = format.toLowerCase();
      // transparent는 빈 문자열로 변환 (SVG 배경 없음)
      const bgColor = background === "transparent" ? "" : background;

      JsBarcode(svgRef.current, trimmedValue, {
        format: barcodeFormat,
        width: 2,
        height: Math.max(30, height - (displayValue ? 30 : 10)),
        displayValue: displayValue,
        lineColor: lineColor,
        background: bgColor,
        margin: margin,
        fontSize: 12,
        textMargin: 2,
      });
    } catch (err: any) {
      // JsBarcode 체크섬 오류 등
      setError(err?.message || "바코드 생성 실패");
    }
  }, [value, format, width, height, displayValue, lineColor, background, margin]);

  return (
    <div className="relative h-full w-full">
      {/* SVG는 항상 렌더링 (에러 시 숨김) */}
      <svg ref={svgRef} className={`max-h-full max-w-full ${error ? "hidden" : ""}`} />
      {/* 에러 메시지 오버레이 */}
      {error && (
        <div className="absolute inset-0 flex flex-col items-center justify-center text-xs text-destructive">
          <span>{error}</span>
          <span className="mt-1 text-muted-foreground/70">{value}</span>
        </div>
      )}
    </div>
  );
}

// QR코드 렌더러 컴포넌트
interface QRCodeRendererProps {
  value: string;
  size: number;
  fgColor: string;
  bgColor: string;
  level: "L" | "M" | "Q" | "H";
}

function QRCodeRenderer({ value, size, fgColor, bgColor, level }: QRCodeRendererProps) {
  const canvasRef = useRef<HTMLCanvasElement>(null);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    if (!canvasRef.current || !value) return;

    // 매번 에러 상태 초기화 후 재시도
    setError(null);

    // qrcode 라이브러리는 hex 색상만 지원, transparent는 흰색으로 대체
    const lightColor = bgColor === "transparent" ? "#ffffff" : bgColor;

    QRCode.toCanvas(
      canvasRef.current,
      value,
      {
        width: Math.max(50, size),
        margin: 2,
        color: {
          dark: fgColor,
          light: lightColor,
        },
        errorCorrectionLevel: level,
      },
      (err) => {
        if (err) {
          // 실제 에러 메시지 표시
          setError(err.message || "QR코드 생성 실패");
        }
      },
    );
  }, [value, size, fgColor, bgColor, level]);

  return (
    <div className="relative h-full w-full">
      {/* Canvas는 항상 렌더링 (에러 시 숨김) */}
      <canvas ref={canvasRef} className={`max-h-full max-w-full ${error ? "hidden" : ""}`} />
      {/* 에러 메시지 오버레이 */}
      {error && (
        <div className="absolute inset-0 flex flex-col items-center justify-center text-xs text-destructive">
          <span>{error}</span>
          <span className="mt-1 text-muted-foreground/70">{value}</span>
        </div>
      )}
    </div>
  );
}

interface CanvasComponentProps {
  component: ComponentConfig;
}

export function CanvasComponent({ component }: CanvasComponentProps) {
  const {
    components,
    selectedComponentId,
    selectedComponentIds,
    selectComponent,
    selectMultipleComponents,
    updateComponent,
    getQueryResult,
    snapValueToGrid,
    calculateAlignmentGuides,
    clearAlignmentGuides,
    canvasWidth,
    canvasHeight,
    margins,
    layoutConfig,
    currentPageId,
    duplicateAtPosition,
  } = useReportDesigner();
  const [isDragging, setIsDragging] = useState(false);
  const [isResizing, setIsResizing] = useState(false);
  const [dragStart, setDragStart] = useState({ x: 0, y: 0 });
  const [resizeStart, setResizeStart] = useState({ x: 0, y: 0, width: 0, height: 0 });
  const componentRef = useRef<HTMLDivElement>(null);
  
  // Alt+드래그 복제를 위한 상태
  const [isAltDuplicating, setIsAltDuplicating] = useState(false);
  const duplicatedIdsRef = useRef<string[]>([]);
  // 복제 시 원본 컴포넌트들의 위치 저장 (상대적 위치 유지용)
  const originalPositionsRef = useRef<Map<string, { x: number; y: number }>>(new Map());

  // 인라인 편집 상태
  const [isEditing, setIsEditing] = useState(false);
  const [editValue, setEditValue] = useState("");
  const textareaRef = useRef<HTMLTextAreaElement>(null);

  const isSelected = selectedComponentId === component.id;
  const isMultiSelected = selectedComponentIds.includes(component.id);
  const isLocked = component.locked === true;
  const isGrouped = !!component.groupId;

  // 표시할 값 결정
  const getDisplayValue = (): string => {
    // 쿼리와 필드가 연결되어 있으면 실제 데이터 조회
    if (component.queryId && component.fieldName) {
      const queryResult = getQueryResult(component.queryId);

      // 실행 결과가 있으면 첫 번째 행의 해당 필드 값 표시
      if (queryResult && queryResult.rows.length > 0) {
        const firstRow = queryResult.rows[0];
        const value = firstRow[component.fieldName];

        // 값이 있으면 문자열로 변환하여 반환
        if (value !== null && value !== undefined) {
          return String(value);
        }
      }

      // 실행 결과가 없거나 값이 없으면 필드명 표시
      return `{${component.fieldName}}`;
    }

    // 기본값이 있으면 기본값 표시
    if (component.defaultValue) {
      return component.defaultValue;
    }

    // 둘 다 없으면 타입에 따라 기본 텍스트
    return component.type === "text" ? "텍스트 입력" : "레이블 텍스트";
  };

  // 텍스트 컴포넌트: 더블 클릭 시 컨텐츠에 맞게 크기 조절
  const fitTextToContent = () => {
    if (isLocked) return;
    if (component.type !== "text" && component.type !== "label") return;

    const minWidth = 50;
    const minHeight = 30;

    // 여백을 px로 변환
    const marginRightPx = margins.right * MM_TO_PX;
    const marginBottomPx = margins.bottom * MM_TO_PX;
    const canvasWidthPx = canvasWidth * MM_TO_PX;
    const canvasHeightPx = canvasHeight * MM_TO_PX;

    // 최대 크기 (여백 고려)
    const maxWidth = canvasWidthPx - marginRightPx - component.x;
    const maxHeight = canvasHeightPx - marginBottomPx - component.y;

    const displayValue = getDisplayValue();
    const fontSize = component.fontSize || 14;
    
    // 줄바꿈으로 분리하여 각 줄의 너비 측정
    const lines = displayValue.split("\n");
    let maxLineWidth = 0;

    lines.forEach((line) => {
      const measureEl = document.createElement("span");
      measureEl.style.position = "absolute";
      measureEl.style.visibility = "hidden";
      measureEl.style.whiteSpace = "nowrap";
      measureEl.style.fontSize = `${fontSize}px`;
      measureEl.style.fontWeight = component.fontWeight || "normal";
      measureEl.style.fontFamily = "system-ui, -apple-system, sans-serif";
      measureEl.textContent = line || " "; // 빈 줄은 공백으로
      document.body.appendChild(measureEl);
      
      const lineWidth = measureEl.getBoundingClientRect().width;
      maxLineWidth = Math.max(maxLineWidth, lineWidth);
      document.body.removeChild(measureEl);
    });

    // 컴포넌트 padding (p-2 = 8px * 2) + 여유분
    const horizontalPadding = 24;
    const verticalPadding = 20;
    
    // 줄 높이 계산 (font-size * line-height 약 1.5)
    const lineHeight = fontSize * 1.5;
    const totalHeight = lines.length * lineHeight;

    const finalWidth = Math.min(maxLineWidth + horizontalPadding, maxWidth);
    const finalHeight = Math.min(totalHeight + verticalPadding, maxHeight);

    const newWidth = Math.max(minWidth, finalWidth);
    const newHeight = Math.max(minHeight, finalHeight);

    // 크기 업데이트
    updateComponent(component.id, {
      width: snapValueToGrid(newWidth),
      height: snapValueToGrid(newHeight),
    });
  };

  // 더블 클릭 핸들러 (텍스트 컴포넌트: 인라인 편집 모드 진입)
  const handleDoubleClick = (e: React.MouseEvent) => {
    if (component.type !== "text" && component.type !== "label") return;
    if (isLocked) return; // 잠긴 컴포넌트는 편집 불가
    
    e.stopPropagation();
    
    // 인라인 편집 모드 진입
    setEditValue(component.defaultValue || "");
    setIsEditing(true);
  };

  // 인라인 편집 시작 시 textarea에 포커스
  useEffect(() => {
    if (isEditing && textareaRef.current) {
      textareaRef.current.focus();
      textareaRef.current.select();
    }
  }, [isEditing]);

  // 선택 해제 시 편집 모드 종료를 위한 ref
  const editValueRef = useRef(editValue);
  const isEditingRef = useRef(isEditing);
  editValueRef.current = editValue;
  isEditingRef.current = isEditing;

  // 선택 해제 시 편집 모드 종료 (저장 후 종료)
  useEffect(() => {
    if (!isSelected && !isMultiSelected && isEditingRef.current) {
      // 현재 편집 값으로 저장
      if (editValueRef.current !== component.defaultValue) {
        updateComponent(component.id, { defaultValue: editValueRef.current });
      }
      setIsEditing(false);
    }
  }, [isSelected, isMultiSelected, component.id, component.defaultValue, updateComponent]);

  // 인라인 편집 저장
  const handleEditSave = () => {
    if (!isEditing) return;
    
    updateComponent(component.id, {
      defaultValue: editValue,
    });
    setIsEditing(false);
  };

  // 인라인 편집 취소
  const handleEditCancel = () => {
    setIsEditing(false);
    setEditValue("");
  };

  // 인라인 편집 키보드 핸들러
  const handleEditKeyDown = (e: React.KeyboardEvent) => {
    if (e.key === "Escape") {
      e.preventDefault();
      handleEditCancel();
    } else if (e.key === "Enter" && !e.shiftKey) {
      // Enter: 저장 (Shift+Enter는 줄바꿈)
      e.preventDefault();
      handleEditSave();
    }
  };

  // 드래그 시작
  const handleMouseDown = (e: React.MouseEvent) => {
    // 편집 모드에서는 드래그 비활성화
    if (isEditing) return;
    
    if ((e.target as HTMLElement).classList.contains("resize-handle")) {
      return;
    }

    // 잠긴 컴포넌트는 드래그 불가
    if (isLocked) {
      e.stopPropagation();
      // Ctrl/Cmd 키 감지 (다중 선택)
      const isMultiSelect = e.ctrlKey || e.metaKey;
      selectComponent(component.id, isMultiSelect);
      return;
    }

    e.stopPropagation();

    // Ctrl/Cmd 키 감지 (다중 선택)
    const isMultiSelect = e.ctrlKey || e.metaKey;
    // Alt 키 감지 (복제 드래그)
    const isAltPressed = e.altKey;

    // 이미 다중 선택의 일부인 경우: 선택 상태 유지 (드래그만 시작)
    const isPartOfMultiSelection = selectedComponentIds.length > 1 && selectedComponentIds.includes(component.id);
    
    if (!isPartOfMultiSelection) {
      // 그룹화된 컴포넌트 클릭 시: 같은 그룹의 모든 컴포넌트 선택
      if (isGrouped && !isMultiSelect) {
        const groupMembers = components.filter((c) => c.groupId === component.groupId);
        const groupMemberIds = groupMembers.map((c) => c.id);
        // 첫 번째 컴포넌트를 선택하고, 나머지를 다중 선택에 추가
        selectComponent(groupMemberIds[0], false);
        groupMemberIds.slice(1).forEach((id) => selectComponent(id, true));
      } else {
        selectComponent(component.id, isMultiSelect);
      }
    }

    // Alt+드래그: 복제 모드
    if (isAltPressed) {
      // 복제할 컴포넌트 ID 목록 결정
      let idsToClone: string[] = [];
      
      if (isPartOfMultiSelection) {
        // 다중 선택된 경우: 잠기지 않은 선택된 모든 컴포넌트 복제
        idsToClone = selectedComponentIds.filter((id) => {
          const c = components.find((comp) => comp.id === id);
          return c && !c.locked;
        });
      } else if (isGrouped) {
        // 그룹화된 경우: 같은 그룹의 모든 컴포넌트 복제
        idsToClone = components
          .filter((c) => c.groupId === component.groupId && !c.locked)
          .map((c) => c.id);
      } else {
        // 단일 컴포넌트
        idsToClone = [component.id];
      }

      if (idsToClone.length > 0) {
        // 원본 컴포넌트들의 위치 저장 (복제본 ID -> 원본 위치 매핑용)
        const positionsMap = new Map<string, { x: number; y: number }>();
        idsToClone.forEach((id) => {
          const comp = components.find((c) => c.id === id);
          if (comp) {
            positionsMap.set(id, { x: comp.x, y: comp.y });
          }
        });

        // 복제 생성 (오프셋 없이 원래 위치에)
        const newIds = duplicateAtPosition(idsToClone, 0, 0);
        if (newIds.length > 0) {
          // 복제된 컴포넌트 ID와 원본 위치 매핑
          // newIds[i]는 idsToClone[i]에서 복제됨
          const dupPositionsMap = new Map<string, { x: number; y: number }>();
          newIds.forEach((newId, index) => {
            const originalId = idsToClone[index];
            const originalPos = positionsMap.get(originalId);
            if (originalPos) {
              dupPositionsMap.set(newId, originalPos);
            }
          });
          originalPositionsRef.current = dupPositionsMap;

          // 복제된 컴포넌트들을 선택하고 드래그 시작
          duplicatedIdsRef.current = newIds;
          setIsAltDuplicating(true);
          
          // 복제된 컴포넌트들 선택
          if (newIds.length === 1) {
            selectComponent(newIds[0], false);
          } else {
            selectMultipleComponents(newIds);
          }
        }
      }
    }

    setIsDragging(true);
    setDragStart({
      x: e.clientX - component.x,
      y: e.clientY - component.y,
    });
  };

  // 리사이즈 시작
  const handleResizeStart = (e: React.MouseEvent) => {
    // 잠긴 컴포넌트는 리사이즈 불가
    if (isLocked) {
      e.stopPropagation();
      return;
    }

    e.stopPropagation();
    setIsResizing(true);
    setResizeStart({
      x: e.clientX,
      y: e.clientY,
      width: component.width,
      height: component.height,
    });
  };

  // 마우스 이동 핸들러 (전역)
  useEffect(() => {
    if (!isDragging && !isResizing) return;

    const handleMouseMove = (e: MouseEvent) => {
      if (isDragging) {
        const newX = Math.max(0, e.clientX - dragStart.x);
        const newY = Math.max(0, e.clientY - dragStart.y);

        // 여백을 px로 변환
        const marginTopPx = margins.top * MM_TO_PX;
        const marginBottomPx = margins.bottom * MM_TO_PX;
        const marginLeftPx = margins.left * MM_TO_PX;
        const marginRightPx = margins.right * MM_TO_PX;

        // 캔버스 경계 체크 (mm를 px로 변환)
        const canvasWidthPx = canvasWidth * MM_TO_PX;
        const canvasHeightPx = canvasHeight * MM_TO_PX;

        // 컴포넌트가 여백 안에 있도록 제한
        const minX = marginLeftPx;
        const minY = marginTopPx;
        const maxX = canvasWidthPx - marginRightPx - component.width;
        const maxY = canvasHeightPx - marginBottomPx - component.height;

        const boundedX = Math.min(Math.max(minX, newX), maxX);
        const boundedY = Math.min(Math.max(minY, newY), maxY);

        const snappedX = snapValueToGrid(boundedX);
        const snappedY = snapValueToGrid(boundedY);

        // 정렬 가이드라인 계산
        calculateAlignmentGuides(component.id, snappedX, snappedY, component.width, component.height);

        // 이동 거리 계산
        const deltaX = snappedX - component.x;
        const deltaY = snappedY - component.y;

        // Alt+드래그 복제 모드: 원본은 이동하지 않고 복제본만 이동
        if (isAltDuplicating && duplicatedIdsRef.current.length > 0) {
          // 복제된 컴포넌트들 이동 (각각의 원본 위치 기준으로 절대 위치 설정)
          duplicatedIdsRef.current.forEach((dupId) => {
            const dupComp = components.find((c) => c.id === dupId);
            const originalPos = originalPositionsRef.current.get(dupId);
            
            if (dupComp && originalPos) {
              // 각 복제본의 원본 위치에서 delta만큼 이동
              const targetX = originalPos.x + deltaX;
              const targetY = originalPos.y + deltaY;

              // 경계 체크
              const dupMaxX = canvasWidthPx - marginRightPx - dupComp.width;
              const dupMaxY = canvasHeightPx - marginBottomPx - dupComp.height;

              updateComponent(dupId, {
                x: Math.min(Math.max(marginLeftPx, targetX), dupMaxX),
                y: Math.min(Math.max(marginTopPx, targetY), dupMaxY),
              });
            }
          });
          return; // 원본 컴포넌트는 이동하지 않음
        }

        // 현재 컴포넌트 이동
        updateComponent(component.id, {
          x: snappedX,
          y: snappedY,
        });

        // 다중 선택된 경우: 선택된 다른 컴포넌트들도 함께 이동
        if (selectedComponentIds.length > 1 && selectedComponentIds.includes(component.id)) {
          components.forEach((c) => {
            // 현재 컴포넌트는 이미 이동됨, 잠긴 컴포넌트는 제외
            if (c.id !== component.id && selectedComponentIds.includes(c.id) && !c.locked) {
              const newMultiX = c.x + deltaX;
              const newMultiY = c.y + deltaY;

              // 경계 체크
              const multiMaxX = canvasWidthPx - marginRightPx - c.width;
              const multiMaxY = canvasHeightPx - marginBottomPx - c.height;

              updateComponent(c.id, {
                x: Math.min(Math.max(marginLeftPx, newMultiX), multiMaxX),
                y: Math.min(Math.max(marginTopPx, newMultiY), multiMaxY),
              });
            }
          });
        }
        // 그룹화된 경우: 같은 그룹의 다른 컴포넌트도 함께 이동
        else if (isGrouped) {
          components.forEach((c) => {
            if (c.groupId === component.groupId && c.id !== component.id) {
              const newGroupX = c.x + deltaX;
              const newGroupY = c.y + deltaY;

              // 그룹 컴포넌트도 경계 체크
              const groupMaxX = canvasWidthPx - c.width;
              const groupMaxY = canvasHeightPx - c.height;

              updateComponent(c.id, {
                x: Math.min(Math.max(0, newGroupX), groupMaxX),
                y: Math.min(Math.max(0, newGroupY), groupMaxY),
              });
            }
          });
        }
      } else if (isResizing) {
        const deltaX = e.clientX - resizeStart.x;
        const deltaY = e.clientY - resizeStart.y;
        const newWidth = Math.max(50, resizeStart.width + deltaX);
        const newHeight = Math.max(30, resizeStart.height + deltaY);

        // 여백을 px로 변환
        const marginRightPx = margins.right * MM_TO_PX;
        const marginBottomPx = margins.bottom * MM_TO_PX;

        // 캔버스 경계 체크
        const canvasWidthPx = canvasWidth * MM_TO_PX;
        const canvasHeightPx = canvasHeight * MM_TO_PX;

        // 컴포넌트가 여백을 벗어나지 않도록 최대 크기 제한
        const maxWidth = canvasWidthPx - marginRightPx - component.x;
        const maxHeight = canvasHeightPx - marginBottomPx - component.y;

        const boundedWidth = Math.min(newWidth, maxWidth);
        const boundedHeight = Math.min(newHeight, maxHeight);

        // 구분선은 방향에 따라 한 축만 조절 가능
        if (component.type === "divider") {
          if (component.orientation === "vertical") {
            // 세로 구분선: 높이만 조절
            updateComponent(component.id, {
              height: snapValueToGrid(boundedHeight),
            });
          } else {
            // 가로 구분선: 너비만 조절
            updateComponent(component.id, {
              width: snapValueToGrid(boundedWidth),
            });
          }
        } else if (component.type === "barcode" && component.barcodeType === "QR") {
          // QR코드는 정사각형 유지: 더 큰 변화량 기준으로 동기화
          const maxDelta = Math.abs(deltaX) > Math.abs(deltaY) ? deltaX : deltaY;
          const newSize = Math.max(50, resizeStart.width + maxDelta);
          const maxSize = Math.min(
            canvasWidthPx - marginRightPx - component.x,
            canvasHeightPx - marginBottomPx - component.y,
          );
          const boundedSize = Math.min(newSize, maxSize);
          const snappedSize = snapValueToGrid(boundedSize);
          updateComponent(component.id, {
            width: snappedSize,
            height: snappedSize,
          });
        } else {
        // Grid Snap 적용
        updateComponent(component.id, {
          width: snapValueToGrid(boundedWidth),
          height: snapValueToGrid(boundedHeight),
        });
        }
      }
    };

    const handleMouseUp = () => {
      setIsDragging(false);
      setIsResizing(false);
      // Alt 복제 상태 초기화
      setIsAltDuplicating(false);
      duplicatedIdsRef.current = [];
      originalPositionsRef.current = new Map();
      // 가이드라인 초기화
      clearAlignmentGuides();
    };

    document.addEventListener("mousemove", handleMouseMove);
    document.addEventListener("mouseup", handleMouseUp);

    return () => {
      document.removeEventListener("mousemove", handleMouseMove);
      document.removeEventListener("mouseup", handleMouseUp);
    };
  }, [
    isDragging,
    isResizing,
    isAltDuplicating,
    dragStart.x,
    dragStart.y,
    resizeStart.x,
    resizeStart.y,
    resizeStart.width,
    resizeStart.height,
    component.id,
    component.x,
    component.y,
    component.width,
    component.height,
    component.groupId,
    isGrouped,
    components,
    updateComponent,
    snapValueToGrid,
    calculateAlignmentGuides,
    clearAlignmentGuides,
    canvasWidth,
    canvasHeight,
  ]);

  // 컴포넌트 타입별 렌더링
  const renderContent = () => {
    const displayValue = getDisplayValue();
    const hasBinding = component.queryId && component.fieldName;

    switch (component.type) {
      case "text":
      case "label":
        // 인라인 편집 모드
        if (isEditing) {
          return (
            <textarea
              ref={textareaRef}
              value={editValue}
              onChange={(e) => setEditValue(e.target.value)}
              onBlur={handleEditSave}
              onKeyDown={handleEditKeyDown}
              className="h-full w-full resize-none border-none bg-transparent p-0 outline-none focus:ring-2 focus:ring-ring"
              style={{
                fontSize: `${component.fontSize}px`,
                color: component.fontColor,
                fontWeight: component.fontWeight,
                textAlign: component.textAlign as "left" | "center" | "right",
                lineHeight: "1.5",
              }}
            />
          );
        }
        
        return (
            <div
            className="h-full w-full"
              style={{
                fontSize: `${component.fontSize}px`,
                color: component.fontColor,
                fontWeight: component.fontWeight,
                textAlign: component.textAlign as "left" | "center" | "right",
                whiteSpace: "pre-wrap",
              }}
            >
              {displayValue}
          </div>
        );

      case "table":
        // 테이블은 쿼리 결과의 모든 행과 필드를 표시
        if (component.queryId) {
          const queryResult = getQueryResult(component.queryId);

          if (queryResult && queryResult.rows.length > 0) {
            // tableColumns가 없으면 자동 생성
            const columns =
              component.tableColumns && component.tableColumns.length > 0
                ? component.tableColumns
                : queryResult.fields.map((field) => ({
                    field,
                    header: field,
                    width: undefined,
                    align: "left" as const,
                  }));

            return (
              <div className="h-full w-full overflow-auto">
                <table
                  className="w-full border-collapse text-xs"
                  style={{
                    borderCollapse: component.showBorder !== false ? "collapse" : "separate",
                  }}
                >
                  <thead>
                    <tr
                      style={{
                        backgroundColor: component.headerBackgroundColor || "#f3f4f6",
                        color: component.headerTextColor || "#111827",
                      }}
                    >
                      {columns.map((col) => (
                        <th
                          key={col.field}
                          className={component.showBorder !== false ? "border border-input" : ""}
                          style={{
                            padding: "6px 8px",
                            textAlign: col.align || "left",
                            width: col.width ? `${col.width}px` : "auto",
                            fontWeight: "600",
                          }}
                        >
                          {col.header}
                        </th>
                      ))}
                    </tr>
                  </thead>
                  <tbody>
                    {queryResult.rows.map((row, idx) => (
                      <tr key={idx}>
                        {columns.map((col) => (
                          <td
                            key={col.field}
                            className={component.showBorder !== false ? "border border-input" : ""}
                            style={{
                              padding: "6px 8px",
                              textAlign: col.align || "left",
                              height: component.rowHeight ? `${component.rowHeight}px` : "auto",
                            }}
                          >
                            {String(row[col.field] ?? "")}
                          </td>
                        ))}
                      </tr>
                    ))}
                  </tbody>
                </table>
              </div>
            );
          }
        }

        // 기본 테이블 (데이터 없을 때)
        return (
          <div className="flex h-full w-full items-center justify-center border-2 border-dashed border-input bg-muted text-xs text-muted-foreground/70">
              쿼리를 연결하세요
          </div>
        );

      case "image":
        return (
          <div className="h-full w-full overflow-hidden">
            {component.imageUrl ? (
              <img
                src={getFullImageUrl(component.imageUrl)}
                alt="이미지"
                style={{
                  width: "100%",
                  height: "100%",
                  objectFit: component.objectFit || "contain",
                }}
              />
            ) : (
              <div className="flex h-full w-full items-center justify-center border border-dashed border-input bg-muted text-xs text-muted-foreground/70">
                이미지를 업로드하세요
              </div>
            )}
          </div>
        );

      case "divider":
        // 구분선 (가로: 너비만 조절, 세로: 높이만 조절)
        const dividerLineWidth = component.lineWidth || 1;
        const dividerLineColor = component.lineColor || "#000000";
        const isHorizontal = component.orientation !== "vertical";

        return (
          <div className={`flex h-full w-full ${isHorizontal ? "items-center" : "justify-center"}`}>
            <div
              style={{
                width: isHorizontal ? "100%" : `${dividerLineWidth}px`,
                height: isHorizontal ? `${dividerLineWidth}px` : "100%",
                backgroundColor: dividerLineColor,
                ...(component.lineStyle === "dashed" && {
                  backgroundImage: `repeating-linear-gradient(
                    ${isHorizontal ? "90deg" : "0deg"},
                    ${dividerLineColor} 0px,
                    ${dividerLineColor} 10px,
                    transparent 10px,
                    transparent 20px
                  )`,
                  backgroundColor: "transparent",
                }),
                ...(component.lineStyle === "dotted" && {
                  backgroundImage: `repeating-linear-gradient(
                    ${isHorizontal ? "90deg" : "0deg"},
                    ${dividerLineColor} 0px,
                    ${dividerLineColor} 3px,
                    transparent 3px,
                    transparent 10px
                  )`,
                  backgroundColor: "transparent",
                }),
                ...(component.lineStyle === "double" && {
                  boxShadow: isHorizontal
                    ? `0 ${dividerLineWidth * 2}px 0 0 ${dividerLineColor}`
                    : `${dividerLineWidth * 2}px 0 0 0 ${dividerLineColor}`,
                }),
              }}
            />
          </div>
        );

      case "signature":
        const sigLabelPos = component.labelPosition || "left";
        const sigShowLabel = component.showLabel !== false;
        const sigLabelText = component.labelText || "서명:";

        return (
          <div className="h-full w-full">
            <div
              className={`flex h-full gap-2 ${
                sigLabelPos === "top"
                  ? "flex-col"
                  : sigLabelPos === "bottom"
                    ? "flex-col-reverse"
                    : sigLabelPos === "right"
                      ? "flex-row-reverse"
                      : "flex-row"
              }`}
            >
              {sigShowLabel && (
                <div
                  className="flex items-center justify-center text-xs font-medium"
                  style={{
                    width: sigLabelPos === "left" || sigLabelPos === "right" ? "auto" : "100%",
                    minWidth: sigLabelPos === "left" || sigLabelPos === "right" ? "40px" : "auto",
                  }}
                >
                  {sigLabelText}
                </div>
              )}
              <div className="relative flex-1">
                {component.imageUrl ? (
                  <img
                    src={getFullImageUrl(component.imageUrl)}
                    alt="서명"
                    style={{
                      width: "100%",
                      height: "100%",
                      objectFit: component.objectFit || "contain",
                    }}
                  />
                ) : (
                  <div
                    className="flex h-full w-full items-center justify-center border-2 border-dashed bg-muted text-xs text-muted-foreground/70"
                    style={{
                      borderColor: component.borderColor || "#cccccc",
                    }}
                  >
                    서명 이미지
                  </div>
                )}
              </div>
            </div>
          </div>
        );

      case "stamp":
        const stampShowLabel = component.showLabel !== false;
        const stampLabelText = component.labelText || "(인)";
        const stampPersonName = component.personName || "";

        return (
          <div className="h-full w-full">
            <div className="flex h-full gap-2">
              {stampPersonName && <div className="flex items-center text-xs font-medium">{stampPersonName}</div>}
              <div className="relative flex-1">
                {component.imageUrl ? (
                  <img
                    src={getFullImageUrl(component.imageUrl)}
                    alt="도장"
                    style={{
                      width: "100%",
                      height: "100%",
                      objectFit: component.objectFit || "contain",
                    }}
                  />
                ) : (
                  <div
                    className="flex h-full w-full items-center justify-center border-2 border-dashed bg-muted text-xs text-muted-foreground/70"
                    style={{
                      borderColor: component.borderColor || "#cccccc",
                      borderRadius: "50%",
                    }}
                  >
                    도장 이미지
                  </div>
                )}
                {stampShowLabel && (
                  <div
                    className="absolute inset-0 flex items-center justify-center text-xs font-medium"
                    style={{
                      pointerEvents: "none",
                    }}
                  >
                    {stampLabelText}
                  </div>
                )}
              </div>
            </div>
          </div>
        );

      case "pageNumber":
        // 페이지 번호 포맷
        const format = component.pageNumberFormat || "number";
        const sortedPages = layoutConfig.pages.sort((a, b) => a.page_order - b.page_order);
        const currentPageIndex = sortedPages.findIndex((p) => p.page_id === currentPageId);
        const totalPages = sortedPages.length;
        const currentPageNum = currentPageIndex + 1;

        let pageNumberText = "";
        switch (format) {
          case "number":
            pageNumberText = `${currentPageNum}`;
            break;
          case "numberTotal":
            pageNumberText = `${currentPageNum} / ${totalPages}`;
            break;
          case "koreanNumber":
            pageNumberText = `${currentPageNum} 페이지`;
            break;
          default:
            pageNumberText = `${currentPageNum}`;
        }

        return (
          <div
            className="flex h-full w-full items-center justify-center"
            style={{
              fontSize: `${component.fontSize}px`,
              color: component.fontColor,
              fontWeight: component.fontWeight,
              textAlign: component.textAlign as "left" | "center" | "right",
            }}
          >
            {pageNumberText}
          </div>
        );

      case "card":
        // 카드 컴포넌트: 제목 + 항목 목록
        const cardTitle = component.cardTitle || "정보 카드";
        const cardItems = component.cardItems || [];
        const labelWidth = component.labelWidth || 80;
        const showCardTitle = component.showCardTitle !== false;
        const titleFontSize = component.titleFontSize || 14;
        const labelFontSize = component.labelFontSize || 13;
        const valueFontSize = component.valueFontSize || 13;
        const titleColor = component.titleColor || "#1e40af";
        const labelColor = component.labelColor || "#374151";
        const valueColor = component.valueColor || "#000000";

        // 쿼리 바인딩된 값 가져오기
        const getCardItemValue = (item: { label: string; value: string; fieldName?: string }) => {
          if (item.fieldName && component.queryId) {
            const queryResult = getQueryResult(component.queryId);
            if (queryResult && queryResult.rows && queryResult.rows.length > 0) {
              const row = queryResult.rows[0];
              return row[item.fieldName] !== undefined ? String(row[item.fieldName]) : item.value;
            }
          }
          return item.value;
        };

        return (
          <div className="flex h-full w-full flex-col overflow-hidden">
            {/* 제목 */}
            {showCardTitle && (
              <>
                <div
                  className="flex-shrink-0 px-2 py-1 font-semibold"
                  style={{
                    fontSize: `${titleFontSize}px`,
                    color: titleColor,
                  }}
                >
                  {cardTitle}
                </div>
                {/* 구분선 */}
                <div
                  className="mx-1 flex-shrink-0 border-b"
                  style={{ borderColor: component.borderColor || "#e5e7eb" }}
                />
              </>
            )}
            {/* 항목 목록 */}
            <div className="flex-1 overflow-auto px-2 py-1">
              {cardItems.map((item: { label: string; value: string; fieldName?: string }, index: number) => (
                <div key={index} className="flex py-0.5">
                  <span
                    className="flex-shrink-0 font-medium"
                    style={{
                      width: `${labelWidth}px`,
                      fontSize: `${labelFontSize}px`,
                      color: labelColor,
                    }}
                  >
                    {item.label}
                  </span>
                  <span
                    className="flex-1"
                    style={{
                      fontSize: `${valueFontSize}px`,
                      color: valueColor,
                    }}
                  >
                    {getCardItemValue(item)}
                  </span>
                </div>
              ))}
            </div>
          </div>
        );

      case "calculation":
        // 계산 컴포넌트
        const calcItems = component.calcItems || [];
        const resultLabel = component.resultLabel || "합계";
        const calcLabelWidth = component.labelWidth || 120;
        const calcLabelFontSize = component.labelFontSize || 13;
        const calcValueFontSize = component.valueFontSize || 13;
        const calcResultFontSize = component.resultFontSize || 16;
        const calcLabelColor = component.labelColor || "#374151";
        const calcValueColor = component.valueColor || "#000000";
        const calcResultColor = component.resultColor || "#2563eb";
        const numberFormat = component.numberFormat || "currency";
        const currencySuffix = component.currencySuffix || "원";

        // 숫자 포맷팅 함수
        const formatNumber = (num: number): string => {
          if (numberFormat === "none") return String(num);
          if (numberFormat === "comma") return num.toLocaleString();
          if (numberFormat === "currency") return num.toLocaleString() + currencySuffix;
          return String(num);
        };

        // 쿼리 바인딩된 값 가져오기
        const getCalcItemValue = (item: {
          label: string;
          value: number | string;
          operator: string;
          fieldName?: string;
        }): number => {
          if (item.fieldName && component.queryId) {
            const queryResult = getQueryResult(component.queryId);
            if (queryResult && queryResult.rows && queryResult.rows.length > 0) {
              const row = queryResult.rows[0];
              const val = row[item.fieldName];
              return typeof val === "number" ? val : parseFloat(String(val)) || 0;
            }
          }
          return typeof item.value === "number" ? item.value : parseFloat(String(item.value)) || 0;
        };

        // 계산 결과 (첫 번째 항목은 기준값, 두 번째부터 연산자 적용)
        const calculateResult = (): number => {
          if (calcItems.length === 0) return 0;
          
          // 첫 번째 항목은 기준값
          let result = getCalcItemValue(
            calcItems[0] as { label: string; value: number | string; operator: string; fieldName?: string },
          );
          
          // 두 번째 항목부터 연산자 적용
          for (let i = 1; i < calcItems.length; i++) {
            const item = calcItems[i];
            const val = getCalcItemValue(
              item as { label: string; value: number | string; operator: string; fieldName?: string },
            );
            switch (item.operator) {
              case "+":
                result += val;
                break;
              case "-":
                result -= val;
                break;
              case "x":
                result *= val;
                break;
              case "÷":
                result = val !== 0 ? result / val : result;
                break;
            }
          }
          return result;
        };

        const calcResult = calculateResult();

        return (
          <div className="flex h-full w-full flex-col overflow-hidden">
            {/* 항목 목록 */}
            <div className="flex-1 overflow-auto px-2 py-1">
              {calcItems.map(
                (
                  item: { label: string; value: number | string; operator: string; fieldName?: string },
                  index: number,
                ) => {
                const itemValue = getCalcItemValue(item);
                return (
                  <div key={index} className="flex items-center justify-between py-1">
                    <span
                      className="flex-shrink-0"
                      style={{
                        width: `${calcLabelWidth}px`,
                        fontSize: `${calcLabelFontSize}px`,
                        color: calcLabelColor,
                      }}
                    >
                      {item.label}
                    </span>
                    <span
                      className="text-right"
                      style={{
                        fontSize: `${calcValueFontSize}px`,
                        color: calcValueColor,
                      }}
                    >
                      {formatNumber(itemValue)}
                    </span>
                  </div>
                );
                },
              )}
            </div>
            {/* 구분선 */}
            <div className="mx-1 flex-shrink-0 border-t" style={{ borderColor: component.borderColor || "#374151" }} />
            {/* 결과 */}
            <div className="flex items-center justify-between px-2 py-2">
              <span
                className="font-semibold"
                style={{
                  width: `${calcLabelWidth}px`,
                  fontSize: `${calcResultFontSize}px`,
                  color: calcLabelColor,
                }}
              >
                {resultLabel}
              </span>
              <span
                className="text-right font-bold"
                style={{
                  fontSize: `${calcResultFontSize}px`,
                  color: calcResultColor,
                }}
              >
                {formatNumber(calcResult)}
              </span>
            </div>
          </div>
        );

      case "barcode":
        // 바코드/QR코드 컴포넌트 렌더링
        const barcodeType = component.barcodeType || "CODE128";
        const showBarcodeText = component.showBarcodeText !== false;
        const barcodeColor = component.barcodeColor || "#000000";
        const barcodeBackground = component.barcodeBackground || "transparent";
        const barcodeMargin = component.barcodeMargin ?? 10;
        const qrErrorLevel = component.qrErrorCorrectionLevel || "M";

        // 바코드 값 결정 (쿼리 바인딩 또는 고정값)
        const getBarcodeValue = (): string => {
          // QR코드 다중 필드 모드
          if (
            barcodeType === "QR" &&
            component.qrUseMultiField &&
            component.qrDataFields &&
            component.qrDataFields.length > 0 &&
            component.queryId
          ) {
            const queryResult = getQueryResult(component.queryId);
            if (queryResult && queryResult.rows && queryResult.rows.length > 0) {
              // 모든 행 포함 모드
              if (component.qrIncludeAllRows) {
                const allRowsData: Record<string, string>[] = [];
                queryResult.rows.forEach((row) => {
                  const rowData: Record<string, string> = {};
                  component.qrDataFields!.forEach((field) => {
                    if (field.fieldName && field.label) {
                      const val = row[field.fieldName];
                      rowData[field.label] = val !== null && val !== undefined ? String(val) : "";
                    }
                  });
                  allRowsData.push(rowData);
                });
                return JSON.stringify(allRowsData);
              }

              // 단일 행 (첫 번째 행만)
              const row = queryResult.rows[0];
              const jsonData: Record<string, string> = {};
              component.qrDataFields.forEach((field) => {
                if (field.fieldName && field.label) {
                  const val = row[field.fieldName];
                  jsonData[field.label] = val !== null && val !== undefined ? String(val) : "";
                }
              });
              return JSON.stringify(jsonData);
            }
            // 쿼리 결과가 없으면 플레이스홀더 표시
            const placeholderData: Record<string, string> = {};
            component.qrDataFields.forEach((field) => {
              if (field.label) {
                placeholderData[field.label] = `{${field.fieldName || "field"}}`;
              }
            });
            return component.qrIncludeAllRows
              ? JSON.stringify([placeholderData, { "...": "..." }])
              : JSON.stringify(placeholderData);
          }

          // 단일 필드 바인딩
          if (component.barcodeFieldName && component.queryId) {
            const queryResult = getQueryResult(component.queryId);
            if (queryResult && queryResult.rows && queryResult.rows.length > 0) {
              // QR코드 + 모든 행 포함
              if (barcodeType === "QR" && component.qrIncludeAllRows) {
                const allValues = queryResult.rows
                  .map((row) => {
                    const val = row[component.barcodeFieldName!];
                    return val !== null && val !== undefined ? String(val) : "";
                  })
                  .filter((v) => v !== "");
                return JSON.stringify(allValues);
              }

              // 단일 행 (첫 번째 행만)
              const row = queryResult.rows[0];
              const val = row[component.barcodeFieldName];
              if (val !== null && val !== undefined) {
                return String(val);
              }
            }
            // 플레이스홀더
            if (barcodeType === "QR" && component.qrIncludeAllRows) {
              return JSON.stringify([`{${component.barcodeFieldName}}`, "..."]);
            }
            return `{${component.barcodeFieldName}}`;
          }
          return component.barcodeValue || "SAMPLE123";
        };

        const barcodeValue = getBarcodeValue();
        const isQR = barcodeType === "QR";

        return (
          <div
            className="flex h-full w-full items-center justify-center overflow-hidden"
            style={{ backgroundColor: barcodeBackground }}
          >
            {isQR ? (
              <QRCodeRenderer
                value={barcodeValue}
                size={Math.min(component.width, component.height) - 10}
                fgColor={barcodeColor}
                bgColor={barcodeBackground}
                level={qrErrorLevel}
              />
            ) : (
              <BarcodeRenderer
                value={barcodeValue}
                format={barcodeType}
                width={component.width}
                height={component.height}
                displayValue={showBarcodeText}
                lineColor={barcodeColor}
                background={barcodeBackground}
                margin={barcodeMargin}
              />
            )}
          </div>
        );

      case "checkbox":
        // 체크박스 컴포넌트 렌더링
        const checkboxSize = component.checkboxSize || 18;
        const checkboxColor = component.checkboxColor || "#2563eb";
        const checkboxBorderColor = component.checkboxBorderColor || "#6b7280";
        const checkboxLabelPosition = component.checkboxLabelPosition || "right";
        const checkboxLabel = component.checkboxLabel || "";

        // 체크 상태 결정 (쿼리 바인딩 또는 고정값)
        const getCheckboxValue = (): boolean => {
          if (component.checkboxFieldName && component.queryId) {
            const queryResult = getQueryResult(component.queryId);
            if (queryResult && queryResult.rows && queryResult.rows.length > 0) {
              const row = queryResult.rows[0];
              const val = row[component.checkboxFieldName];
              // truthy/falsy 값 판정
              if (val === true || val === "true" || val === "Y" || val === 1 || val === "1") {
                return true;
              }
              return false;
            }
            return false;
          }
          return component.checkboxChecked === true;
        };

        const isChecked = getCheckboxValue();

        return (
          <div
            className={`flex h-full w-full items-center gap-2 ${
              checkboxLabelPosition === "left" ? "flex-row-reverse justify-end" : ""
            }`}
          >
            {/* 체크박스 */}
            <div
              className="flex items-center justify-center rounded-sm border-2 transition-colors"
              style={{
                width: `${checkboxSize}px`,
                height: `${checkboxSize}px`,
                borderColor: isChecked ? checkboxColor : checkboxBorderColor,
                backgroundColor: isChecked ? checkboxColor : "transparent",
              }}
            >
              {isChecked && (
                <svg
                  viewBox="0 0 24 24"
                  fill="none"
                  stroke="white"
                  strokeWidth="3"
                  strokeLinecap="round"
                  strokeLinejoin="round"
                  style={{
                    width: `${checkboxSize * 0.7}px`,
                    height: `${checkboxSize * 0.7}px`,
                  }}
                >
                  <polyline points="20 6 9 17 4 12" />
                </svg>
              )}
            </div>
            {/* 레이블 */}
            {/* 레이블 */}
            {checkboxLabel && (
              <span
                style={{
                  fontSize: `${component.fontSize || 14}px`,
                  color: component.fontColor || "#374151",
                }}
              >
                {checkboxLabel}
              </span>
            )}
          </div>
        );

      default:
        return <div>알 수 없는 컴포넌트</div>;
    }
  };

  return (
    <div
      ref={componentRef}
      className={`absolute ${component.type === "divider" ? "p-0" : "p-2"} shadow-sm ${isLocked ? "cursor-not-allowed opacity-80" : "cursor-move"} ${
        isSelected
          ? isLocked
            ? "ring-2 ring-destructive"
            : "ring-2 ring-ring"
          : isMultiSelected
            ? isLocked
              ? "ring-2 ring-red-300"
              : "ring-2 ring-blue-300"
            : ""
      }`}
      style={{
        left: `${component.x}px`,
        top: `${component.y}px`,
        width: `${component.width}px`,
        height: `${component.height}px`,
        zIndex: component.zIndex,
        backgroundColor: component.backgroundColor,
        border: component.borderWidth
          ? `${component.borderWidth}px solid ${component.borderColor}`
          : "1px solid #e5e7eb",
      }}
      onMouseDown={handleMouseDown}
      onDoubleClick={handleDoubleClick}
    >
      {renderContent()}

      {/* 잠금 표시 */}
      {isLocked && (
        <div className="absolute top-1 right-1 rounded bg-destructive px-1 py-0.5 text-[10px] text-white">🔒</div>
      )}

      {/* 그룹화 표시 */}
      {isGrouped && !isLocked && (
        <div className="absolute top-1 left-1 rounded bg-purple-500 px-1 py-0.5 text-[10px] text-white">👥</div>
      )}

      {/* 리사이즈 핸들 (선택된 경우만, 잠금 안 된 경우만) */}
      {isSelected && !isLocked && (
        <div
          className={`resize-handle absolute h-3 w-3 rounded-full bg-primary ${
            component.type === "divider"
              ? component.orientation === "vertical"
                ? "bottom-0 left-1/2 cursor-s-resize" // 세로 구분선: 하단 중앙
                : "top-1/2 right-0 cursor-e-resize" // 가로 구분선: 우측 중앙
              : "right-0 bottom-0 cursor-se-resize" // 일반 컴포넌트: 우하단
          }`}
          style={{
            transform:
              component.type === "divider"
                ? component.orientation === "vertical"
                  ? "translate(-50%, 50%)" // 세로 구분선
                  : "translate(50%, -50%)" // 가로 구분선
                : "translate(50%, 50%)", // 일반 컴포넌트
          }}
          onMouseDown={handleResizeStart}
        />
      )}
    </div>
  );
}
