"use client";

import { createContext, useContext, useState, useCallback, ReactNode, useEffect, useRef } from "react";
import { ComponentConfig, ReportDetail, ReportLayout, ReportPage, ReportLayoutConfig, WatermarkConfig } from "@/types/report";
import { reportApi } from "@/lib/api/reportApi";
import { useToast } from "@/hooks/use-toast";
import { v4 as uuidv4 } from "uuid";

export interface ReportQuery {
  id: string;
  name: string;
  type: "MASTER" | "DETAIL";
  sqlQuery: string;
  parameters: string[];
  externalConnectionId?: number | null; // 외부 DB 연결 ID
}

// 하드코딩된 템플릿 제거 - 모든 템플릿은 DB에서 관리

export interface QueryResult {
  queryId: string;
  fields: string[];
  rows: Record<string, unknown>[];
}

interface ReportDesignerContextType {
  reportId: string;
  reportDetail: ReportDetail | null;
  layout: ReportLayout | null;

  // 페이지 관리
  layoutConfig: ReportLayoutConfig; // { pages: [...] }
  currentPageId: string | null;
  currentPage: ReportPage | undefined;

  // 페이지 액션
  addPage: (name?: string) => void;
  deletePage: (pageId: string) => void;
  duplicatePage: (pageId: string) => void;
  reorderPages: (sourceIndex: number, targetIndex: number) => void;
  selectPage: (pageId: string) => void;
  updatePageSettings: (pageId: string, settings: Partial<ReportPage>) => void;
  updateWatermark: (watermark: WatermarkConfig | undefined) => void; // 전체 페이지 공유 워터마크

  // 컴포넌트 (현재 페이지)
  components: ComponentConfig[]; // currentPage의 components (읽기 전용)
  selectedComponentId: string | null;
  selectedComponentIds: string[]; // 다중 선택된 컴포넌트 ID 배열
  isLoading: boolean;
  isSaving: boolean;

  // 쿼리 관리
  queries: ReportQuery[];
  setQueries: (queries: ReportQuery[]) => void;

  // 쿼리 실행 결과
  queryResults: QueryResult[];
  setQueryResult: (queryId: string, fields: string[], rows: Record<string, unknown>[]) => void;
  getQueryResult: (queryId: string) => QueryResult | null;

  // 컴포넌트 관리
  addComponent: (component: ComponentConfig) => void;
  updateComponent: (id: string, updates: Partial<ComponentConfig>) => void;
  removeComponent: (id: string) => void;
  selectComponent: (id: string | null, isMultiSelect?: boolean) => void;
  selectMultipleComponents: (ids: string[]) => void; // 여러 컴포넌트 한번에 선택

  // 레이아웃 관리
  updateLayout: (updates: Partial<ReportLayout>) => void;
  saveLayout: () => Promise<void>;
  loadLayout: () => Promise<void>;

  // 템플릿 적용
  applyTemplate: (templateId: string) => void;

  // 캔버스 설정
  canvasWidth: number;
  canvasHeight: number;
  pageOrientation: string;
  margins: {
    top: number;
    bottom: number;
    left: number;
    right: number;
  };

  // 레이아웃 도구
  gridSize: number;
  setGridSize: (size: number) => void;
  showGrid: boolean;
  setShowGrid: (show: boolean) => void;
  snapToGrid: boolean;
  setSnapToGrid: (snap: boolean) => void;
  snapValueToGrid: (value: number) => number;

  // 정렬 가이드라인
  alignmentGuides: { vertical: number[]; horizontal: number[] };
  calculateAlignmentGuides: (draggingId: string, x: number, y: number, width: number, height: number) => void;
  clearAlignmentGuides: () => void;

  // 복사/붙여넣기
  copyComponents: () => void;
  pasteComponents: () => void;
  duplicateComponents: () => void; // Ctrl+D 즉시 복제
  copyStyles: () => void; // Ctrl+Shift+C 스타일만 복사
  pasteStyles: () => void; // Ctrl+Shift+V 스타일만 붙여넣기
  duplicateAtPosition: (componentIds: string[], offsetX?: number, offsetY?: number) => string[]; // Alt+드래그 복제용
  fitSelectedToContent: () => void; // Ctrl+Shift+F 텍스트 크기 자동 맞춤

  // Undo/Redo
  undo: () => void;
  redo: () => void;
  canUndo: boolean;
  canRedo: boolean;

  // 정렬 기능
  alignLeft: () => void;
  alignRight: () => void;
  alignTop: () => void;
  alignBottom: () => void;
  alignCenterHorizontal: () => void;
  alignCenterVertical: () => void;
  distributeHorizontal: () => void;
  distributeVertical: () => void;
  makeSameWidth: () => void;
  makeSameHeight: () => void;
  makeSameSize: () => void;

  // 레이어 관리
  bringToFront: () => void;
  sendToBack: () => void;
  bringForward: () => void;
  sendBackward: () => void;

  // 잠금 관리
  toggleLock: () => void;
  lockComponents: () => void;
  unlockComponents: () => void;

  // 눈금자 표시
  showRuler: boolean;
  setShowRuler: (show: boolean) => void;

  // 그룹화
  groupComponents: () => void;
  ungroupComponents: () => void;

  // 메뉴 연결
  menuObjids: number[];
  setMenuObjids: (menuObjids: number[]) => void;
  saveLayoutWithMenus: (menuObjids: number[]) => Promise<void>;
}

const ReportDesignerContext = createContext<ReportDesignerContextType | undefined>(undefined);

// 페이지 사이즈 변경 시 컴포넌트 위치 및 크기 재계산 유틸리티 함수
const recalculateComponentPositions = (
  components: ComponentConfig[],
  oldWidth: number,
  oldHeight: number,
  newWidth: number,
  newHeight: number
): ComponentConfig[] => {
  // 사이즈가 동일하면 그대로 반환
  if (oldWidth === newWidth && oldHeight === newHeight) {
    return components;
  }

  const widthRatio = newWidth / oldWidth;
  const heightRatio = newHeight / oldHeight;

  return components.map((comp) => {
    // 위치와 크기 모두 비율대로 재계산
    // 소수점 2자리까지만 유지
    const newX = Math.round(comp.x * widthRatio * 100) / 100;
    const newY = Math.round(comp.y * heightRatio * 100) / 100;
    const newCompWidth = Math.round(comp.width * widthRatio * 100) / 100;
    const newCompHeight = Math.round(comp.height * heightRatio * 100) / 100;

    return {
      ...comp,
      x: newX,
      y: newY,
      width: newCompWidth,
      height: newCompHeight,
    };
  });
};

export function ReportDesignerProvider({ reportId, children }: { reportId: string; children: ReactNode }) {
  const [reportDetail, setReportDetail] = useState<ReportDetail | null>(null);
  const [layout, setLayout] = useState<ReportLayout | null>(null);

  // 페이지 기반 레이아웃
  const [layoutConfig, setLayoutConfig] = useState<ReportLayoutConfig>({
    pages: [],
  });
  const [currentPageId, setCurrentPageId] = useState<string | null>(null);

  const [queries, setQueries] = useState<ReportQuery[]>([]);
  const [queryResults, setQueryResults] = useState<QueryResult[]>([]);
  const [selectedComponentId, setSelectedComponentId] = useState<string | null>(null);
  const [selectedComponentIds, setSelectedComponentIds] = useState<string[]>([]); // 다중 선택
  const [isLoading, setIsLoading] = useState(true);
  const [isSaving, setIsSaving] = useState(false);
  const [menuObjids, setMenuObjids] = useState<number[]>([]); // 연결된 메뉴 ID 목록
  const { toast } = useToast();

  // 현재 페이지 계산
  const currentPage = layoutConfig.pages.find((p) => p.page_id === currentPageId);

  // 현재 페이지의 컴포넌트 (읽기 전용) - 배열인지 확인
  const components = Array.isArray(currentPage?.components) ? currentPage.components : [];

  // currentPageId를 ref로 저장하여 클로저 문제 해결
  const currentPageIdRef = useRef<string | null>(currentPageId);
  useEffect(() => {
    currentPageIdRef.current = currentPageId;
  }, [currentPageId]);

  // 현재 페이지의 컴포넌트를 업데이트하는 헬퍼 함수
  const setComponents = useCallback(
    (updater: ComponentConfig[] | ((prev: ComponentConfig[]) => ComponentConfig[])) => {
      setLayoutConfig((prev) => {
        const pageId = currentPageIdRef.current;
        if (!pageId) {
          console.error("❌ currentPageId가 없음");
          return prev;
        }

        // 현재 선택된 페이지 찾기
        const currentPageIndex = prev.pages.findIndex((p) => p.page_id === pageId);
        if (currentPageIndex === -1) {
          console.error("❌ 페이지를 찾을 수 없음:", pageId);
          return prev;
        }

        const currentPageData = prev.pages[currentPageIndex];
        const newComponents = typeof updater === "function" ? updater(currentPageData.components) : updater;

        const newPages = [...prev.pages];
        newPages[currentPageIndex] = {
          ...currentPageData,
          components: newComponents,
        };

        console.log("✅ 컴포넌트 업데이트:", {
          pageId,
          before: currentPageData.components.length,
          after: newComponents.length,
        });

        return { pages: newPages };
      });
    },
    [], // ref를 사용하므로 의존성 배열 비움
  );

  // 레이아웃 도구 설정
  const [gridSize, setGridSize] = useState(10); // Grid Snap 크기 (px)
  const [showGrid, setShowGrid] = useState(true); // Grid 표시 여부
  const [snapToGrid, setSnapToGrid] = useState(true); // Grid Snap 활성화

  // 눈금자 표시
  const [showRuler, setShowRuler] = useState(true);

  // 정렬 가이드라인
  const [alignmentGuides, setAlignmentGuides] = useState<{
    vertical: number[];
    horizontal: number[];
  }>({ vertical: [], horizontal: [] });

  // 클립보드 (복사/붙여넣기)
  const [clipboard, setClipboard] = useState<ComponentConfig[]>([]);

  // 스타일 클립보드 (스타일만 복사/붙여넣기)
  const [styleClipboard, setStyleClipboard] = useState<Partial<ComponentConfig> | null>(null);

  // Undo/Redo 히스토리
  const [history, setHistory] = useState<ComponentConfig[][]>([]);
  const [historyIndex, setHistoryIndex] = useState(-1);
  const [isUndoRedoing, setIsUndoRedoing] = useState(false); // Undo/Redo 실행 중 플래그

  // Grid Snap 함수
  const snapValueToGrid = useCallback(
    (value: number): number => {
      if (!snapToGrid) return value;
      return Math.round(value / gridSize) * gridSize;
    },
    [snapToGrid, gridSize],
  );

  // 복사 (Ctrl+C)
  const copyComponents = useCallback(() => {
    if (selectedComponentIds.length > 0) {
      // 잠긴 컴포넌트는 복사에서 제외
      const componentsToCopy = components.filter(
        (comp) => selectedComponentIds.includes(comp.id) && !comp.locked
      );
      if (componentsToCopy.length === 0) {
        toast({
          title: "복사 불가",
          description: "잠긴 컴포넌트는 복사할 수 없습니다.",
          variant: "destructive",
        });
        return;
      }
      setClipboard(componentsToCopy);
      toast({
        title: "복사 완료",
        description: `${componentsToCopy.length}개의 컴포넌트가 복사되었습니다.`,
      });
    } else if (selectedComponentId) {
      const componentToCopy = components.find((comp) => comp.id === selectedComponentId);
      if (componentToCopy) {
        // 잠긴 컴포넌트는 복사 불가
        if (componentToCopy.locked) {
          toast({
            title: "복사 불가",
            description: "잠긴 컴포넌트는 복사할 수 없습니다.",
            variant: "destructive",
          });
          return;
        }
        setClipboard([componentToCopy]);
        toast({
          title: "복사 완료",
          description: "컴포넌트가 복사되었습니다.",
        });
      }
    }
  }, [selectedComponentId, selectedComponentIds, components, toast]);

  // 붙여넣기 (Ctrl+V)
  const pasteComponents = useCallback(() => {
    if (clipboard.length === 0) return;

    const newComponents = clipboard.map((comp) => ({
      ...comp,
      id: `comp_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`,
      x: comp.x + 20, // 약간 오프셋
      y: comp.y + 20,
      zIndex: components.length,
    }));

    // setComponents를 직접 사용
    setComponents((prev) => [...prev, ...newComponents]);

    // 새로 생성된 컴포넌트 선택
    if (newComponents.length === 1) {
      setSelectedComponentId(newComponents[0].id);
      setSelectedComponentIds([newComponents[0].id]);
    } else {
      setSelectedComponentIds(newComponents.map((c) => c.id));
      setSelectedComponentId(newComponents[0].id);
    }

    toast({
      title: "붙여넣기 완료",
      description: `${newComponents.length}개의 컴포넌트가 추가되었습니다.`,
    });
  }, [clipboard, components.length, toast]);

  // 복제 (Ctrl+D) - 선택된 컴포넌트를 즉시 복제
  const duplicateComponents = useCallback(() => {
    // 복제할 컴포넌트 결정
    let componentsToDuplicate: ComponentConfig[] = [];
    
    if (selectedComponentIds.length > 0) {
      componentsToDuplicate = components.filter(
        (comp) => selectedComponentIds.includes(comp.id) && !comp.locked
      );
    } else if (selectedComponentId) {
      const comp = components.find((c) => c.id === selectedComponentId);
      if (comp && !comp.locked) {
        componentsToDuplicate = [comp];
      }
    }

    if (componentsToDuplicate.length === 0) {
      toast({
        title: "복제 불가",
        description: "복제할 컴포넌트가 없거나 잠긴 컴포넌트입니다.",
        variant: "destructive",
      });
      return;
    }

    const newComponents = componentsToDuplicate.map((comp) => ({
      ...comp,
      id: `comp_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`,
      x: comp.x + 20,
      y: comp.y + 20,
      zIndex: components.length,
      locked: false, // 복제된 컴포넌트는 잠금 해제
    }));

    setComponents((prev) => [...prev, ...newComponents]);

    // 복제된 컴포넌트 선택
    if (newComponents.length === 1) {
      setSelectedComponentId(newComponents[0].id);
      setSelectedComponentIds([newComponents[0].id]);
    } else {
      setSelectedComponentIds(newComponents.map((c) => c.id));
      setSelectedComponentId(newComponents[0].id);
    }

    toast({
      title: "복제 완료",
      description: `${newComponents.length}개의 컴포넌트가 복제되었습니다.`,
    });
  }, [selectedComponentId, selectedComponentIds, components, toast]);

  // 스타일 복사 (Ctrl+Shift+C)
  const copyStyles = useCallback(() => {
    // 단일 컴포넌트만 스타일 복사 가능
    const targetId = selectedComponentId || selectedComponentIds[0];
    if (!targetId) {
      toast({
        title: "스타일 복사 불가",
        description: "컴포넌트를 선택해주세요.",
        variant: "destructive",
      });
      return;
    }

    const component = components.find((c) => c.id === targetId);
    if (!component) return;

    // 스타일 관련 속성만 추출
    const styleProperties: Partial<ComponentConfig> = {
      fontSize: component.fontSize,
      fontColor: component.fontColor,
      fontWeight: component.fontWeight,
      fontFamily: component.fontFamily,
      textAlign: component.textAlign,
      backgroundColor: component.backgroundColor,
      borderWidth: component.borderWidth,
      borderColor: component.borderColor,
      borderStyle: component.borderStyle,
      borderRadius: component.borderRadius,
      boxShadow: component.boxShadow,
      opacity: component.opacity,
      padding: component.padding,
      letterSpacing: component.letterSpacing,
      lineHeight: component.lineHeight,
    };

    // undefined 값 제거
    Object.keys(styleProperties).forEach((key) => {
      if (styleProperties[key as keyof typeof styleProperties] === undefined) {
        delete styleProperties[key as keyof typeof styleProperties];
      }
    });

    setStyleClipboard(styleProperties);
    toast({
      title: "스타일 복사 완료",
      description: "스타일이 복사되었습니다. Ctrl+Shift+V로 적용할 수 있습니다.",
    });
  }, [selectedComponentId, selectedComponentIds, components, toast]);

  // 스타일 붙여넣기 (Ctrl+Shift+V)
  const pasteStyles = useCallback(() => {
    if (!styleClipboard) {
      toast({
        title: "스타일 붙여넣기 불가",
        description: "먼저 Ctrl+Shift+C로 스타일을 복사해주세요.",
        variant: "destructive",
      });
      return;
    }

    // 선택된 컴포넌트들에 스타일 적용
    const targetIds =
      selectedComponentIds.length > 0
        ? selectedComponentIds
        : selectedComponentId
          ? [selectedComponentId]
          : [];

    if (targetIds.length === 0) {
      toast({
        title: "스타일 붙여넣기 불가",
        description: "스타일을 적용할 컴포넌트를 선택해주세요.",
        variant: "destructive",
      });
      return;
    }

    // 잠긴 컴포넌트 필터링
    const applicableIds = targetIds.filter((id) => {
      const comp = components.find((c) => c.id === id);
      return comp && !comp.locked;
    });

    if (applicableIds.length === 0) {
      toast({
        title: "스타일 붙여넣기 불가",
        description: "잠긴 컴포넌트에는 스타일을 적용할 수 없습니다.",
        variant: "destructive",
      });
      return;
    }

    setComponents((prev) =>
      prev.map((comp) => {
        if (applicableIds.includes(comp.id)) {
          return { ...comp, ...styleClipboard };
        }
        return comp;
      })
    );

    toast({
      title: "스타일 적용 완료",
      description: `${applicableIds.length}개의 컴포넌트에 스타일이 적용되었습니다.`,
    });
  }, [styleClipboard, selectedComponentId, selectedComponentIds, components, toast]);

  // Alt+드래그 복제용: 지정된 위치에 컴포넌트 복제
  const duplicateAtPosition = useCallback(
    (componentIds: string[], offsetX: number = 0, offsetY: number = 0): string[] => {
      const componentsToDuplicate = components.filter(
        (comp) => componentIds.includes(comp.id) && !comp.locked
      );

      if (componentsToDuplicate.length === 0) return [];

      const newComponents = componentsToDuplicate.map((comp) => ({
        ...comp,
        id: `comp_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`,
        x: comp.x + offsetX,
        y: comp.y + offsetY,
        zIndex: components.length,
        locked: false,
      }));

      setComponents((prev) => [...prev, ...newComponents]);

      return newComponents.map((c) => c.id);
    },
    [components]
  );

  // 히스토리에 현재 상태 저장
  const saveToHistory = useCallback(
    (newComponents: ComponentConfig[]) => {
      setHistory((prev) => {
        // 현재 인덱스 이후의 히스토리 제거 (새 분기 시작)
        const newHistory = prev.slice(0, historyIndex + 1);
        // 새 상태 추가 (최대 50개까지만 유지)
        newHistory.push(JSON.parse(JSON.stringify(newComponents)));
        return newHistory.slice(-50);
      });
      setHistoryIndex((prev) => Math.min(prev + 1, 49));
    },
    [historyIndex],
  );

  // Undo
  const undo = useCallback(() => {
    if (historyIndex > 0) {
      setIsUndoRedoing(true);
      setHistoryIndex((prev) => prev - 1);
      setComponents(JSON.parse(JSON.stringify(history[historyIndex - 1])));
      setTimeout(() => setIsUndoRedoing(false), 100);
      toast({
        title: "실행 취소",
        description: "이전 상태로 되돌렸습니다.",
      });
    }
  }, [historyIndex, history, toast]);

  // Redo
  const redo = useCallback(() => {
    if (historyIndex < history.length - 1) {
      setIsUndoRedoing(true);
      setHistoryIndex((prev) => prev + 1);
      setComponents(JSON.parse(JSON.stringify(history[historyIndex + 1])));
      setTimeout(() => setIsUndoRedoing(false), 100);
      toast({
        title: "다시 실행",
        description: "다음 상태로 이동했습니다.",
      });
    }
  }, [historyIndex, history, toast]);

  // 정렬 함수들 (선택된 컴포넌트들 기준)
  const getSelectedComponents = useCallback(() => {
    return components.filter((c) => selectedComponentIds.includes(c.id));
  }, [components, selectedComponentIds]);

  // 왼쪽 정렬
  const alignLeft = useCallback(() => {
    const selected = getSelectedComponents();
    if (selected.length < 2) return;

    const minX = Math.min(...selected.map((c) => c.x));
    const updates = selected.map((c) => ({ ...c, x: minX }));

    setComponents((prev) =>
      prev.map((c) => {
        const update = updates.find((u) => u.id === c.id);
        return update || c;
      }),
    );

    toast({ title: "정렬 완료", description: "왼쪽 정렬되었습니다." });
  }, [getSelectedComponents, toast]);

  // 오른쪽 정렬
  const alignRight = useCallback(() => {
    const selected = getSelectedComponents();
    if (selected.length < 2) return;

    const maxRight = Math.max(...selected.map((c) => c.x + c.width));
    const updates = selected.map((c) => ({ ...c, x: maxRight - c.width }));

    setComponents((prev) =>
      prev.map((c) => {
        const update = updates.find((u) => u.id === c.id);
        return update || c;
      }),
    );

    toast({ title: "정렬 완료", description: "오른쪽 정렬되었습니다." });
  }, [getSelectedComponents, toast]);

  // 위쪽 정렬
  const alignTop = useCallback(() => {
    const selected = getSelectedComponents();
    if (selected.length < 2) return;

    const minY = Math.min(...selected.map((c) => c.y));
    const updates = selected.map((c) => ({ ...c, y: minY }));

    setComponents((prev) =>
      prev.map((c) => {
        const update = updates.find((u) => u.id === c.id);
        return update || c;
      }),
    );

    toast({ title: "정렬 완료", description: "위쪽 정렬되었습니다." });
  }, [getSelectedComponents, toast]);

  // 아래쪽 정렬
  const alignBottom = useCallback(() => {
    const selected = getSelectedComponents();
    if (selected.length < 2) return;

    const maxBottom = Math.max(...selected.map((c) => c.y + c.height));
    const updates = selected.map((c) => ({ ...c, y: maxBottom - c.height }));

    setComponents((prev) =>
      prev.map((c) => {
        const update = updates.find((u) => u.id === c.id);
        return update || c;
      }),
    );

    toast({ title: "정렬 완료", description: "아래쪽 정렬되었습니다." });
  }, [getSelectedComponents, toast]);

  // 가로 중앙 정렬
  const alignCenterHorizontal = useCallback(() => {
    const selected = getSelectedComponents();
    if (selected.length < 2) return;

    const minX = Math.min(...selected.map((c) => c.x));
    const maxRight = Math.max(...selected.map((c) => c.x + c.width));
    const centerX = (minX + maxRight) / 2;

    const updates = selected.map((c) => ({ ...c, x: centerX - c.width / 2 }));

    setComponents((prev) =>
      prev.map((c) => {
        const update = updates.find((u) => u.id === c.id);
        return update || c;
      }),
    );

    toast({ title: "정렬 완료", description: "가로 중앙 정렬되었습니다." });
  }, [getSelectedComponents, toast]);

  // 세로 중앙 정렬
  const alignCenterVertical = useCallback(() => {
    const selected = getSelectedComponents();
    if (selected.length < 2) return;

    const minY = Math.min(...selected.map((c) => c.y));
    const maxBottom = Math.max(...selected.map((c) => c.y + c.height));
    const centerY = (minY + maxBottom) / 2;

    const updates = selected.map((c) => ({ ...c, y: centerY - c.height / 2 }));

    setComponents((prev) =>
      prev.map((c) => {
        const update = updates.find((u) => u.id === c.id);
        return update || c;
      }),
    );

    toast({ title: "정렬 완료", description: "세로 중앙 정렬되었습니다." });
  }, [getSelectedComponents, toast]);

  // 가로 균등 배치
  const distributeHorizontal = useCallback(() => {
    const selected = getSelectedComponents();
    if (selected.length < 3) return;

    const sorted = [...selected].sort((a, b) => a.x - b.x);
    const minX = sorted[0].x;
    const maxX = sorted[sorted.length - 1].x + sorted[sorted.length - 1].width;
    const totalWidth = sorted.reduce((sum, c) => sum + c.width, 0);
    const totalGap = maxX - minX - totalWidth;
    const gap = totalGap / (sorted.length - 1);

    let currentX = minX;
    const updates = sorted.map((c) => {
      const newC = { ...c, x: currentX };
      currentX += c.width + gap;
      return newC;
    });

    setComponents((prev) =>
      prev.map((c) => {
        const update = updates.find((u) => u.id === c.id);
        return update || c;
      }),
    );

    toast({ title: "정렬 완료", description: "가로 균등 배치되었습니다." });
  }, [getSelectedComponents, toast]);

  // 세로 균등 배치
  const distributeVertical = useCallback(() => {
    const selected = getSelectedComponents();
    if (selected.length < 3) return;

    const sorted = [...selected].sort((a, b) => a.y - b.y);
    const minY = sorted[0].y;
    const maxY = sorted[sorted.length - 1].y + sorted[sorted.length - 1].height;
    const totalHeight = sorted.reduce((sum, c) => sum + c.height, 0);
    const totalGap = maxY - minY - totalHeight;
    const gap = totalGap / (sorted.length - 1);

    let currentY = minY;
    const updates = sorted.map((c) => {
      const newC = { ...c, y: currentY };
      currentY += c.height + gap;
      return newC;
    });

    setComponents((prev) =>
      prev.map((c) => {
        const update = updates.find((u) => u.id === c.id);
        return update || c;
      }),
    );

    toast({ title: "정렬 완료", description: "세로 균등 배치되었습니다." });
  }, [getSelectedComponents, toast]);

  // 같은 너비로
  const makeSameWidth = useCallback(() => {
    const selected = getSelectedComponents();
    if (selected.length < 2) return;

    const targetWidth = selected[0].width;
    const updates = selected.map((c) => ({ ...c, width: targetWidth }));

    setComponents((prev) =>
      prev.map((c) => {
        const update = updates.find((u) => u.id === c.id);
        return update || c;
      }),
    );

    toast({ title: "크기 조정 완료", description: "같은 너비로 조정되었습니다." });
  }, [getSelectedComponents, toast]);

  // 같은 높이로
  const makeSameHeight = useCallback(() => {
    const selected = getSelectedComponents();
    if (selected.length < 2) return;

    const targetHeight = selected[0].height;
    const updates = selected.map((c) => ({ ...c, height: targetHeight }));

    setComponents((prev) =>
      prev.map((c) => {
        const update = updates.find((u) => u.id === c.id);
        return update || c;
      }),
    );

    toast({ title: "크기 조정 완료", description: "같은 높이로 조정되었습니다." });
  }, [getSelectedComponents, toast]);

  // 같은 크기로
  const makeSameSize = useCallback(() => {
    const selected = getSelectedComponents();
    if (selected.length < 2) return;

    const targetWidth = selected[0].width;
    const targetHeight = selected[0].height;
    const updates = selected.map((c) => ({ ...c, width: targetWidth, height: targetHeight }));

    setComponents((prev) =>
      prev.map((c) => {
        const update = updates.find((u) => u.id === c.id);
        return update || c;
      }),
    );

    toast({ title: "크기 조정 완료", description: "같은 크기로 조정되었습니다." });
  }, [getSelectedComponents, toast]);

  // 레이어 관리 함수들
  const bringToFront = useCallback(() => {
    if (!selectedComponentId && selectedComponentIds.length === 0) return;

    const idsToUpdate =
      selectedComponentIds.length > 0 ? selectedComponentIds : ([selectedComponentId].filter(Boolean) as string[]);
    const maxZIndex = Math.max(...components.map((c) => c.zIndex));

    setComponents((prev) =>
      prev.map((c) => {
        if (idsToUpdate.includes(c.id)) {
          return { ...c, zIndex: maxZIndex + 1 };
        }
        return c;
      }),
    );

    toast({ title: "레이어 변경", description: "맨 앞으로 이동했습니다." });
  }, [selectedComponentId, selectedComponentIds, components, toast]);

  const sendToBack = useCallback(() => {
    if (!selectedComponentId && selectedComponentIds.length === 0) return;

    const idsToUpdate =
      selectedComponentIds.length > 0 ? selectedComponentIds : ([selectedComponentId].filter(Boolean) as string[]);
    const minZIndex = Math.min(...components.map((c) => c.zIndex));

    setComponents((prev) =>
      prev.map((c) => {
        if (idsToUpdate.includes(c.id)) {
          // zIndex는 최소 1로 제한 (0이면 캔버스 배경 뒤로 가버림)
          return { ...c, zIndex: Math.max(1, minZIndex - 1) };
        }
        return c;
      }),
    );

    toast({ title: "레이어 변경", description: "맨 뒤로 이동했습니다." });
  }, [selectedComponentId, selectedComponentIds, components, toast]);

  const bringForward = useCallback(() => {
    if (!selectedComponentId && selectedComponentIds.length === 0) return;

    const idsToUpdate =
      selectedComponentIds.length > 0 ? selectedComponentIds : ([selectedComponentId].filter(Boolean) as string[]);

    setComponents((prev) => {
      const sorted = [...prev].sort((a, b) => a.zIndex - b.zIndex);
      const updated = sorted.map((c, index) => ({ ...c, zIndex: index }));

      return updated.map((c) => {
        if (idsToUpdate.includes(c.id)) {
          return { ...c, zIndex: Math.min(c.zIndex + 1, updated.length - 1) };
        }
        return c;
      });
    });

    toast({ title: "레이어 변경", description: "한 단계 앞으로 이동했습니다." });
  }, [selectedComponentId, selectedComponentIds, toast]);

  const sendBackward = useCallback(() => {
    if (!selectedComponentId && selectedComponentIds.length === 0) return;

    const idsToUpdate =
      selectedComponentIds.length > 0 ? selectedComponentIds : ([selectedComponentId].filter(Boolean) as string[]);

    setComponents((prev) => {
      const sorted = [...prev].sort((a, b) => a.zIndex - b.zIndex);
      const updated = sorted.map((c, index) => ({ ...c, zIndex: index + 1 }));

      return updated.map((c) => {
        if (idsToUpdate.includes(c.id)) {
          // zIndex는 최소 1로 제한
          return { ...c, zIndex: Math.max(c.zIndex - 1, 1) };
        }
        return c;
      });
    });

    toast({ title: "레이어 변경", description: "한 단계 뒤로 이동했습니다." });
  }, [selectedComponentId, selectedComponentIds, toast]);

  // 잠금 관리 함수들
  const toggleLock = useCallback(() => {
    if (!selectedComponentId && selectedComponentIds.length === 0) return;

    const idsToUpdate =
      selectedComponentIds.length > 0 ? selectedComponentIds : ([selectedComponentId].filter(Boolean) as string[]);

    setComponents((prev) =>
      prev.map((c) => {
        if (idsToUpdate.includes(c.id)) {
          return { ...c, locked: !c.locked };
        }
        return c;
      }),
    );

    const isLocking = components.find((c) => idsToUpdate.includes(c.id))?.locked === false;
    toast({
      title: isLocking ? "잠금 설정" : "잠금 해제",
      description: isLocking ? "선택된 컴포넌트가 잠겼습니다." : "선택된 컴포넌트의 잠금이 해제되었습니다.",
    });
  }, [selectedComponentId, selectedComponentIds, components, toast]);

  const lockComponents = useCallback(() => {
    if (!selectedComponentId && selectedComponentIds.length === 0) return;

    const idsToUpdate =
      selectedComponentIds.length > 0 ? selectedComponentIds : ([selectedComponentId].filter(Boolean) as string[]);

    setComponents((prev) =>
      prev.map((c) => {
        if (idsToUpdate.includes(c.id)) {
          return { ...c, locked: true };
        }
        return c;
      }),
    );

    toast({ title: "잠금 설정", description: "선택된 컴포넌트가 잠겼습니다." });
  }, [selectedComponentId, selectedComponentIds, toast]);

  const unlockComponents = useCallback(() => {
    if (!selectedComponentId && selectedComponentIds.length === 0) return;

    const idsToUpdate =
      selectedComponentIds.length > 0 ? selectedComponentIds : ([selectedComponentId].filter(Boolean) as string[]);

    setComponents((prev) =>
      prev.map((c) => {
        if (idsToUpdate.includes(c.id)) {
          return { ...c, locked: false };
        }
        return c;
      }),
    );

    toast({ title: "잠금 해제", description: "선택된 컴포넌트의 잠금이 해제되었습니다." });
  }, [selectedComponentId, selectedComponentIds, toast]);

  // 그룹화 함수들
  const groupComponents = useCallback(() => {
    if (selectedComponentIds.length < 2) {
      toast({
        title: "그룹화 불가",
        description: "2개 이상의 컴포넌트를 선택해야 합니다.",
        variant: "destructive",
      });
      return;
    }

    // 새로운 그룹 ID 생성
    const newGroupId = `group_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;

    setComponents((prev) =>
      prev.map((c) => {
        if (selectedComponentIds.includes(c.id)) {
          return { ...c, groupId: newGroupId };
        }
        return c;
      }),
    );

    toast({
      title: "그룹화 완료",
      description: `${selectedComponentIds.length}개의 컴포넌트가 그룹화되었습니다.`,
    });
  }, [selectedComponentIds, toast]);

  const ungroupComponents = useCallback(() => {
    if (!selectedComponentId && selectedComponentIds.length === 0) {
      toast({
        title: "그룹 해제 불가",
        description: "그룹을 해제할 컴포넌트를 선택해주세요.",
        variant: "destructive",
      });
      return;
    }

    const idsToUngroup =
      selectedComponentIds.length > 0 ? selectedComponentIds : ([selectedComponentId].filter(Boolean) as string[]);

    // 선택된 컴포넌트들의 그룹 ID 수집
    const groupIds = new Set<string>();
    components.forEach((c) => {
      if (idsToUngroup.includes(c.id) && c.groupId) {
        groupIds.add(c.groupId);
      }
    });

    if (groupIds.size === 0) {
      toast({
        title: "그룹 해제 불가",
        description: "선택된 컴포넌트 중 그룹화된 것이 없습니다.",
        variant: "destructive",
      });
      return;
    }

    // 해당 그룹 ID를 가진 모든 컴포넌트의 그룹 해제
    setComponents((prev) =>
      prev.map((c) => {
        if (c.groupId && groupIds.has(c.groupId)) {
          // eslint-disable-next-line @typescript-eslint/no-unused-vars
          const { groupId, ...rest } = c;
          return rest as ComponentConfig;
        }
        return c;
      }),
    );

    toast({
      title: "그룹 해제 완료",
      description: `${groupIds.size}개의 그룹이 해제되었습니다.`,
    });
  }, [selectedComponentId, selectedComponentIds, components, toast]);

  // 캔버스 설정 (현재 페이지 기반)
  const canvasWidth = currentPage?.width || 210;
  const canvasHeight = currentPage?.height || 297;
  const pageOrientation = currentPage?.orientation || "portrait";
  const margins = currentPage?.margins || {
    top: 20,
    bottom: 20,
    left: 20,
    right: 20,
  };

  // 정렬 가이드라인 계산 (캔버스 중앙선 포함)
  const calculateAlignmentGuides = useCallback(
    (draggingId: string, x: number, y: number, width: number, height: number) => {
      const verticalLines: number[] = [];
      const horizontalLines: number[] = [];
      const threshold = 5; // 5px 오차 허용

      // 캔버스를 픽셀로 변환 (고정 스케일 팩터: 1mm = 4px)
      const MM_TO_PX = 4;
      const canvasWidthPx = canvasWidth * MM_TO_PX;
      const canvasHeightPx = canvasHeight * MM_TO_PX;
      const canvasCenterX = canvasWidthPx / 2;
      const canvasCenterY = canvasHeightPx / 2;

      // 드래그 중인 컴포넌트의 주요 위치
      const left = x;
      const right = x + width;
      const centerX = x + width / 2;
      const top = y;
      const bottom = y + height;
      const centerY = y + height / 2;

      // 캔버스 중앙선 체크
      if (Math.abs(centerX - canvasCenterX) < threshold) {
        verticalLines.push(canvasCenterX);
      }
      if (Math.abs(centerY - canvasCenterY) < threshold) {
        horizontalLines.push(canvasCenterY);
      }

      // 다른 컴포넌트들과 비교
      components.forEach((comp) => {
        if (comp.id === draggingId) return;

        const compLeft = comp.x;
        const compRight = comp.x + comp.width;
        const compCenterX = comp.x + comp.width / 2;
        const compTop = comp.y;
        const compBottom = comp.y + comp.height;
        const compCenterY = comp.y + comp.height / 2;

        // 세로 정렬 체크 (left, center, right) - 오차 허용
        if (Math.abs(left - compLeft) < threshold) verticalLines.push(compLeft);
        if (Math.abs(left - compRight) < threshold) verticalLines.push(compRight);
        if (Math.abs(right - compLeft) < threshold) verticalLines.push(compLeft);
        if (Math.abs(right - compRight) < threshold) verticalLines.push(compRight);
        if (Math.abs(centerX - compCenterX) < threshold) verticalLines.push(compCenterX);

        // 가로 정렬 체크 (top, center, bottom) - 오차 허용
        if (Math.abs(top - compTop) < threshold) horizontalLines.push(compTop);
        if (Math.abs(top - compBottom) < threshold) horizontalLines.push(compBottom);
        if (Math.abs(bottom - compTop) < threshold) horizontalLines.push(compTop);
        if (Math.abs(bottom - compBottom) < threshold) horizontalLines.push(compBottom);
        if (Math.abs(centerY - compCenterY) < threshold) horizontalLines.push(compCenterY);
      });

      // 중복 제거
      setAlignmentGuides({
        vertical: Array.from(new Set(verticalLines)),
        horizontal: Array.from(new Set(horizontalLines)),
      });
    },
    [components, canvasWidth, canvasHeight],
  );

  // 정렬 가이드라인 초기화
  const clearAlignmentGuides = useCallback(() => {
    setAlignmentGuides({ vertical: [], horizontal: [] });
  }, []);

  // 페이지 관리 함수들
  const addPage = useCallback(
    (name?: string) => {
      const newPageId = uuidv4();
      const newPage: ReportPage = {
        page_id: newPageId,
        page_name: name || `페이지 ${layoutConfig.pages.length + 1}`,
        page_order: layoutConfig.pages.length,
        width: 210,
        height: 297,
        orientation: "portrait",
        margins: { top: 20, bottom: 20, left: 20, right: 20 },
        background_color: "#ffffff",
        components: [],
      };

      setLayoutConfig((prev) => ({
        pages: [...prev.pages, newPage],
      }));

      // 새 페이지로 자동 선택
      setCurrentPageId(newPageId);

      toast({
        title: "페이지 추가",
        description: `${newPage.page_name}이(가) 추가되었습니다.`,
      });
    },
    [layoutConfig.pages.length, toast],
  );

  const deletePage = useCallback(
    (pageId: string) => {
      if (layoutConfig.pages.length <= 1) {
        toast({
          title: "삭제 불가",
          description: "최소 1개의 페이지는 필요합니다.",
          variant: "destructive",
        });
        return;
      }

      const pageIndex = layoutConfig.pages.findIndex((p) => p.page_id === pageId);
      if (pageIndex === -1) return;

      setLayoutConfig((prev) => ({
        pages: prev.pages.filter((p) => p.page_id !== pageId).map((p, idx) => ({ ...p, page_order: idx })), // 순서 재정렬
      }));

      // 현재 페이지가 삭제된 경우 첫 번째 페이지로 이동
      if (currentPageId === pageId) {
        const remainingPages = layoutConfig.pages.filter((p) => p.page_id !== pageId);
        setCurrentPageId(remainingPages[0]?.page_id || null);
      }

      toast({
        title: "페이지 삭제",
        description: "페이지가 삭제되었습니다.",
      });
    },
    [layoutConfig.pages, currentPageId, toast],
  );

  const duplicatePage = useCallback(
    (pageId: string) => {
      const sourcePage = layoutConfig.pages.find((p) => p.page_id === pageId);
      if (!sourcePage) return;

      const newPageId = uuidv4();
      const newPage: ReportPage = {
        ...sourcePage,
        page_id: newPageId,
        page_name: `${sourcePage.page_name} (복사)`,
        page_order: layoutConfig.pages.length,
        // 컴포넌트도 복제 (새로운 ID 부여)
        components: sourcePage.components.map((comp) => ({
          ...comp,
          id: `comp_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`,
        })),
      };

      setLayoutConfig((prev) => ({
        pages: [...prev.pages, newPage],
      }));

      setCurrentPageId(newPageId);

      toast({
        title: "페이지 복제",
        description: `${newPage.page_name}이(가) 생성되었습니다.`,
      });
    },
    [layoutConfig.pages, toast],
  );

  const reorderPages = useCallback(
    (sourceIndex: number, targetIndex: number) => {
      if (sourceIndex === targetIndex) return;

      const newPages = [...layoutConfig.pages];
      const [movedPage] = newPages.splice(sourceIndex, 1);
      newPages.splice(targetIndex, 0, movedPage);

      // page_order 업데이트
      newPages.forEach((page, idx) => {
        page.page_order = idx;
      });

      setLayoutConfig({ pages: newPages });
    },
    [layoutConfig.pages],
  );

  const selectPage = useCallback((pageId: string) => {
    setCurrentPageId(pageId);
    // 페이지 전환 시 선택 해제
    setSelectedComponentId(null);
    setSelectedComponentIds([]);
  }, []);

  const updatePageSettings = useCallback((pageId: string, settings: Partial<ReportPage>) => {
    setLayoutConfig((prev) => {
      const targetPage = prev.pages.find((p) => p.page_id === pageId);
      if (!targetPage) {
        return prev;
      }

      // 페이지 사이즈 변경 감지
      const isWidthChanging = settings.width !== undefined && settings.width !== targetPage.width;
      const isHeightChanging = settings.height !== undefined && settings.height !== targetPage.height;

      // 사이즈 변경 시 컴포넌트 위치 재계산
      let updatedComponents = targetPage.components;
      if (isWidthChanging || isHeightChanging) {
        const oldWidth = targetPage.width;
        const oldHeight = targetPage.height;
        const newWidth = settings.width ?? targetPage.width;
        const newHeight = settings.height ?? targetPage.height;

        updatedComponents = recalculateComponentPositions(
          targetPage.components,
          oldWidth,
          oldHeight,
          newWidth,
          newHeight
        );
      }

      return {
        ...prev,
        pages: prev.pages.map((page) =>
          page.page_id === pageId
            ? { ...page, ...settings, components: updatedComponents }
            : page
        ),
      };
    });
  }, []);

  // 전체 페이지 공유 워터마크 업데이트
  const updateWatermark = useCallback((watermark: WatermarkConfig | undefined) => {
    setLayoutConfig((prev) => ({
      ...prev,
      watermark,
    }));
  }, []);

  // 리포트 및 레이아웃 로드
  const loadLayout = useCallback(async () => {
    setIsLoading(true);
    try {
      // 'new'는 새 리포트 생성 모드 - 기본 페이지 1개 생성
      if (reportId === "new") {
        const defaultPageId = uuidv4();
        const defaultPage: ReportPage = {
          page_id: defaultPageId,
          page_name: "페이지 1",
          page_order: 0,
          width: 210,
          height: 297,
          orientation: "portrait",
          margins: { top: 20, bottom: 20, left: 20, right: 20 },
          background_color: "#ffffff",
          components: [],
        };
        setLayoutConfig({ pages: [defaultPage] });
        setCurrentPageId(defaultPageId);
        setIsLoading(false);
        return;
      }

      // 리포트 상세 조회 (쿼리 포함)
      const detailResponse = await reportApi.getReportById(reportId);
      if (detailResponse.success && detailResponse.data) {
        setReportDetail(detailResponse.data);

        // 쿼리 로드
        if (detailResponse.data.queries && detailResponse.data.queries.length > 0) {
          const loadedQueries = detailResponse.data.queries.map((q) => ({
            id: q.query_id,
            name: q.query_name,
            type: q.query_type,
            sqlQuery: q.sql_query,
            parameters: Array.isArray(q.parameters) ? q.parameters : [],
            externalConnectionId: q.external_connection_id || undefined,
          }));
          setQueries(loadedQueries);
        }

        // 연결된 메뉴 로드
        if (detailResponse.data.menuObjids && detailResponse.data.menuObjids.length > 0) {
          setMenuObjids(detailResponse.data.menuObjids);
        } else {
          setMenuObjids([]);
        }
      }

      // 레이아웃 조회
      try {
        const layoutResponse = await reportApi.getLayout(reportId);
        if (layoutResponse.success && layoutResponse.data) {
          const layoutData = layoutResponse.data;
          setLayout(layoutData);

          // layoutData가 이미 pages를 가지고 있는지 확인
          if (layoutData.pages && Array.isArray(layoutData.pages) && layoutData.pages.length > 0) {
            // 이미 새 구조 (pages 배열)
            setLayoutConfig({ pages: layoutData.pages });
            setCurrentPageId(layoutData.pages[0].page_id);
          } else {
            // 자동 마이그레이션: 기존 단일 페이지 구조 → 다중 페이지 구조
            const oldComponents = layoutData.components || [];

            // 기존 구조 감지
            if (oldComponents.length > 0) {
              const migratedPageId = uuidv4();
              const migratedPage: ReportPage = {
                page_id: migratedPageId,
                page_name: "페이지 1",
                page_order: 0,
                width: layoutData.canvas_width || 210,
                height: layoutData.canvas_height || 297,
                orientation: (layoutData.page_orientation as "portrait" | "landscape") || "portrait",
                margins: {
                  top: layoutData.margin_top || 20,
                  bottom: layoutData.margin_bottom || 20,
                  left: layoutData.margin_left || 20,
                  right: layoutData.margin_right || 20,
                },
                background_color: "#ffffff",
                components: oldComponents,
              };

              setLayoutConfig({ pages: [migratedPage] });
              setCurrentPageId(migratedPageId);
            } else {
              // 빈 레이아웃 - 기본 페이지 생성
              const defaultPageId = uuidv4();
              const defaultPage: ReportPage = {
                page_id: defaultPageId,
                page_name: "페이지 1",
                page_order: 0,
                width: 210,
                height: 297,
                orientation: "portrait",
                margins: { top: 20, bottom: 20, left: 20, right: 20 },
                background_color: "#ffffff",
                components: [],
              };
              setLayoutConfig({ pages: [defaultPage] });
              setCurrentPageId(defaultPageId);
            }
          }
        }
      } catch {
        // 레이아웃이 없으면 기본 페이지 생성
        const defaultPageId = uuidv4();
        const defaultPage: ReportPage = {
          page_id: defaultPageId,
          page_name: "페이지 1",
          page_order: 0,
          width: 210,
          height: 297,
          orientation: "portrait",
          margins: { top: 20, bottom: 20, left: 20, right: 20 },
          background_color: "#ffffff",
          components: [],
        };
        setLayoutConfig({ pages: [defaultPage] });
        setCurrentPageId(defaultPageId);
      }
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : "리포트를 불러오는데 실패했습니다.";
      toast({
        title: "오류",
        description: errorMessage,
        variant: "destructive",
      });
    } finally {
      setIsLoading(false);
    }
  }, [reportId, toast]);

  // 초기 로드
  useEffect(() => {
    loadLayout();
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [reportId]);

  // 초기 히스토리 설정
  useEffect(() => {
    if (!isLoading && components.length > 0 && history.length === 0) {
      // 최초 컴포넌트 로드 시 히스토리에 추가
      setHistory([JSON.parse(JSON.stringify(components))]);
      setHistoryIndex(0);
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [isLoading, components.length]);

  // 컴포넌트 변경 시 히스토리 저장 (디바운스 적용)
  useEffect(() => {
    if (components.length === 0) return;
    if (isLoading) return; // 로딩 중에는 히스토리 저장 안 함
    if (isUndoRedoing) return; // Undo/Redo 중에는 히스토리 저장 안 함

    const timeoutId = setTimeout(() => {
      // 현재 히스토리의 마지막 항목과 비교하여 다를 때만 저장
      const lastHistory = history[historyIndex];
      const isDifferent = !lastHistory || JSON.stringify(lastHistory) !== JSON.stringify(components);

      if (isDifferent) {
        saveToHistory(components);
      }
    }, 300); // 300ms 디바운스

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

  // 쿼리 결과 저장
  const setQueryResult = useCallback((queryId: string, fields: string[], rows: Record<string, unknown>[]) => {
    setQueryResults((prev) => {
      const existing = prev.find((r) => r.queryId === queryId);
      if (existing) {
        return prev.map((r) => (r.queryId === queryId ? { queryId, fields, rows } : r));
      }
      return [...prev, { queryId, fields, rows }];
    });
  }, []);

  // 쿼리 결과 조회
  const getQueryResult = useCallback(
    (queryId: string): QueryResult | null => {
      return queryResults.find((r) => r.queryId === queryId) || null;
    },
    [queryResults],
  );

  // 컴포넌트 추가 (현재 페이지에)
  const addComponent = useCallback(
    (component: ComponentConfig) => {
      setComponents((prev) => [...prev, component]);
    },
    [setComponents],
  );

  // 컴포넌트 업데이트 (현재 페이지에서)
  const updateComponent = useCallback(
    (id: string, updates: Partial<ComponentConfig>) => {
      if (!currentPageId) return;

      setLayoutConfig((prev) => ({
        pages: prev.pages.map((page) =>
          page.page_id === currentPageId
            ? {
                ...page,
                components: page.components.map((comp) => (comp.id === id ? { ...comp, ...updates } : comp)),
              }
            : page,
        ),
      }));
    },
    [currentPageId],
  );

  // 텍스트 컴포넌트 크기 자동 맞춤 (Ctrl+Shift+F)
  const fitSelectedToContent = useCallback(() => {
    const MM_TO_PX = 4; // 고정 스케일 팩터
    
    // 선택된 컴포넌트 ID 결정
    const targetIds =
      selectedComponentIds.length > 0
        ? selectedComponentIds
        : selectedComponentId
          ? [selectedComponentId]
          : [];

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

    // 텍스트/레이블 컴포넌트만 필터링
    const textComponents = components.filter(
      (c) =>
        targetIds.includes(c.id) &&
        (c.type === "text" || c.type === "label") &&
        !c.locked
    );

    if (textComponents.length === 0) {
      toast({
        title: "크기 조정 불가",
        description: "선택된 텍스트 컴포넌트가 없습니다.",
        variant: "destructive",
      });
      return;
    }

    // 현재 페이지 설정 가져오기
    const page = currentPage;
    if (!page) return;

    const canvasWidthPx = page.width * MM_TO_PX;
    const canvasHeightPx = page.height * MM_TO_PX;
    const marginRightPx = (page.margins?.right || 10) * MM_TO_PX;
    const marginBottomPx = (page.margins?.bottom || 10) * MM_TO_PX;

    // 각 텍스트 컴포넌트 크기 조정
    textComponents.forEach((comp) => {
      const displayValue = comp.defaultValue || (comp.type === "text" ? "텍스트 입력" : "레이블 텍스트");
      const fontSize = comp.fontSize || 14;

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

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

      lines.forEach((line: string) => {
        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 = comp.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);
      });

      // 패딩 및 높이 계산
      const horizontalPadding = 24;
      const verticalPadding = 20;
      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(50, finalWidth);
      const newHeight = Math.max(30, finalHeight);

      // 크기 업데이트 - setLayoutConfig 직접 사용
      setLayoutConfig((prev) => ({
        pages: prev.pages.map((p) =>
          p.page_id === currentPageId
            ? {
                ...p,
                components: p.components.map((c) =>
                  c.id === comp.id
                    ? {
                        ...c,
                        width: snapToGrid ? Math.round(newWidth / gridSize) * gridSize : newWidth,
                        height: snapToGrid ? Math.round(newHeight / gridSize) * gridSize : newHeight,
                      }
                    : c
                ),
              }
            : p
        ),
      }));
    });

    toast({
      title: "크기 조정 완료",
      description: `${textComponents.length}개의 컴포넌트 크기가 조정되었습니다.`,
    });
  }, [selectedComponentId, selectedComponentIds, components, currentPage, currentPageId, snapToGrid, gridSize, toast]);

  // 컴포넌트 삭제 (현재 페이지에서)
  const removeComponent = useCallback(
    (id: string) => {
      if (!currentPageId) return;

      setLayoutConfig((prev) => ({
        pages: prev.pages.map((page) =>
          page.page_id === currentPageId
            ? { ...page, components: page.components.filter((comp) => comp.id !== id) }
            : page,
        ),
      }));

      if (selectedComponentId === id) {
        setSelectedComponentId(null);
      }
      // 다중 선택에서도 제거
      setSelectedComponentIds((prev) => prev.filter((compId) => compId !== id));
    },
    [currentPageId, selectedComponentId],
  );

  // 컴포넌트 선택 (단일/다중)
  const selectComponent = useCallback((id: string | null, isMultiSelect = false) => {
    if (id === null) {
      // 선택 해제
      setSelectedComponentId(null);
      setSelectedComponentIds([]);
      return;
    }

    if (isMultiSelect) {
      // Ctrl+클릭: 다중 선택 토글
      setSelectedComponentIds((prev) => {
        if (prev.includes(id)) {
          // 이미 선택되어 있으면 제거
          const newSelection = prev.filter((compId) => compId !== id);
          setSelectedComponentId(newSelection.length > 0 ? newSelection[0] : null);
          return newSelection;
        } else {
          // 선택 추가
          setSelectedComponentId(id);
          return [...prev, id];
        }
      });
    } else {
      // 일반 클릭: 단일 선택
      setSelectedComponentId(id);
      setSelectedComponentIds([id]);
    }
  }, []);

  // 여러 컴포넌트 한번에 선택 (마퀴 선택용)
  const selectMultipleComponents = useCallback((ids: string[]) => {
    if (ids.length === 0) {
      setSelectedComponentId(null);
      setSelectedComponentIds([]);
      return;
    }
    setSelectedComponentId(ids[0]);
    setSelectedComponentIds(ids);
  }, []);

  // 레이아웃 업데이트
  const updateLayout = useCallback(
    (updates: Partial<ReportLayout>) => {
      setLayout((prev) => (prev ? { ...prev, ...updates } : null));

      // 현재 페이지 설정 업데이트
      if (!currentPageId) return;

      const pageUpdates: Partial<ReportPage> = {};
      if (updates.canvas_width !== undefined) pageUpdates.width = updates.canvas_width;
      if (updates.canvas_height !== undefined) pageUpdates.height = updates.canvas_height;
      if (updates.page_orientation !== undefined)
        pageUpdates.orientation = updates.page_orientation as "portrait" | "landscape";

      if (
        updates.margin_top !== undefined ||
        updates.margin_bottom !== undefined ||
        updates.margin_left !== undefined ||
        updates.margin_right !== undefined
      ) {
        pageUpdates.margins = {
          top: updates.margin_top ?? currentPage?.margins.top ?? 20,
          bottom: updates.margin_bottom ?? currentPage?.margins.bottom ?? 20,
          left: updates.margin_left ?? currentPage?.margins.left ?? 20,
          right: updates.margin_right ?? currentPage?.margins.right ?? 20,
        };
      }

      if (Object.keys(pageUpdates).length > 0) {
        updatePageSettings(currentPageId, pageUpdates);
      }
    },
    [currentPageId, currentPage, updatePageSettings],
  );

  // 레이아웃 저장
  const saveLayout = useCallback(async () => {
    setIsSaving(true);
    try {
      let actualReportId = reportId;

      // 새 리포트인 경우 먼저 리포트 생성
      if (reportId === "new") {
        const createResponse = await reportApi.createReport({
          reportNameKor: "새 리포트",
          reportType: "BASIC",
          description: "새로 생성된 리포트입니다.",
        });

        if (!createResponse.success || !createResponse.data) {
          throw new Error("리포트 생성에 실패했습니다.");
        }

        actualReportId = createResponse.data.reportId;

        // URL 업데이트 (페이지 리로드 없이)
        window.history.replaceState({}, "", `/admin/report/designer/${actualReportId}`);
      }

      // 레이아웃 저장 (페이지 구조로)
      await reportApi.saveLayout(actualReportId, {
        layoutConfig, // 페이지 기반 구조
        queries: queries.map((q) => ({
          ...q,
          externalConnectionId: q.externalConnectionId || undefined,
        })),
        menuObjids, // 연결된 메뉴 목록
      });

      toast({
        title: "성공",
        description: reportId === "new" ? "리포트가 생성되었습니다." : "레이아웃이 저장되었습니다.",
      });

      // 새 리포트였다면 데이터 다시 로드
      if (reportId === "new") {
        await loadLayout();
      }
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : "저장에 실패했습니다.";
      toast({
        title: "오류",
        description: errorMessage,
        variant: "destructive",
      });
    } finally {
      setIsSaving(false);
    }
  }, [reportId, layoutConfig, queries, menuObjids, toast, loadLayout]);

  // 메뉴를 선택하고 저장하는 함수
  const saveLayoutWithMenus = useCallback(
    async (selectedMenuObjids: number[]) => {
      // 먼저 메뉴 상태 업데이트
      setMenuObjids(selectedMenuObjids);

      setIsSaving(true);
      try {
        let actualReportId = reportId;

        // 새 리포트인 경우 먼저 리포트 생성
        if (reportId === "new") {
          const createResponse = await reportApi.createReport({
            reportNameKor: "새 리포트",
            reportType: "BASIC",
            description: "새로 생성된 리포트입니다.",
          });

          if (!createResponse.success || !createResponse.data) {
            throw new Error("리포트 생성에 실패했습니다.");
          }

          actualReportId = createResponse.data.reportId;

          // URL 업데이트 (페이지 리로드 없이)
          window.history.replaceState({}, "", `/admin/report/designer/${actualReportId}`);
        }

        // 레이아웃 저장 (선택된 메뉴와 함께)
        await reportApi.saveLayout(actualReportId, {
          layoutConfig,
          queries: queries.map((q) => ({
            ...q,
            externalConnectionId: q.externalConnectionId || undefined,
          })),
          menuObjids: selectedMenuObjids,
        });

        toast({
          title: "성공",
          description: reportId === "new" ? "리포트가 생성되었습니다." : "레이아웃이 저장되었습니다.",
        });

        // 새 리포트였다면 데이터 다시 로드
        if (reportId === "new") {
          await loadLayout();
        }
      } catch (error) {
        const errorMessage = error instanceof Error ? error.message : "저장에 실패했습니다.";
        toast({
          title: "오류",
          description: errorMessage,
          variant: "destructive",
        });
      } finally {
        setIsSaving(false);
      }
    },
    [reportId, layoutConfig, queries, toast, loadLayout],
  );

  // 템플릿 적용
  const applyTemplate = useCallback(
    async (templateId: string) => {
      try {
        // 기존 컴포넌트가 있으면 확인
        if (components.length > 0) {
          if (!confirm("현재 레이아웃을 덮어씁니다. 계속하시겠습니까?")) {
            return;
          }
        }

        // DB에서 템플릿 조회 (시스템 템플릿 또는 사용자 정의 템플릿)
        const response = await reportApi.getTemplates();

        if (!response.success || !response.data) {
          throw new Error("템플릿 목록을 불러올 수 없습니다.");
        }

        // 시스템 템플릿과 커스텀 템플릿 모두에서 찾기
        const allTemplates = [...(response.data.system || []), ...(response.data.custom || [])];
        const template = allTemplates.find((t: { template_id: string }) => t.template_id === templateId);

        if (!template) {
          throw new Error("템플릿을 찾을 수 없습니다.");
        }

        // 템플릿 데이터 확인용 로그
        console.log("===== 선택된 템플릿 =====");
        console.log("Template ID:", template.template_id);
        console.log("Template Name:", template.template_name_kor);
        console.log("Layout Config:", template.layout_config);
        console.log("Default Queries:", template.default_queries);
        console.log("========================");

        // 템플릿 데이터 파싱 및 적용
        let layoutConfig: { components?: ComponentConfig[] } | null = null;
        let defaultQueries: unknown[] = [];

        // layout_config 파싱 (안전하게)
        try {
          if (template.layout_config) {
            layoutConfig =
              typeof template.layout_config === "string" ? JSON.parse(template.layout_config) : template.layout_config;
          }
        } catch (e) {
          console.error("layout_config 파싱 오류:", e);
          layoutConfig = { components: [] };
        }

        // default_queries 파싱 (안전하게)
        try {
          if (template.default_queries) {
            defaultQueries =
              typeof template.default_queries === "string"
                ? JSON.parse(template.default_queries)
                : template.default_queries;
          }
        } catch (e) {
          console.error("default_queries 파싱 오류:", e);
          defaultQueries = [];
        }

        // layoutConfig가 없으면 빈 구조로 초기화
        if (!layoutConfig || typeof layoutConfig !== "object") {
          layoutConfig = { components: [] };
        }

        // 컴포넌트 적용 (ID 재생성)
        const templateComponents = Array.isArray(layoutConfig.components) ? layoutConfig.components : [];
        const newComponents = templateComponents.map((comp: ComponentConfig) => ({
          ...comp,
          id: `comp-${Date.now()}-${Math.random()}`,
        }));

        // 쿼리 적용 (ID 재생성)
        const templateQueries = Array.isArray(defaultQueries) ? defaultQueries : [];
        const newQueries = templateQueries
          .filter((q): q is Record<string, unknown> => typeof q === "object" && q !== null)
          .map((q) => ({
            id: `query-${Date.now()}-${Math.random()}`,
            name: (q.name as string) || "",
            type: (q.type as "MASTER" | "DETAIL") || "MASTER",
            sqlQuery: (q.sqlQuery as string) || "",
            parameters: Array.isArray(q.parameters) ? (q.parameters as string[]) : [],
            externalConnectionId: (q.externalConnectionId as number | null) || null,
          }));

        setComponents(newComponents);
        setQueries(newQueries);

        const message =
          newComponents.length === 0
            ? "템플릿이 적용되었습니다. (빈 템플릿)"
            : `템플릿이 적용되었습니다. (컴포넌트 ${newComponents.length}개, 쿼리 ${newQueries.length}개)`;

        toast({
          title: "성공",
          description: message,
        });
      } catch (error) {
        const errorMessage = error instanceof Error ? error.message : "템플릿 적용에 실패했습니다.";
        toast({
          title: "오류",
          description: errorMessage,
          variant: "destructive",
        });
      }
    },
    // eslint-disable-next-line react-hooks/exhaustive-deps
    [toast],
  );

  const value: ReportDesignerContextType = {
    reportId,
    reportDetail,
    layout,

    // 페이지 관리
    layoutConfig,
    currentPageId,
    currentPage,
    addPage,
    deletePage,
    duplicatePage,
    reorderPages,
    selectPage,
    updatePageSettings,
    updateWatermark,

    // 컴포넌트 (현재 페이지)
    components,
    queries,
    setQueries,
    queryResults,
    setQueryResult,
    getQueryResult,
    selectedComponentId,
    selectedComponentIds,
    isLoading,
    isSaving,
    addComponent,
    updateComponent,
    removeComponent,
    selectComponent,
    selectMultipleComponents,
    updateLayout,
    saveLayout,
    loadLayout,
    applyTemplate,
    canvasWidth,
    canvasHeight,
    pageOrientation,
    margins,
    // 레이아웃 도구
    gridSize,
    setGridSize,
    showGrid,
    setShowGrid,
    snapToGrid,
    setSnapToGrid,
    snapValueToGrid,
    // 정렬 가이드라인
    alignmentGuides,
    calculateAlignmentGuides,
    clearAlignmentGuides,
    // 복사/붙여넣기
    copyComponents,
    pasteComponents,
    duplicateComponents,
    copyStyles,
    pasteStyles,
    duplicateAtPosition,
    fitSelectedToContent,
    // Undo/Redo
    undo,
    redo,
    canUndo: historyIndex > 0,
    canRedo: historyIndex < history.length - 1,
    // 정렬 기능
    alignLeft,
    alignRight,
    alignTop,
    alignBottom,
    alignCenterHorizontal,
    alignCenterVertical,
    distributeHorizontal,
    distributeVertical,
    makeSameWidth,
    makeSameHeight,
    makeSameSize,
    // 레이어 관리
    bringToFront,
    sendToBack,
    bringForward,
    sendBackward,
    // 잠금 관리
    toggleLock,
    lockComponents,
    unlockComponents,
    // 눈금자 표시
    showRuler,
    setShowRuler,
    // 그룹화
    groupComponents,
    ungroupComponents,
    // 메뉴 연결
    menuObjids,
    setMenuObjids,
    saveLayoutWithMenus,
  };

  return <ReportDesignerContext.Provider value={value}>{children}</ReportDesignerContext.Provider>;
}

export function useReportDesigner() {
  const context = useContext(ReportDesignerContext);
  if (context === undefined) {
    throw new Error("useReportDesigner must be used within a ReportDesignerProvider");
  }
  return context;
}
