/**
 * @module useHistoryManager
 * @description 컴포넌트 변경 이력을 관리하여 Undo/Redo 기능을 제공한다.
 * - 최대 50개의 히스토리 스냅샷 유지 (메모리 제한)
 * - 300ms 디바운스로 연속 변경을 하나의 히스토리 항목으로 묶음
 * - Undo/Redo 실행 중에는 히스토리 저장을 건너뜀 (isUndoRedoing 플래그)
 */

import { useState, useCallback, useEffect } from "react";
import type { ComponentConfig } from "@/types/report";
import type { SetComponentsFn, ToastFunction } from "./internalTypes";

/** 히스토리에 유지할 최대 스냅샷 수 */
const MAX_HISTORY_SIZE = 50;
/** 컴포넌트 변경 후 히스토리 저장까지 대기 시간 (ms) */
const HISTORY_DEBOUNCE_MS = 300;

export interface HistoryManagerResult {
  undo: () => void;
  redo: () => void;
  canUndo: boolean;
  canRedo: boolean;
  /** 외부에서 히스토리 초기화가 필요할 때 사용 (레이아웃 최초 로드 시) */
  initHistory: (components: ComponentConfig[]) => void;
  isUndoRedoing: boolean;
}

export function useHistoryManager(
  components: ComponentConfig[],
  setComponents: SetComponentsFn,
  isLoading: boolean,
  toast: ToastFunction,
): HistoryManagerResult {
  const [history, setHistory] = useState<ComponentConfig[][]>([]);
  const [historyIndex, setHistoryIndex] = useState(-1);
  const [isUndoRedoing, setIsUndoRedoing] = useState(false);

  /**
   * 현재 컴포넌트 배열을 히스토리에 저장한다.
   * 현재 인덱스 이후의 미래 이력은 제거하여 새 분기를 시작한다.
   */
  const saveToHistory = useCallback(
    (newComponents: ComponentConfig[]) => {
      setHistory((prev) => {
        const truncated = prev.slice(0, historyIndex + 1);
        truncated.push(JSON.parse(JSON.stringify(newComponents)));
        return truncated.slice(-MAX_HISTORY_SIZE);
      });
      setHistoryIndex((prev) => Math.min(prev + 1, MAX_HISTORY_SIZE - 1));
    },
    [historyIndex],
  );

  /** 최초 레이아웃 로드 후 히스토리를 초기 상태로 설정한다. */
  const initHistory = useCallback((initialComponents: ComponentConfig[]) => {
    setHistory([JSON.parse(JSON.stringify(initialComponents))]);
    setHistoryIndex(0);
  }, []);

  /** 컴포넌트 변경 감지 → 300ms 디바운스 후 히스토리에 저장 */
  useEffect(() => {
    if (components.length === 0 || isLoading || isUndoRedoing) return;

    const timeoutId = setTimeout(() => {
      setHistory((prev) => {
        const lastSnapshot = prev[historyIndex];
        const isDifferent = !lastSnapshot || JSON.stringify(lastSnapshot) !== JSON.stringify(components);
        if (isDifferent) {
          saveToHistory(components);
        }
        return prev; // saveToHistory가 내부에서 setState를 호출함
      });
    }, HISTORY_DEBOUNCE_MS);

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

  /** 이전 상태로 되돌린다. */
  const undo = useCallback(() => {
    if (historyIndex <= 0) return;

    setIsUndoRedoing(true);
    const prevIndex = historyIndex - 1;
    setHistoryIndex(prevIndex);
    setComponents(JSON.parse(JSON.stringify(history[prevIndex])));
    setTimeout(() => setIsUndoRedoing(false), 100);

    toast({ title: "실행 취소", description: "이전 상태로 되돌렸습니다." });
  }, [historyIndex, history, setComponents, toast]);

  /** 취소한 작업을 다시 실행한다. */
  const redo = useCallback(() => {
    if (historyIndex >= history.length - 1) return;

    setIsUndoRedoing(true);
    const nextIndex = historyIndex + 1;
    setHistoryIndex(nextIndex);
    setComponents(JSON.parse(JSON.stringify(history[nextIndex])));
    setTimeout(() => setIsUndoRedoing(false), 100);

    toast({ title: "다시 실행", description: "다음 상태로 이동했습니다." });
  }, [historyIndex, history, setComponents, toast]);

  return {
    undo,
    redo,
    canUndo: historyIndex > 0,
    canRedo: historyIndex < history.length - 1,
    initHistory,
    isUndoRedoing,
  };
}
