/**
 * @module useClipboardActions
 * @description 컴포넌트 복사/붙여넣기/복제와 스타일 전달 기능을 제공한다.
 *
 * - copyComponents / pasteComponents: 전체 컴포넌트 복사·붙여넣기 (Ctrl+C/V)
 * - duplicateComponents: 선택 컴포넌트 즉시 복제 + 20px 오프셋 (Ctrl+D)
 * - copyStyles / pasteStyles: 스타일 속성만 복사·붙여넣기 (Ctrl+Shift+C/V)
 * - duplicateAtPosition: Alt+드래그 시 지정 위치에 복제
 * - fitSelectedToContent: 텍스트 컴포넌트 크기를 내용에 맞게 자동 조정 (Ctrl+Shift+F)
 *
 * 잠긴 컴포넌트는 모든 복사/복제 대상에서 제외된다.
 */

import { useState, useCallback } from "react";
import type { ComponentConfig, ReportPage, ReportLayoutConfig } from "@/types/report";
import type { SetComponentsFn, ToastFunction } from "./internalTypes";
import { MM_TO_PX, generateComponentId } from "@/lib/report/constants";

/** fitSelectedToContent에서 텍스트 너비 측정 시 추가하는 수평 패딩 (px) */
const HORIZONTAL_PADDING_PX = 24;
/** fitSelectedToContent에서 높이 계산 시 추가하는 수직 패딩 (px) */
const VERTICAL_PADDING_PX = 20;

/** 스타일 복사/붙여넣기 대상 속성 목록 (ComponentConfig에 실제로 존재하는 스타일 필드만 포함) */
const STYLE_PROPERTY_KEYS: (keyof ComponentConfig)[] = [
  "fontSize",
  "fontColor",
  "fontWeight",
  "fontFamily",
  "textAlign",
  "backgroundColor",
  "borderWidth",
  "borderColor",
  "borderRadius",
  "padding",
  "letterSpacing",
  "lineHeight",
];


export interface ClipboardActions {
  copyComponents: () => void;
  pasteComponents: () => void;
  duplicateComponents: () => void;
  copyStyles: () => void;
  pasteStyles: () => void;
  duplicateAtPosition: (componentIds: string[], offsetX?: number, offsetY?: number) => string[];
  fitSelectedToContent: () => void;
}

interface ClipboardDeps {
  components: ComponentConfig[];
  selectedComponentId: string | null;
  selectedComponentIds: string[];
  setComponents: SetComponentsFn;
  /** fitSelectedToContent에 필요한 현재 페이지 정보 */
  currentPage: ReportPage | undefined;
  currentPageId: string | null;
  setLayoutConfig: React.Dispatch<React.SetStateAction<ReportLayoutConfig>>;
  snapToGrid: boolean;
  gridSize: number;
  toast: ToastFunction;
}

export function useClipboardActions({
  components,
  selectedComponentId,
  selectedComponentIds,
  setComponents,
  currentPage,
  currentPageId,
  setLayoutConfig,
  snapToGrid,
  gridSize,
  toast,
}: ClipboardDeps): ClipboardActions {
  const [clipboard, setClipboard] = useState<ComponentConfig[]>([]);
  const [styleClipboard, setStyleClipboard] = useState<Partial<ComponentConfig> | null>(null);

  /**
   * 선택된 컴포넌트를 클립보드에 저장한다.
   * 잠긴 컴포넌트는 복사에서 제외된다.
   */
  const copyComponents = useCallback(() => {
    const targetIds = selectedComponentIds.length > 0 ? selectedComponentIds : selectedComponentId ? [selectedComponentId] : [];
    const toCopy = components.filter((c) => targetIds.includes(c.id) && !c.locked);

    if (toCopy.length === 0) {
      toast({ title: "복사 불가", description: "잠긴 컴포넌트는 복사할 수 없습니다.", variant: "destructive" });
      return;
    }

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

  /**
   * 클립보드의 컴포넌트를 20px 오프셋으로 붙여넣는다.
   * 붙여넣기된 컴포넌트는 자동으로 선택 상태가 된다.
   */
  const pasteComponents = useCallback(() => {
    if (clipboard.length === 0) return;

    const newComponents = clipboard.map((comp) => ({
      ...comp,
      id: generateComponentId(),
      x: comp.x + 20,
      y: comp.y + 20,
      zIndex: components.length,
    }));

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

  /**
   * 선택된 컴포넌트를 즉시 복제하고 20px 오프셋으로 배치한다.
   * 복제된 컴포넌트는 잠금 해제 상태로 생성된다.
   */
  const duplicateComponents = useCallback(() => {
    const targetIds = selectedComponentIds.length > 0 ? selectedComponentIds : selectedComponentId ? [selectedComponentId] : [];
    const toDuplicate = components.filter((c) => targetIds.includes(c.id) && !c.locked);

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

    const newComponents = toDuplicate.map((comp) => ({
      ...comp,
      id: generateComponentId(),
      x: comp.x + 20,
      y: comp.y + 20,
      zIndex: components.length,
      locked: false,
    }));

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

  /**
   * 선택된 컴포넌트의 스타일 속성만 스타일 클립보드에 저장한다.
   * 단일 컴포넌트 기준으로 복사하며, 위치/크기/타입 등 레이아웃 속성은 제외된다.
   */
  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 styleProps: Partial<ComponentConfig> = {};
    STYLE_PROPERTY_KEYS.forEach((key) => {
      const value = component[key];
      if (value !== undefined) {
        (styleProps as Record<string, unknown>)[key] = value;
      }
    });

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

  /**
   * 스타일 클립보드의 스타일을 선택된 컴포넌트에 적용한다.
   * 잠긴 컴포넌트는 제외된다.
   */
  const pasteStyles = useCallback(() => {
    if (!styleClipboard) {
      toast({ title: "스타일 붙여넣기 불가", description: "먼저 Ctrl+Shift+C로 스타일을 복사해주세요.", variant: "destructive" });
      return;
    }

    const targetIds = selectedComponentIds.length > 0 ? selectedComponentIds : selectedComponentId ? [selectedComponentId] : [];
    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((c) => (applicableIds.includes(c.id) ? { ...c, ...styleClipboard } : c)),
    );
    toast({ title: "스타일 적용 완료", description: `${applicableIds.length}개의 컴포넌트에 스타일이 적용되었습니다.` });
  }, [styleClipboard, selectedComponentId, selectedComponentIds, components, setComponents, toast]);

  /**
   * Alt+드래그 복제용. 지정된 오프셋 위치에 컴포넌트를 복제하고 새 ID 배열을 반환한다.
   * 잠긴 컴포넌트는 복제되지 않는다.
   */
  const duplicateAtPosition = useCallback(
    (componentIds: string[], offsetX = 0, offsetY = 0): string[] => {
      const toDuplicate = components.filter((c) => componentIds.includes(c.id) && !c.locked);
      if (toDuplicate.length === 0) return [];

      const newComponents = toDuplicate.map((comp) => ({
        ...comp,
        id: generateComponentId(),
        x: comp.x + offsetX,
        y: comp.y + offsetY,
        zIndex: components.length,
        locked: false,
      }));

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

  /**
   * 선택된 텍스트/레이블 컴포넌트의 크기를 내용 텍스트에 맞게 자동 조정한다.
   * DOM에 임시 요소를 생성하여 실제 렌더링 너비를 측정한 뒤, 적절한 크기로 업데이트한다.
   * snapToGrid가 활성화된 경우 최종 크기도 그리드에 맞춰 반올림된다.
   */
  const fitSelectedToContent = useCallback(() => {
    if (!currentPage || !currentPageId) return;

    const targetIds = selectedComponentIds.length > 0 ? selectedComponentIds : selectedComponentId ? [selectedComponentId] : [];
    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 canvasWidthPx = currentPage.width * MM_TO_PX;
    const canvasHeightPx = currentPage.height * MM_TO_PX;
    const marginRightPx = (currentPage.margins?.right ?? 10) * MM_TO_PX;
    const marginBottomPx = (currentPage.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;

      // DOM에 임시 요소를 추가하여 각 줄의 실제 렌더링 너비 측정
      const lines = displayValue.split("\n");
      let maxLineWidth = 0;
      lines.forEach((line: string) => {
        const el = document.createElement("span");
        el.style.cssText = `position:absolute;visibility:hidden;white-space:nowrap;font-size:${fontSize}px;font-weight:${comp.fontWeight || "normal"};font-family:system-ui,-apple-system,sans-serif`;
        el.textContent = line || " ";
        document.body.appendChild(el);
        maxLineWidth = Math.max(maxLineWidth, el.getBoundingClientRect().width);
        document.body.removeChild(el);
      });

      const lineHeight = fontSize * 1.5;
      const rawWidth = Math.min(maxLineWidth + HORIZONTAL_PADDING_PX, maxWidth);
      const rawHeight = Math.min(lines.length * lineHeight + VERTICAL_PADDING_PX, maxHeight);
      const finalWidth = Math.max(50, snapToGrid ? Math.round(rawWidth / gridSize) * gridSize : rawWidth);
      const finalHeight = Math.max(30, snapToGrid ? Math.round(rawHeight / gridSize) * gridSize : rawHeight);

      setLayoutConfig((prev) => ({
        pages: prev.pages.map((p) =>
          p.page_id === currentPageId
            ? {
                ...p,
                components: p.components.map((c) =>
                  c.id === comp.id ? { ...c, width: finalWidth, height: finalHeight } : c,
                ),
              }
            : p,
        ),
      }));
    });

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

  return {
    copyComponents,
    pasteComponents,
    duplicateComponents,
    copyStyles,
    pasteStyles,
    duplicateAtPosition,
    fitSelectedToContent,
  };
}
