"use client";

/**
 * PivotGrid 메인 컴포넌트
 * 다차원 데이터 분석을 위한 피벗 테이블
 */

import React, { useState, useMemo, useCallback, useEffect, useRef } from "react";
import { cn } from "@/lib/utils";
import { Checkbox } from "@/components/ui/checkbox";
import { Label } from "@/components/ui/label";
import {
  PivotGridProps,
  PivotResult,
  PivotFieldConfig,
  PivotCellData,
  PivotFlatRow,
  PivotCellValue,
  PivotGridState,
} from "./types";
import { processPivotData, pathToKey } from "./utils/pivotEngine";
import { exportPivotToExcel } from "./utils/exportExcel";
import { getConditionalStyle, formatStyleToReact, CellFormatStyle } from "./utils/conditionalFormat";
import { FieldPanel } from "./components/FieldPanel";
import { FieldChooser } from "./components/FieldChooser";
import { DrillDownModal } from "./components/DrillDownModal";
import { PivotChart } from "./components/PivotChart";
import { FilterPopup } from "./components/FilterPopup";
import { useVirtualScroll } from "./hooks/useVirtualScroll";
import {
  ChevronRight,
  ChevronDown,
  Download,
  Settings,
  RefreshCw,
  Maximize2,
  Minimize2,
  LayoutGrid,
  FileSpreadsheet,
  BarChart3,
  Filter,
  ArrowUp,
  ArrowDown,
  ArrowUpDown,
  Printer,
  Save,
  RotateCcw,
  FileText,
  Loader2,
  Eye,
  EyeOff,
} from "lucide-react";
import { Button } from "@/components/ui/button";

// ==================== 상수 ====================

const PIVOT_STATE_VERSION = "1.0"; // 상태 저장 버전 (호환성 체크용)

// ==================== 유틸리티 함수 ====================

// 셀 병합 정보 계산
interface MergeCellInfo {
  rowSpan: number;
  skip: boolean; // 병합된 셀에서 건너뛸지 여부
}

const calculateMergeCells = (
  rows: PivotFlatRow[],
  mergeCells: boolean
): Map<number, MergeCellInfo> => {
  const mergeInfo = new Map<number, MergeCellInfo>();
  
  if (!mergeCells || rows.length === 0) {
    rows.forEach((_, idx) => mergeInfo.set(idx, { rowSpan: 1, skip: false }));
    return mergeInfo;
  }
  
  let i = 0;
  while (i < rows.length) {
    const currentPath = rows[i].path.join("|||");
    let spanCount = 1;
    
    // 같은 path를 가진 연속 행 찾기
    while (
      i + spanCount < rows.length &&
      rows[i + spanCount].path.join("|||") === currentPath
    ) {
      spanCount++;
    }
    
    // 첫 번째 행은 rowSpan 설정
    mergeInfo.set(i, { rowSpan: spanCount, skip: false });
    
    // 나머지 행은 skip
    for (let j = 1; j < spanCount; j++) {
      mergeInfo.set(i + j, { rowSpan: 1, skip: true });
    }
    
    i += spanCount;
  }
  
  return mergeInfo;
};

// ==================== 서브 컴포넌트 ====================

// 행 헤더 셀
interface RowHeaderCellProps {
  row: PivotFlatRow;
  rowFields: PivotFieldConfig[];
  onToggleExpand: (path: string[]) => void;
  rowSpan?: number;
}

const RowHeaderCell: React.FC<RowHeaderCellProps> = ({
  row,
  rowFields,
  onToggleExpand,
  rowSpan = 1,
}) => {
  const indentSize = row.level * 20;

  return (
    <td
      className={cn(
        "border-r border-b border-border bg-muted/50",
        "px-2 py-1.5 text-left text-sm",
        "whitespace-nowrap font-medium",
        row.isExpanded && "bg-muted/70"
      )}
      style={{ paddingLeft: `${8 + indentSize}px` }}
      rowSpan={rowSpan > 1 ? rowSpan : undefined}
    >
      <div className="flex items-center gap-1">
        {row.hasChildren && (
          <button
            onClick={(e) => {
              e.stopPropagation();
              onToggleExpand(row.path);
            }}
            className="p-0.5 hover:bg-accent rounded"
          >
            {row.isExpanded ? (
              <ChevronDown className="h-3.5 w-3.5" />
            ) : (
              <ChevronRight className="h-3.5 w-3.5" />
            )}
          </button>
        )}
        {!row.hasChildren && <span className="w-4" />}
        <span>{row.caption}</span>
      </div>
    </td>
  );
};

// 데이터 셀
interface DataCellProps {
  values: PivotCellValue[];
  isTotal?: boolean;
  isSelected?: boolean;
  onClick?: (e?: React.MouseEvent) => void;
  onDoubleClick?: () => void;
  conditionalStyle?: CellFormatStyle;
}

const DataCell: React.FC<DataCellProps> = ({
  values,
  isTotal = false,
  isSelected = false,
  onClick,
  onDoubleClick,
  conditionalStyle,
}) => {
  // 조건부 서식 스타일 계산
  const cellStyle = conditionalStyle ? formatStyleToReact(conditionalStyle) : {};
  const hasDataBar = conditionalStyle?.dataBarWidth !== undefined;
  const icon = conditionalStyle?.icon;
  
  // 선택 상태 스타일
  const selectedClass = isSelected && "ring-2 ring-primary ring-inset bg-primary/10";

  if (!values || values.length === 0) {
    return (
      <td
        className={cn(
          "border-r border-b border-border",
          "px-2 py-1.5 text-right text-sm",
          isTotal && "bg-primary/5 font-medium",
          selectedClass
        )}
        style={cellStyle}
        onClick={onClick}
        onDoubleClick={onDoubleClick}
      >
        0
      </td>
    );
  }

  // 툴팁 내용 생성
  const tooltipContent = values.map((v) => 
    `${v.field || "값"}: ${v.formattedValue || v.value}`
  ).join("\n");

  // 단일 데이터 필드인 경우
  if (values.length === 1) {
    return (
      <td
        className={cn(
          "border-r border-b border-border relative group/cell",
          "px-2 py-1.5 text-right text-sm tabular-nums",
          isTotal && "bg-primary/5 font-medium",
          (onClick || onDoubleClick) && "cursor-pointer hover:bg-accent/50",
          selectedClass
        )}
        style={cellStyle}
        onClick={onClick}
        onDoubleClick={onDoubleClick}
        title={tooltipContent}
      >
        {/* Data Bar */}
        {hasDataBar && (
          <div
            className="absolute inset-y-0 left-0 opacity-30"
            style={{
              width: `${conditionalStyle?.dataBarWidth}%`,
              backgroundColor: conditionalStyle?.dataBarColor || "#3b82f6",
            }}
          />
        )}
        <span className="relative z-10 flex items-center justify-end gap-1">
          {icon && <span>{icon}</span>}
          {values[0].formattedValue || (values[0].value === 0 ? '0' : values[0].formattedValue)}
        </span>
      </td>
    );
  }

  // 다중 데이터 필드인 경우
  return (
    <>
      {values.map((val, idx) => (
        <td
          key={idx}
          className={cn(
            "border-r border-b border-border relative",
            "px-2 py-1.5 text-right text-sm tabular-nums",
            isTotal && "bg-primary/5 font-medium",
            (onClick || onDoubleClick) && "cursor-pointer hover:bg-accent/50",
            selectedClass
          )}
          style={cellStyle}
          onClick={onClick}
          onDoubleClick={onDoubleClick}
          title={`${val.field || "값"}: ${val.formattedValue || val.value}`}
        >
          {hasDataBar && (
            <div
              className="absolute inset-y-0 left-0 opacity-30"
              style={{
                width: `${conditionalStyle?.dataBarWidth}%`,
                backgroundColor: conditionalStyle?.dataBarColor || "#3b82f6",
              }}
            />
          )}
          <span className="relative z-10 flex items-center justify-end gap-1">
            {icon && <span>{icon}</span>}
            {val.formattedValue || (val.value === 0 ? '0' : val.formattedValue)}
          </span>
        </td>
      ))}
    </>
  );
};

// ==================== 메인 컴포넌트 ====================

export const PivotGridComponent: React.FC<PivotGridProps> = ({
  title,
  fields: initialFields = [],
  totals = {
    showRowGrandTotals: true,
    showColumnGrandTotals: true,
    showRowTotals: true,
    showColumnTotals: true,
  },
  style = {
    theme: "default",
    headerStyle: "default",
    cellPadding: "normal",
    borderStyle: "light",
    alternateRowColors: true,
    highlightTotals: true,
  },
  fieldChooser,
  chart: chartConfig,
  allowExpandAll = true,
  height = "auto",
  maxHeight,
  exportConfig,
  data: externalData,
  onCellClick,
  onCellDoubleClick,
  onFieldDrop,
  onExpandChange,
}) => {
  // ==================== 상태 ====================

  const [fields, setFields] = useState<PivotFieldConfig[]>(initialFields);
  // 초기 필드 설정 저장 (초기화용)
  const initialFieldsRef = useRef<PivotFieldConfig[]>(initialFields);
  const [pivotState, setPivotState] = useState<PivotGridState>({
    expandedRowPaths: [],
    expandedColumnPaths: [],
    sortConfig: null,
    filterConfig: {},
  });
  
  // 🆕 초기 로드 시 자동 확장 (첫 레벨만)
  const [isInitialExpanded, setIsInitialExpanded] = useState(false);
  const [isFullscreen, setIsFullscreen] = useState(false);
  const [showFieldPanel, setShowFieldPanel] = useState(false); // 기본적으로 접힌 상태
  const [showFieldChooser, setShowFieldChooser] = useState(false);
  const [drillDownData, setDrillDownData] = useState<{
    open: boolean;
    cellData: PivotCellData | null;
  }>({ open: false, cellData: null });
  const [showChart, setShowChart] = useState(chartConfig?.enabled || false);
  const [containerHeight, setContainerHeight] = useState(400);
  const tableContainerRef = useRef<HTMLDivElement>(null);
  
  // 셀 선택 상태 (범위 선택 지원)
  const [selectedCell, setSelectedCell] = useState<{
    rowIndex: number;
    colIndex: number;
  } | null>(null);
  const [selectionRange, setSelectionRange] = useState<{
    startRow: number;
    startCol: number;
    endRow: number;
    endCol: number;
  } | null>(null);
  const tableRef = useRef<HTMLTableElement>(null);
  
  // 정렬 상태
  const [sortConfig, setSortConfig] = useState<{
    field: string;
    direction: "asc" | "desc";
  } | null>(null);
  
  // 열 너비 상태
  const [columnWidths, setColumnWidths] = useState<Record<number, number>>({});
  const [resizingColumn, setResizingColumn] = useState<number | null>(null);
  const [resizeStartX, setResizeStartX] = useState<number>(0);
  const [resizeStartWidth, setResizeStartWidth] = useState<number>(0);

  // 상태 저장 키
  const stateStorageKey = `pivot-state-${title || "default"}`;
  const persistSettingKey = `pivot-persist-${title || "default"}`;
  
  // 상태 유지 설정 (체크박스용)
  const [persistState, setPersistState] = useState<boolean>(() => {
    if (typeof window === "undefined") return true;
    const saved = localStorage.getItem(persistSettingKey);
    return saved !== null ? saved === "true" : true; // 기본값 true
  });
  
  // 복원 완료 여부 (initialFields 덮어쓰기 방지)
  const [isStateRestored, setIsStateRestored] = useState(false);
  
  // 상태 복원 (localStorage) - 마운트 시 한 번만 실행
  useEffect(() => {
    if (typeof window === "undefined") return;
    
    // 상태 유지가 꺼져 있으면 복원하지 않음
    if (!persistState) {
      localStorage.removeItem(stateStorageKey);
      setIsStateRestored(true);
      return;
    }
    
    try {
      const savedState = localStorage.getItem(stateStorageKey);
      if (!savedState) {
        setIsStateRestored(true);
        return;
      }
      
      const parsed = JSON.parse(savedState);
      
      // 버전 체크 - 버전이 다르면 이전 상태 무시
      if (parsed.version !== PIVOT_STATE_VERSION) {
        localStorage.removeItem(stateStorageKey);
        setIsStateRestored(true);
        return;
      }
      
      // 필드 복원 시 유효성 검사 (중요!)
      if (parsed.fields && Array.isArray(parsed.fields) && parsed.fields.length > 0) {
        // 저장된 필드가 현재 데이터와 호환되는지 확인
        const validFields = parsed.fields.filter((f: PivotFieldConfig) => 
          f && typeof f.field === "string" && typeof f.area === "string"
        );
        
        if (validFields.length > 0) {
          setFields(validFields);
        }
      }
      
      // pivotState 복원 시 유효성 검사 (확장 경로 검증)
      if (parsed.pivotState && typeof parsed.pivotState === "object") {
        const restoredState: PivotGridState = {
          // expandedRowPaths는 배열의 배열이어야 함
          expandedRowPaths: Array.isArray(parsed.pivotState.expandedRowPaths) 
            ? parsed.pivotState.expandedRowPaths.filter(
                (p: unknown) => Array.isArray(p) && p.every(item => typeof item === "string")
              )
            : [],
          // expandedColumnPaths도 동일하게 검증
          expandedColumnPaths: Array.isArray(parsed.pivotState.expandedColumnPaths)
            ? parsed.pivotState.expandedColumnPaths.filter(
                (p: unknown) => Array.isArray(p) && p.every(item => typeof item === "string")
              )
            : [],
          sortConfig: parsed.pivotState.sortConfig || null,
          filterConfig: parsed.pivotState.filterConfig || {},
        };
        setPivotState(restoredState);
      }
      
      if (parsed.sortConfig) setSortConfig(parsed.sortConfig);
      if (parsed.columnWidths && typeof parsed.columnWidths === "object") {
        setColumnWidths(parsed.columnWidths);
      }
    } catch (e) {
      console.warn("피벗 상태 복원 실패, localStorage 초기화:", e);
      // 손상된 상태는 제거
      localStorage.removeItem(stateStorageKey);
    }
    
    setIsStateRestored(true);
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []); // 마운트 시 한 번만 실행
  
  // 외부 fields 변경 시 동기화 (복원이 완료된 후에만, 저장된 상태가 없을 때만)
  useEffect(() => {
    if (!isStateRestored) return; // 복원 완료 전에는 무시
    
    // 저장된 상태가 있으면 initialFields로 덮어쓰지 않음
    if (typeof window !== "undefined") {
      const savedState = localStorage.getItem(stateStorageKey);
      if (savedState) return; // 이미 저장된 상태가 있으면 무시
    }
    
    if (initialFields.length > 0) {
      setFields(initialFields);
    }
  // persistState는 의존성에서 제외 - 체크박스 변경 시 현재 상태 유지
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [initialFields, isStateRestored, stateStorageKey]);
  
  // 상태 유지 설정 저장 + 켜질 때 현재 상태 즉시 저장
  useEffect(() => {
    if (typeof window === "undefined") return;
    localStorage.setItem(persistSettingKey, String(persistState));
    
    // 상태 유지를 켜면 현재 상태를 즉시 저장
    if (persistState && isStateRestored && fields.length > 0) {
      const stateToSave = {
        version: PIVOT_STATE_VERSION,
        fields,
        pivotState,
        sortConfig,
        columnWidths,
      };
      localStorage.setItem(stateStorageKey, JSON.stringify(stateToSave));
    }
    
    // 상태 유지를 끄면 저장된 상태 삭제
    if (!persistState) {
      localStorage.removeItem(stateStorageKey);
    }
  }, [persistState, persistSettingKey, isStateRestored, fields, pivotState, sortConfig, columnWidths, stateStorageKey]);
  
  // 상태 저장 (localStorage)
  const saveStateToStorage = useCallback(() => {
    if (typeof window === "undefined" || !persistState) return;
    const stateToSave = {
      version: PIVOT_STATE_VERSION,
      fields,
      pivotState,
      sortConfig,
      columnWidths,
    };
    localStorage.setItem(stateStorageKey, JSON.stringify(stateToSave));
  }, [fields, pivotState, sortConfig, columnWidths, stateStorageKey, persistState]);
  
  // 상태 변경 시 자동 저장 (복원 완료 후에만)
  useEffect(() => {
    if (!persistState || !isStateRestored) return;
    // 초기 로드 후에만 저장 (빈 필드일 때는 저장 안 함)
    if (fields.length > 0) {
      saveStateToStorage();
    }
  }, [fields, pivotState, sortConfig, columnWidths, persistState, isStateRestored, saveStateToStorage]);

  // 데이터
  const data = externalData || [];

  // ==================== 필드 분류 ====================

  const rowFields = useMemo(
    () =>
      fields
        .filter((f) => f.area === "row" && f.visible !== false)
        .sort((a, b) => (a.areaIndex || 0) - (b.areaIndex || 0)),
    [fields]
  );

  const columnFields = useMemo(
    () =>
      fields
        .filter((f) => f.area === "column" && f.visible !== false)
        .sort((a, b) => (a.areaIndex || 0) - (b.areaIndex || 0)),
    [fields]
  );

  const dataFields = useMemo(
    () =>
      fields
        .filter((f) => f.area === "data" && f.visible !== false)
        .sort((a, b) => (a.areaIndex || 0) - (b.areaIndex || 0)),
    [fields]
  );

  // 필터 영역 필드
  const filterFields = useMemo(
    () => {
      const result = fields
        .filter((f) => f.area === "filter" && f.visible !== false)
        .sort((a, b) => (a.areaIndex || 0) - (b.areaIndex || 0));
      return result;
    },
    [fields]
  );

  // 사용 가능한 필드 목록 (FieldChooser용)
  const availableFields = useMemo(() => {
    if (data.length === 0) return [];

    const sampleRow = data[0];
    return Object.keys(sampleRow).map((key) => {
      const existingField = fields.find((f) => f.field === key);
      const value = sampleRow[key];

      // 데이터 타입 추론
      let dataType: "string" | "number" | "date" | "boolean" = "string";
      if (typeof value === "number") dataType = "number";
      else if (typeof value === "boolean") dataType = "boolean";
      else if (value instanceof Date) dataType = "date";
      else if (typeof value === "string") {
        // 날짜 문자열 감지
        if (/^\d{4}-\d{2}-\d{2}/.test(value)) dataType = "date";
      }

      return {
        field: key,
        caption: existingField?.caption || key,
        dataType,
        isSelected: existingField?.visible !== false,
        currentArea: existingField?.area,
      };
    });
  }, [data, fields]);

  // ==================== 필터 적용 ====================

  const filteredData = useMemo(() => {
    if (!data || data.length === 0) return data;

    // 모든 영역(행/열/필터)의 필터 값이 있는 필드로 데이터 필터링
    const activeFilters = fields.filter(
      (f) => f.filterValues && f.filterValues.length > 0
    );

    if (activeFilters.length === 0) return data;

    const result = data.filter((row) => {
      return activeFilters.every((filter) => {
        const rawValue = row[filter.field];
        const filterValues = filter.filterValues || [];
        const filterType = filter.filterType || "include";

        // 타입 안전한 비교: 값을 문자열로 변환하여 비교
        const value = rawValue === null || rawValue === undefined 
          ? "(빈 값)" 
          : String(rawValue);

        if (filterType === "include") {
          return filterValues.some((fv) => String(fv) === value);
        } else {
          return filterValues.every((fv) => String(fv) !== value);
        }
      });
    });

    // 모든 데이터가 필터링되면 경고 (디버깅용)
    if (result.length === 0 && data.length > 0) {
      console.warn("⚠️ [PivotGrid] 필터로 인해 모든 데이터가 제거됨");
    }

    return result;
  }, [data, fields]);

  // ==================== 피벗 처리 ====================

  const pivotResult = useMemo<PivotResult | null>(() => {
    try {
      if (!filteredData || filteredData.length === 0 || fields.length === 0) {
        return null;
      }

      // FieldChooser에서 이미 필드를 완전히 제거하므로 visible 필터링 불필요
      // 행, 열, 데이터 영역에 필드가 하나도 없으면 null 반환 (필터는 제외)
      if (fields.filter((f) => ["row", "column", "data"].includes(f.area)).length === 0) {
        return null;
      }

      const result = processPivotData(
        filteredData,
        fields,
        pivotState.expandedRowPaths,
        pivotState.expandedColumnPaths
      );
      
      return result;
    } catch (error) {
      console.error("❌ [pivotResult] 피벗 처리 에러:", error);
      return null;
    }
  }, [filteredData, fields, pivotState.expandedRowPaths, pivotState.expandedColumnPaths]);
  
  // 초기 로드 시 첫 레벨 자동 확장
  useEffect(() => {
    try {
      if (pivotResult && pivotResult.flatRows && pivotResult.flatRows.length > 0 && !isInitialExpanded) {
        // 첫 레벨 행들의 경로 수집 (level 0인 행들)
        const firstLevelRows = pivotResult.flatRows.filter((row) => row.level === 0 && row.hasChildren);

        // 첫 레벨 행이 있으면 자동 확장
        if (firstLevelRows.length > 0 && firstLevelRows.length < 100) {
          const firstLevelPaths = firstLevelRows.map((row) => row.path);
          setPivotState((prev) => ({
            ...prev,
            expandedRowPaths: firstLevelPaths,
          }));
          setIsInitialExpanded(true);
        } else {
          // 행이 너무 많으면 자동 확장 건너뛰기
          setIsInitialExpanded(true);
        }
      }
    } catch (error) {
      console.error("❌ [초기 확장] 에러:", error);
      setIsInitialExpanded(true);
    }
  }, [pivotResult, isInitialExpanded]);

  // 조건부 서식용 전체 값 수집
  const allCellValues = useMemo(() => {
    if (!pivotResult) return new Map<string, number[]>();

    const valuesByField = new Map<string, number[]>();

    // 데이터 매트릭스에서 모든 값 수집
    pivotResult.dataMatrix.forEach((values) => {
      values.forEach((val) => {
        if (val.field && typeof val.value === "number" && !isNaN(val.value)) {
          const existing = valuesByField.get(val.field) || [];
          existing.push(val.value);
          valuesByField.set(val.field, existing);
        }
      });
    });

    // 행 총계 값 수집
    pivotResult.grandTotals.row.forEach((values) => {
      values.forEach((val) => {
        if (val.field && typeof val.value === "number" && !isNaN(val.value)) {
          const existing = valuesByField.get(val.field) || [];
          existing.push(val.value);
          valuesByField.set(val.field, existing);
        }
      });
    });

    // 열 총계 값 수집
    pivotResult.grandTotals.column.forEach((values) => {
      values.forEach((val) => {
        if (val.field && typeof val.value === "number" && !isNaN(val.value)) {
          const existing = valuesByField.get(val.field) || [];
          existing.push(val.value);
          valuesByField.set(val.field, existing);
        }
      });
    });

    return valuesByField;
  }, [pivotResult]);

  // ==================== 가상 스크롤 ====================
  
  const ROW_HEIGHT = 32; // 행 높이 (px)
  const VIRTUAL_SCROLL_THRESHOLD = 50; // 이 행 수 이상이면 가상 스크롤 활성화
  
  // 컨테이너 높이 측정
  useEffect(() => {
    if (!tableContainerRef.current) return;
    
    const observer = new ResizeObserver((entries) => {
      for (const entry of entries) {
        setContainerHeight(entry.contentRect.height);
      }
    });
    
    observer.observe(tableContainerRef.current);
    return () => observer.disconnect();
  }, []);
  
  // 열 크기 조절 중
  useEffect(() => {
    if (resizingColumn === null) return;
    
    const handleMouseMove = (e: MouseEvent) => {
      const diff = e.clientX - resizeStartX;
      const newWidth = Math.max(50, resizeStartWidth + diff); // 최소 50px
      setColumnWidths((prev) => ({
        ...prev,
        [resizingColumn]: newWidth,
      }));
    };
    
    const handleMouseUp = () => {
      setResizingColumn(null);
    };
    
    document.addEventListener("mousemove", handleMouseMove);
    document.addEventListener("mouseup", handleMouseUp);
    
    return () => {
      document.removeEventListener("mousemove", handleMouseMove);
      document.removeEventListener("mouseup", handleMouseUp);
    };
  }, [resizingColumn, resizeStartX, resizeStartWidth]);
  
  // 가상 스크롤 훅 사용
  const flatRows = pivotResult?.flatRows || [];
  
  // 정렬된 행 데이터
  const sortedFlatRows = useMemo(() => {
    if (!sortConfig || !pivotResult) return flatRows;
    
    const { field, direction } = sortConfig;
    const { dataMatrix, flatColumns } = pivotResult;
    
    // 각 행의 정렬 기준 값 계산
    const rowsWithSortValue = flatRows.map((row) => {
      let sortValue = 0;
      // 모든 열에 대해 해당 필드의 합계 계산
      flatColumns.forEach((col) => {
        const cellKey = `${pathToKey(row.path)}|||${pathToKey(col.path)}`;
        const values = dataMatrix.get(cellKey) || [];
        const targetValue = values.find((v) => v.field === field);
        if (targetValue?.value != null) {
          sortValue += targetValue.value;
        }
      });
      return { row, sortValue };
    });
    
    // 정렬
    rowsWithSortValue.sort((a, b) => {
      if (direction === "asc") {
        return a.sortValue - b.sortValue;
      }
      return b.sortValue - a.sortValue;
    });
    
    return rowsWithSortValue.map((item) => item.row);
  }, [flatRows, sortConfig, pivotResult]);
  
  const enableVirtualScroll = sortedFlatRows.length > VIRTUAL_SCROLL_THRESHOLD;
  
  const virtualScroll = useVirtualScroll({
    itemCount: sortedFlatRows.length,
    itemHeight: ROW_HEIGHT,
    containerHeight: containerHeight,
    overscan: 10,
  });
  
  // 가상 스크롤 적용된 행 데이터
  const visibleFlatRows = useMemo(() => {
    if (!enableVirtualScroll) return sortedFlatRows;
    return sortedFlatRows.slice(virtualScroll.startIndex, virtualScroll.endIndex + 1);
  }, [enableVirtualScroll, sortedFlatRows, virtualScroll.startIndex, virtualScroll.endIndex]);

  // 조건부 서식 스타일 계산 헬퍼
  const getCellConditionalStyle = useCallback(
    (value: number | undefined, field: string): CellFormatStyle => {
      if (!style?.conditionalFormats || style.conditionalFormats.length === 0) {
        return {};
      }
      const allValues = allCellValues.get(field) || [];
      return getConditionalStyle(value, field, style.conditionalFormats, allValues);
    },
    [style?.conditionalFormats, allCellValues]
  );

  // ==================== 이벤트 핸들러 ====================

  // 필드 변경
  const handleFieldsChange = useCallback(
    (newFields: PivotFieldConfig[]) => {
      setFields(newFields);
    },
    []
  );

  // 행 확장/축소
  const handleToggleRowExpand = useCallback(
    (path: string[]) => {
      setPivotState((prev) => {
        const pathKey = pathToKey(path);
        const existingIndex = prev.expandedRowPaths.findIndex(
          (p) => pathToKey(p) === pathKey
        );

        let newPaths: string[][];
        if (existingIndex >= 0) {
          newPaths = prev.expandedRowPaths.filter(
            (_, i) => i !== existingIndex
          );
        } else {
          newPaths = [...prev.expandedRowPaths, path];
        }

        onExpandChange?.(newPaths);

        return {
          ...prev,
          expandedRowPaths: newPaths,
        };
      });
    },
    [onExpandChange]
  );

  // 전체 확장 (재귀적으로 모든 레벨 확장)
  const handleExpandAll = useCallback(() => {
    try {
      if (!pivotResult) {
        return;
      }

      // 재귀적으로 모든 가능한 경로 생성
      const allRowPaths: string[][] = [];
      const rowFields = fields.filter((f) => f.area === "row" && f.visible !== false);
      
      // 행 필드가 없으면 종료
      if (rowFields.length === 0) {
        return;
      }
      
      // 데이터에서 모든 고유한 경로 추출
      const pathSet = new Set<string>();
      filteredData.forEach((item) => {
        // 마지막 레벨은 제외 (확장할 자식이 없으므로)
        for (let depth = 1; depth < rowFields.length; depth++) {
          const path = rowFields.slice(0, depth).map((f) => String(item[f.field] ?? ""));
          const pathKey = JSON.stringify(path);
          pathSet.add(pathKey);
        }
      });

      // Set을 배열로 변환 (최대 1000개로 제한하여 성능 보호)
      const MAX_PATHS = 1000;
      let count = 0;
      pathSet.forEach((pathKey) => {
        if (count < MAX_PATHS) {
          allRowPaths.push(JSON.parse(pathKey));
          count++;
        }
      });

      setPivotState((prev) => ({
        ...prev,
        expandedRowPaths: allRowPaths,
        expandedColumnPaths: [],
      }));
    } catch (error) {
      console.error("❌ [handleExpandAll] 에러:", error);
    }
  }, [pivotResult, fields, filteredData]);

  // 전체 축소
  const handleCollapseAll = useCallback(() => {
    setPivotState((prev) => ({
      ...prev,
      expandedRowPaths: [],
      expandedColumnPaths: [],
    }));
  }, []);

  // 셀 클릭
  const handleCellClick = useCallback(
    (rowPath: string[], colPath: string[], values: PivotCellValue[]) => {
      if (!onCellClick) return;

      const cellData: PivotCellData = {
        value: values[0]?.value,
        rowPath,
        columnPath: colPath,
        field: values[0]?.field,
      };

      onCellClick(cellData);
    },
    [onCellClick]
  );

  // 셀 더블클릭 (Drill Down)
  const handleCellDoubleClick = useCallback(
    (rowPath: string[], colPath: string[], values: PivotCellValue[]) => {
      const cellData: PivotCellData = {
        value: values[0]?.value,
        rowPath,
        columnPath: colPath,
        field: values[0]?.field,
      };

      // Drill Down 모달 열기
      setDrillDownData({ open: true, cellData });

      // 외부 콜백 호출
      if (onCellDoubleClick) {
        onCellDoubleClick(cellData);
      }
    },
    [onCellDoubleClick]
  );

  // CSV 내보내기
  const handleExportCSV = useCallback(() => {
    if (!pivotResult) return;

    const { flatRows, flatColumns, dataMatrix, grandTotals } = pivotResult;

    let csv = "";

    // 헤더 행
    const headerRow = [""].concat(
      flatColumns.map((col) => col.caption || "총계")
    );
    if (totals?.showRowGrandTotals) {
      headerRow.push("총계");
    }
    csv += headerRow.join(",") + "\n";

    // 데이터 행
    flatRows.forEach((row) => {
      const rowData = [row.caption];

      flatColumns.forEach((col) => {
        const cellKey = `${pathToKey(row.path)}|||${pathToKey(col.path)}`;
        const values = dataMatrix.get(cellKey);
        rowData.push(values?.[0]?.value?.toString() || "");
      });

      if (totals?.showRowGrandTotals) {
        const rowTotal = grandTotals.row.get(pathToKey(row.path));
        rowData.push(rowTotal?.[0]?.value?.toString() || "");
      }

      csv += rowData.join(",") + "\n";
    });

    // 열 총계 행
    if (totals?.showColumnGrandTotals) {
      const totalRow = ["총계"];
      flatColumns.forEach((col) => {
        const colTotal = grandTotals.column.get(pathToKey(col.path));
        totalRow.push(colTotal?.[0]?.value?.toString() || "");
      });
      if (totals?.showRowGrandTotals) {
        totalRow.push(grandTotals.grand[0]?.value?.toString() || "");
      }
      csv += totalRow.join(",") + "\n";
    }

    // 다운로드
    const blob = new Blob(["\uFEFF" + csv], {
      type: "text/csv;charset=utf-8;",
    });
    const link = document.createElement("a");
    link.href = URL.createObjectURL(blob);
    link.download = `${title || "pivot"}_export.csv`;
    link.click();
  }, [pivotResult, totals, title]);

  // Excel 내보내기
  const handleExportExcel = useCallback(async () => {
    if (!pivotResult) return;

    try {
      await exportPivotToExcel(pivotResult, fields, totals, {
        fileName: title || "pivot_export",
        title: title,
      });
    } catch (error) {
      console.error("Excel 내보내기 실패:", error);
    }
  }, [pivotResult, fields, totals, title]);
  
  // 인쇄 기능 (PDF 내보내기보다 먼저 정의해야 함)
  const handlePrint = useCallback(() => {
    if (typeof window === "undefined") return;
    
    const printContent = tableRef.current;
    if (!printContent) return;
    
    const printWindow = window.open("", "_blank");
    if (!printWindow) return;
    
    printWindow.document.write(`
      <!DOCTYPE html>
      <html>
        <head>
          <title>${title || "피벗 테이블"}</title>
          <style>
            body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; padding: 20px; }
            h1 { font-size: 18px; margin-bottom: 16px; }
            table { border-collapse: collapse; width: 100%; font-size: 12px; }
            th, td { border: 1px solid #ddd; padding: 6px 8px; text-align: left; }
            th { background-color: #f5f5f5; font-weight: 600; }
            td { text-align: right; }
            td:first-child { text-align: left; font-weight: 500; }
            tr:nth-child(even) { background-color: #fafafa; }
            .total-row { background-color: #e8e8e8 !important; font-weight: 600; }
            @media print {
              body { padding: 0; }
              table { page-break-inside: auto; }
              tr { page-break-inside: avoid; }
            }
          </style>
        </head>
        <body>
          <h1>${title || "피벗 테이블"}</h1>
          ${printContent.outerHTML}
        </body>
      </html>
    `);
    
    printWindow.document.close();
    printWindow.focus();
    setTimeout(() => {
      printWindow.print();
      printWindow.close();
    }, 250);
  }, [title]);
  
  // PDF 내보내기
  const handleExportPDF = useCallback(async () => {
    if (!pivotResult || !tableRef.current) return;
    
    try {
      // 동적 import로 jspdf와 html2canvas 로드
      const [{ default: jsPDF }, { default: html2canvas }] = await Promise.all([
        import("jspdf"),
        import("html2canvas"),
      ]);
      
      const canvas = await html2canvas(tableRef.current, {
        scale: 2,
        useCORS: true,
        logging: false,
      });
      
      const imgData = canvas.toDataURL("image/png");
      const pdf = new jsPDF({
        orientation: canvas.width > canvas.height ? "landscape" : "portrait",
        unit: "px",
        format: [canvas.width, canvas.height],
      });
      
      pdf.addImage(imgData, "PNG", 0, 0, canvas.width, canvas.height);
      pdf.save(`${title || "pivot"}_export.pdf`);
    } catch (error) {
      console.error("PDF 내보내기 실패:", error);
      // jspdf가 없으면 인쇄 대화상자로 대체
      handlePrint();
    }
  }, [pivotResult, title, handlePrint]);
  
  // 데이터 새로고침
  const [isRefreshing, setIsRefreshing] = useState(false);
  const handleRefreshData = useCallback(async () => {
    setIsRefreshing(true);
    // 외부 데이터 소스가 있으면 새로고침
    // 여기서는 상태만 초기화
    setPivotState({
      expandedRowPaths: [],
      expandedColumnPaths: [],
      sortConfig: null,
      filterConfig: {},
    });
    setSortConfig(null);
    setSelectedCell(null);
    setSelectionRange(null);
    setTimeout(() => setIsRefreshing(false), 500);
  }, []);
  
  // 상태 저장 버튼 핸들러
  const handleSaveState = useCallback(() => {
    saveStateToStorage();
    console.log("피벗 상태가 저장되었습니다.");
  }, [saveStateToStorage]);
  
  // 상태 초기화 (확장/축소, 정렬, 필터만 초기화, 필드 설정은 유지)
  const handleResetState = useCallback(() => {
    // 로컬 스토리지에서 상태 제거 (SSR 보호)
    if (typeof window !== "undefined") {
      localStorage.removeItem(stateStorageKey);
    }
    
    // 확장/축소, 정렬, 필터 상태만 초기화
    setPivotState({
      expandedRowPaths: [],
      expandedColumnPaths: [],
      sortConfig: null,
      filterConfig: {},
    });
    setSortConfig(null);
    setColumnWidths({});
    setSelectedCell(null);
    setSelectionRange(null);
  }, [stateStorageKey]);
  
  // 필드 숨기기/표시 상태
  const [hiddenFields, setHiddenFields] = useState<Set<string>>(new Set());
  
  const toggleFieldVisibility = useCallback((fieldName: string) => {
    setHiddenFields((prev) => {
      const newSet = new Set(prev);
      if (newSet.has(fieldName)) {
        newSet.delete(fieldName);
      } else {
        newSet.add(fieldName);
      }
      return newSet;
    });
  }, []);
  
  // 숨겨진 필드 목록
  const hiddenFieldsList = useMemo(() => {
    return fields.filter((f) => hiddenFields.has(f.field));
  }, [fields, hiddenFields]);
  
  // 모든 필드 표시
  const showAllFields = useCallback(() => {
    setHiddenFields(new Set());
  }, []);

  // ==================== 렌더링 ====================

  // 빈 상태
  if (!data || data.length === 0) {
    return (
      <div
        className={cn(
          "flex flex-col items-center justify-center",
          "p-8 text-center text-muted-foreground",
          "border border-dashed border-border rounded-lg"
        )}
      >
        <RefreshCw className="h-8 w-8 mb-2 opacity-50" />
        <p className="text-sm">데이터가 없습니다</p>
        <p className="text-xs mt-1">데이터를 로드하거나 필드를 설정해주세요</p>
      </div>
    );
  }

  // 필드 미설정 (행, 열, 데이터 영역에 필드가 있는지 확인)
  const hasActiveFields = fields.some(
    (f) => f.visible !== false && ["row", "column", "data"].includes(f.area)
  );
  if (!hasActiveFields) {
    return (
      <div
        className={cn(
          "flex flex-col",
          "border border-border rounded-lg overflow-hidden bg-background"
        )}
      >
        {/* 필드 패널 */}
        <FieldPanel
          fields={fields}
          onFieldsChange={handleFieldsChange}
          collapsed={!showFieldPanel}
          onToggleCollapse={() => setShowFieldPanel(!showFieldPanel)}
          initialFields={initialFieldsRef.current}
        />

        {/* 안내 메시지 */}
        <div className="flex flex-col items-center justify-center p-8 text-center text-muted-foreground">
          <Settings className="h-8 w-8 mb-2 opacity-50" />
          <p className="text-sm">필드가 설정되지 않았습니다</p>
          <p className="text-xs mt-1">
            행, 열, 데이터 영역에 필드를 배치해주세요
          </p>
          <Button
            variant="outline"
            size="sm"
            className="mt-4"
            onClick={() => setShowFieldChooser(true)}
          >
            <LayoutGrid className="h-4 w-4 mr-2" />
            필드 선택기 열기
          </Button>
        </div>

        {/* 필드 선택기 모달 */}
        <FieldChooser
          open={showFieldChooser}
          onOpenChange={setShowFieldChooser}
          availableFields={availableFields}
          selectedFields={fields}
          onFieldsChange={handleFieldsChange}
        />
      </div>
    );
  }

  // 피벗 결과 없음
  if (!pivotResult) {
    return (
      <div className="flex items-center justify-center p-8">
        <RefreshCw className="h-5 w-5 animate-spin" />
      </div>
    );
  }

  const { flatColumns, dataMatrix, grandTotals, columnHeaderLevels } = pivotResult;

  // ==================== 키보드 네비게이션 ====================
  
  // 키보드 핸들러
  const handleKeyDown = (e: React.KeyboardEvent) => {
    if (!selectedCell) return;
    
    const { rowIndex, colIndex } = selectedCell;
    const maxRowIndex = visibleFlatRows.length - 1;
    const maxColIndex = flatColumns.length - 1;
    
    let newRowIndex = rowIndex;
    let newColIndex = colIndex;
    
    switch (e.key) {
      case "ArrowUp":
        e.preventDefault();
        newRowIndex = Math.max(0, rowIndex - 1);
        break;
      case "ArrowDown":
        e.preventDefault();
        newRowIndex = Math.min(maxRowIndex, rowIndex + 1);
        break;
      case "ArrowLeft":
        e.preventDefault();
        newColIndex = Math.max(0, colIndex - 1);
        break;
      case "ArrowRight":
        e.preventDefault();
        newColIndex = Math.min(maxColIndex, colIndex + 1);
        break;
      case "Home":
        e.preventDefault();
        if (e.ctrlKey) {
          newRowIndex = 0;
          newColIndex = 0;
        } else {
          newColIndex = 0;
        }
        break;
      case "End":
        e.preventDefault();
        if (e.ctrlKey) {
          newRowIndex = maxRowIndex;
          newColIndex = maxColIndex;
        } else {
          newColIndex = maxColIndex;
        }
        break;
      case "PageUp":
        e.preventDefault();
        newRowIndex = Math.max(0, rowIndex - 10);
        break;
      case "PageDown":
        e.preventDefault();
        newRowIndex = Math.min(maxRowIndex, rowIndex + 10);
        break;
      case "Enter":
        e.preventDefault();
        // 셀 더블클릭과 동일한 동작 (드릴다운)
        if (visibleFlatRows[rowIndex] && flatColumns[colIndex]) {
          const row = visibleFlatRows[rowIndex];
          const col = flatColumns[colIndex];
          const cellKey = `${pathToKey(row.path)}|||${pathToKey(col.path)}`;
          const values = dataMatrix.get(cellKey) || [];
          // 드릴다운 모달 열기
          const cellData: PivotCellData = {
            value: values[0]?.value,
            rowPath: row.path,
            columnPath: col.path,
            field: values[0]?.field,
          };
          setDrillDownData({ open: true, cellData });
        }
        break;
      case "Escape":
        e.preventDefault();
        setSelectedCell(null);
        setSelectionRange(null);
        break;
      case "c":
        // Ctrl+C: 클립보드 복사
        if (e.ctrlKey || e.metaKey) {
          e.preventDefault();
          copySelectionToClipboard();
        }
        return;
      case "a":
        // Ctrl+A: 전체 선택
        if (e.ctrlKey || e.metaKey) {
          e.preventDefault();
          setSelectionRange({
            startRow: 0,
            startCol: 0,
            endRow: visibleFlatRows.length - 1,
            endCol: flatColumns.length - 1,
          });
        }
        return;
      default:
        return;
    }
    
    if (newRowIndex !== rowIndex || newColIndex !== colIndex) {
      setSelectedCell({ rowIndex: newRowIndex, colIndex: newColIndex });
    }
  };
  
  // 셀 클릭으로 선택 (Shift+클릭으로 범위 선택)
  const handleCellSelect = (rowIndex: number, colIndex: number, shiftKey: boolean = false) => {
    if (shiftKey && selectedCell) {
      // Shift+클릭: 범위 선택
      setSelectionRange({
        startRow: Math.min(selectedCell.rowIndex, rowIndex),
        startCol: Math.min(selectedCell.colIndex, colIndex),
        endRow: Math.max(selectedCell.rowIndex, rowIndex),
        endCol: Math.max(selectedCell.colIndex, colIndex),
      });
    } else {
      // 일반 클릭: 단일 선택
      setSelectedCell({ rowIndex, colIndex });
      setSelectionRange(null);
    }
  };
  
  // 셀이 선택 범위 내에 있는지 확인
  const isCellInRange = (rowIndex: number, colIndex: number): boolean => {
    if (selectionRange) {
      return (
        rowIndex >= selectionRange.startRow &&
        rowIndex <= selectionRange.endRow &&
        colIndex >= selectionRange.startCol &&
        colIndex <= selectionRange.endCol
      );
    }
    if (selectedCell) {
      return selectedCell.rowIndex === rowIndex && selectedCell.colIndex === colIndex;
    }
    return false;
  };
  
  // 열 크기 조절 시작
  const handleResizeStart = (colIdx: number, e: React.MouseEvent) => {
    e.preventDefault();
    e.stopPropagation();
    setResizingColumn(colIdx);
    setResizeStartX(e.clientX);
    setResizeStartWidth(columnWidths[colIdx] || 100);
  };
  
  // 클립보드에 선택 영역 복사
  const copySelectionToClipboard = () => {
    const range = selectionRange || (selectedCell ? {
      startRow: selectedCell.rowIndex,
      startCol: selectedCell.colIndex,
      endRow: selectedCell.rowIndex,
      endCol: selectedCell.colIndex,
    } : null);
    
    if (!range) return;
    
    const lines: string[] = [];
    
    for (let rowIdx = range.startRow; rowIdx <= range.endRow; rowIdx++) {
      const row = visibleFlatRows[rowIdx];
      if (!row) continue;
      
      const rowValues: string[] = [];
      for (let colIdx = range.startCol; colIdx <= range.endCol; colIdx++) {
        const col = flatColumns[colIdx];
        if (!col) continue;
        
        const cellKey = `${pathToKey(row.path)}|||${pathToKey(col.path)}`;
        const values = dataMatrix.get(cellKey) || [];
        const cellValue = values.map((v) => v.formattedValue || v.value || "").join(", ");
        rowValues.push(cellValue);
      }
      lines.push(rowValues.join("\t"));
    }
    
    const text = lines.join("\n");
    navigator.clipboard.writeText(text).then(() => {
      // 복사 성공 피드백 (선택적)
      console.log("클립보드에 복사됨:", text);
    }).catch((err) => {
      console.error("클립보드 복사 실패:", err);
    });
  };
  
  // 정렬 토글
  const handleSort = (field: string) => {
    setSortConfig((prev) => {
      if (prev?.field === field) {
        // 같은 필드 클릭: asc -> desc -> null 순환
        if (prev.direction === "asc") {
          return { field, direction: "desc" };
        }
        return null; // 정렬 해제
      }
      // 새로운 필드: asc로 시작
      return { field, direction: "asc" };
    });
  };
  
  // 정렬 아이콘 렌더링
  const SortIcon = ({ field }: { field: string }) => {
    if (sortConfig?.field !== field) {
      return <ArrowUpDown className="h-3 w-3 opacity-30" />;
    }
    if (sortConfig.direction === "asc") {
      return <ArrowUp className="h-3 w-3 text-primary" />;
    }
    return <ArrowDown className="h-3 w-3 text-primary" />;
  };

  return (
    <div
      className={cn(
        "flex flex-col",
        "border border-border rounded-lg overflow-hidden",
        "bg-background",
        isFullscreen && "fixed inset-4 z-50 shadow-2xl"
      )}
      style={{
        height: isFullscreen ? "auto" : height,
        maxHeight: isFullscreen ? "none" : maxHeight,
      }}
    >
      {/* 필드 패널 - 항상 렌더링 (collapsed 상태로 접기/펼치기 제어) */}
      <FieldPanel
        fields={fields}
        onFieldsChange={handleFieldsChange}
        collapsed={!showFieldPanel}
        onToggleCollapse={() => setShowFieldPanel(!showFieldPanel)}
        initialFields={initialFieldsRef.current}
      />

      {/* 헤더 툴바 */}
      <div className="flex items-center justify-between px-3 py-2 border-b border-border bg-muted/30">
        <div className="flex items-center gap-2">
          {title && <h3 className="text-sm font-medium">{title}</h3>}
          <span className="text-xs text-muted-foreground">
            ({filteredData.length !== data.length 
              ? `${filteredData.length} / ${data.length}건` 
              : `${data.length}건`})
          </span>
        </div>

        <div className="flex items-center gap-1">
          {/* 필드 선택기 버튼 */}
          {fieldChooser?.enabled !== false && (
            <Button
              variant="ghost"
              size="sm"
              className="h-7 px-2"
              onClick={() => setShowFieldChooser(true)}
              title="필드 선택기"
            >
              <LayoutGrid className="h-4 w-4" />
            </Button>
          )}

          {/* 필드 패널 토글 */}
          <Button
            variant="ghost"
            size="sm"
            className="h-7 px-2"
            onClick={() => setShowFieldPanel(!showFieldPanel)}
            title={showFieldPanel ? "필드 패널 숨기기" : "필드 패널 보기"}
          >
            <Settings className="h-4 w-4" />
          </Button>

          {allowExpandAll && (
            <>
              <Button
                variant="ghost"
                size="sm"
                className="h-7 px-2"
                onClick={handleCollapseAll}
                title="전체 축소"
              >
                <ChevronDown className="h-4 w-4" />
              </Button>

              <Button
                variant="ghost"
                size="sm"
                className="h-7 px-2"
                onClick={handleExpandAll}
                title="전체 확장"
              >
                <ChevronRight className="h-4 w-4" />
              </Button>
            </>
          )}
          
          {/* 상태 유지 체크박스 */}
          <div className="flex items-center gap-1.5 ml-2 pl-2 border-l">
            <Checkbox
              id="persist-state"
              checked={persistState}
              onCheckedChange={(checked) => setPersistState(checked === true)}
              className="h-3.5 w-3.5"
            />
            <Label
              htmlFor="persist-state"
              className="text-xs text-muted-foreground cursor-pointer whitespace-nowrap"
            >
              상태 유지
            </Label>
          </div>

          {/* 차트 토글 */}
          {chartConfig && (
            <Button
              variant={showChart ? "secondary" : "ghost"}
              size="sm"
              className="h-7 px-2"
              onClick={() => setShowChart(!showChart)}
              title={showChart ? "차트 숨기기" : "차트 보기"}
            >
              <BarChart3 className="h-4 w-4" />
            </Button>
          )}

          {/* 내보내기 버튼들 */}
          {exportConfig?.excel && (
            <>
              <Button
                variant="ghost"
                size="sm"
                className="h-7 px-2"
                onClick={handleExportCSV}
                title="CSV 내보내기"
              >
                <Download className="h-4 w-4" />
              </Button>
              <Button
                variant="ghost"
                size="sm"
                className="h-7 px-2"
                onClick={handleExportExcel}
                title="Excel 내보내기"
              >
                <FileSpreadsheet className="h-4 w-4" />
              </Button>
              <Button
                variant="ghost"
                size="sm"
                className="h-7 px-2"
                onClick={handlePrint}
                title="인쇄"
              >
                <Printer className="h-4 w-4" />
              </Button>
              <Button
                variant="ghost"
                size="sm"
                className="h-7 px-2"
                onClick={handleExportPDF}
                title="PDF 내보내기"
              >
                <FileText className="h-4 w-4" />
              </Button>
            </>
          )}
          
          <Button
            variant="ghost"
            size="sm"
            className="h-7 px-2"
            onClick={handleRefreshData}
            disabled={isRefreshing}
            title="새로고침"
          >
            {isRefreshing ? (
              <Loader2 className="h-4 w-4 animate-spin" />
            ) : (
              <RefreshCw className="h-4 w-4" />
            )}
          </Button>
          
          <Button
            variant="ghost"
            size="sm"
            className="h-7 px-2"
            onClick={handleSaveState}
            title="상태 저장"
          >
            <Save className="h-4 w-4" />
          </Button>
          
          <Button
            variant="ghost"
            size="sm"
            className="h-7 px-2"
            onClick={handleResetState}
            title="상태 초기화"
          >
            <RotateCcw className="h-4 w-4" />
          </Button>

          {/* 숨겨진 필드 표시 드롭다운 */}
          {hiddenFieldsList.length > 0 && (
            <div className="relative group">
              <Button
                variant="ghost"
                size="sm"
                className="h-7 px-2"
                title={`숨겨진 필드: ${hiddenFieldsList.length}개`}
              >
                <EyeOff className="h-4 w-4" />
                <span className="ml-1 text-xs">{hiddenFieldsList.length}</span>
              </Button>
              <div className="absolute right-0 top-full mt-1 hidden group-hover:block z-50 bg-popover border border-border rounded-md shadow-lg min-w-[160px]">
                <div className="p-2 border-b border-border">
                  <span className="text-xs font-medium text-muted-foreground">숨겨진 필드</span>
                </div>
                <div className="max-h-[200px] overflow-y-auto">
                  {hiddenFieldsList.map((field) => (
                    <button
                      key={field.field}
                      className="w-full px-3 py-1.5 text-xs text-left hover:bg-accent flex items-center gap-2"
                      onClick={() => toggleFieldVisibility(field.field)}
                    >
                      <Eye className="h-3 w-3" />
                      <span>{field.caption || field.field}</span>
                    </button>
                  ))}
                </div>
                <div className="p-1 border-t border-border">
                  <button
                    className="w-full px-3 py-1.5 text-xs text-left hover:bg-accent text-primary"
                    onClick={showAllFields}
                  >
                    모두 표시
                  </button>
                </div>
              </div>
            </div>
          )}

          <Button
            variant="ghost"
            size="sm"
            className="h-7 px-2"
            onClick={() => setIsFullscreen(!isFullscreen)}
            title={isFullscreen ? "원래 크기" : "전체 화면"}
          >
            {isFullscreen ? (
              <Minimize2 className="h-4 w-4" />
            ) : (
              <Maximize2 className="h-4 w-4" />
            )}
          </Button>
        </div>
      </div>

      {/* 필터 바 - 필터 영역에 필드가 있을 때만 표시 */}
      {filterFields.length > 0 && (
        <div className="flex items-center gap-2 px-3 py-1.5 border-b border-border bg-amber-50/50 dark:bg-orange-950/10">
          <Filter className="h-3.5 w-3.5 text-amber-600 dark:text-orange-400" />
          <span className="text-xs font-medium text-orange-700 dark:text-orange-300">필터:</span>
          <div className="flex items-center gap-2 flex-wrap">
            {filterFields.map((filterField) => {
              const selectedValues = filterField.filterValues || [];
              const isFiltered = selectedValues.length > 0;

              return (
                <FilterPopup
                  key={filterField.field}
                  field={filterField}
                  data={data}
                  onFilterChange={(field, values, type) => {
                    const newFields = fields.map((f) =>
                      f.field === field.field && f.area === field.area
                        ? { ...f, filterValues: values, filterType: type }
                        : f
                    );
                    handleFieldsChange(newFields);
                  }}
                  trigger={
                    <button
                      className={cn(
                        "flex items-center gap-1.5 px-2 py-1 rounded text-xs",
                        "border transition-colors max-w-xs",
                        isFiltered
                          ? "bg-amber-100 border-orange-300 text-orange-800 dark:bg-orange-900/30 dark:border-orange-700 dark:text-orange-200"
                          : "bg-background border-border hover:bg-accent"
                      )}
                      title={isFiltered ? `${filterField.caption}: ${selectedValues.join(", ")}` : filterField.caption}
                    >
                      <span className="font-medium">{filterField.caption}:</span>
                      {isFiltered ? (
                        <span className="truncate">
                          {selectedValues.length <= 2 
                            ? selectedValues.join(", ")
                            : `${selectedValues.slice(0, 2).join(", ")} 외 ${selectedValues.length - 2}개`
                          }
                        </span>
                      ) : (
                        <span className="text-muted-foreground">전체</span>
                      )}
                      <ChevronDown className="h-3 w-3 shrink-0" />
                    </button>
                  }
                />
              );
            })}
          </div>
        </div>
      )}

      {/* 피벗 테이블 */}
      <div 
        ref={tableContainerRef}
        className="flex-1 overflow-auto focus:outline-none"
        style={{ 
          maxHeight: enableVirtualScroll && containerHeight > 0 ? containerHeight : undefined,
          // 최소 200px 보장 + 데이터에 맞게 조정 (최대 400px)
          minHeight: Math.max(
            200, // 절대 최소값 - 블라인드 효과 방지
            Math.min(400, (sortedFlatRows.length + 3) * ROW_HEIGHT + 50)
          )
        }}
        tabIndex={0}
        onKeyDown={handleKeyDown}
      >
        <table ref={tableRef} className="w-full border-collapse">
          <thead>
            {/* 다중 행 열 헤더 */}
            {columnHeaderLevels.length > 0 ? (
              // 열 필드가 있는 경우: 각 레벨별로 행 생성
              columnHeaderLevels.map((levelCells, levelIdx) => (
                <tr key={`col-level-${levelIdx}`} className="bg-background">
                  {/* 좌상단 코너 (첫 번째 레벨에만 표시) */}
                  {levelIdx === 0 && (
                    <th
                      className={cn(
                        "border-r border-b border-border",
                        "px-2 py-1 text-left text-xs font-medium",
                        "bg-background sticky left-0 top-0 z-20"
                      )}
                      rowSpan={columnHeaderLevels.length + (dataFields.length > 1 ? 1 : 0)}
                    >
                      <div className="flex items-center gap-1 flex-wrap">
                        {rowFields.map((f, idx) => (
                          <div key={f.field} className="flex items-center gap-0.5 group">
                            <span>{f.caption}</span>
                            <FilterPopup
                              field={f}
                              data={data}
                              onFilterChange={(field, values, type) => {
                                const newFields = fields.map((fld) =>
                                  fld.field === field.field && fld.area === "row"
                                    ? { ...fld, filterValues: values, filterType: type }
                                    : fld
                                );
                                handleFieldsChange(newFields);
                              }}
                              trigger={
                                <button
                                  className={cn(
                                    "p-0.5 rounded opacity-0 group-hover:opacity-100 transition-opacity",
                                    "hover:bg-accent",
                                    f.filterValues && f.filterValues.length > 0 && "opacity-100 text-primary"
                                  )}
                                >
                                  <Filter className="h-3 w-3" />
                                </button>
                              }
                            />
                            {idx < rowFields.length - 1 && <span className="mx-0.5 text-muted-foreground">/</span>}
                          </div>
                        ))}
                        {rowFields.length === 0 && <span>항목</span>}
                      </div>
                    </th>
                  )}

                  {/* 열 헤더 셀 - 해당 레벨 */}
                  {levelCells.map((cell, cellIdx) => (
                    <th
                      key={`${levelIdx}-${cellIdx}`}
                      className={cn(
                        "border-r border-b border-border relative",
                        "px-2 py-1 text-center text-xs font-medium",
                        "bg-background sticky top-0 z-10",
                        levelIdx === columnHeaderLevels.length - 1 && dataFields.length === 1 && "cursor-pointer hover:bg-accent/50"
                      )}
                      colSpan={cell.colSpan * (dataFields.length || 1)}
                    >
                      <div className="flex items-center justify-center gap-1">
                        <span>{cell.caption || "(전체)"}</span>
                        {levelIdx === columnHeaderLevels.length - 1 && dataFields.length === 1 && (
                          <SortIcon field={dataFields[0].field} />
                        )}
                      </div>
                    </th>
                  ))}

                  {/* 행 총계 헤더 (첫 번째 레벨에만 표시) */}
                  {levelIdx === 0 && totals?.showRowGrandTotals && (
                    <th
                      className={cn(
                        "border-b border-border",
                        "px-2 py-1 text-center text-xs font-medium",
                        "bg-background sticky top-0 z-10"
                      )}
                      colSpan={dataFields.length || 1}
                      rowSpan={columnHeaderLevels.length + (dataFields.length > 1 ? 1 : 0)}
                    >
                      총계
                    </th>
                  )}
                  
                  {/* 열 필드 필터 (첫 번째 레벨에만 표시) */}
                  {levelIdx === 0 && columnFields.length > 0 && (
                    <th
                      className={cn(
                        "border-b border-border",
                        "px-1 py-1 text-center text-xs",
                        "bg-background sticky top-0 z-10"
                      )}
                      rowSpan={columnHeaderLevels.length + (dataFields.length > 1 ? 1 : 0)}
                    >
                      <div className="flex flex-col gap-0.5">
                        {columnFields.map((f) => (
                          <FilterPopup
                            key={f.field}
                            field={f}
                            data={data}
                            onFilterChange={(field, values, type) => {
                              const newFields = fields.map((fld) =>
                                fld.field === field.field && fld.area === "column"
                                  ? { ...fld, filterValues: values, filterType: type }
                                  : fld
                              );
                              handleFieldsChange(newFields);
                            }}
                            trigger={
                              <button
                                className={cn(
                                  "p-0.5 rounded hover:bg-accent",
                                  f.filterValues && f.filterValues.length > 0 && "text-primary"
                                )}
                                title={`${f.caption} 필터`}
                              >
                                <Filter className="h-3 w-3" />
                              </button>
                            }
                          />
                        ))}
                      </div>
                    </th>
                  )}
                </tr>
              ))
            ) : (
              // 열 필드가 없는 경우: 단일 행
              <tr className="bg-background">
                <th
                  className={cn(
                    "border-r border-b border-border",
                    "px-2 py-1 text-left text-xs font-medium",
                    "bg-background sticky left-0 top-0 z-20"
                  )}
                  rowSpan={dataFields.length > 1 ? 2 : 1}
                >
                  <div className="flex items-center gap-1 flex-wrap">
                    {rowFields.map((f, idx) => (
                      <div key={f.field} className="flex items-center gap-0.5 group">
                        <span>{f.caption}</span>
                        <FilterPopup
                          field={f}
                          data={data}
                          onFilterChange={(field, values, type) => {
                            const newFields = fields.map((fld) =>
                              fld.field === field.field && fld.area === "row"
                                ? { ...fld, filterValues: values, filterType: type }
                                : fld
                            );
                            handleFieldsChange(newFields);
                          }}
                          trigger={
                            <button
                              className={cn(
                                "p-0.5 rounded opacity-0 group-hover:opacity-100 transition-opacity",
                                "hover:bg-accent",
                                f.filterValues && f.filterValues.length > 0 && "opacity-100 text-primary"
                              )}
                            >
                              <Filter className="h-3 w-3" />
                            </button>
                          }
                        />
                        {idx < rowFields.length - 1 && <span className="mx-0.5 text-muted-foreground">/</span>}
                      </div>
                    ))}
                    {rowFields.length === 0 && <span>항목</span>}
                  </div>
                </th>

                {/* 열 헤더 셀 (열 필드 없을 때) */}
                {flatColumns.map((col, idx) => (
                  <th
                    key={idx}
                    className={cn(
                      "border-r border-b border-border relative group",
                      "px-2 py-1 text-center text-xs font-medium",
                      "bg-background sticky top-0 z-10",
                      dataFields.length === 1 && "cursor-pointer hover:bg-accent/50"
                    )}
                    colSpan={dataFields.length || 1}
                    style={{ width: columnWidths[idx] || "auto", minWidth: 50 }}
                    onClick={dataFields.length === 1 ? () => handleSort(dataFields[0].field) : undefined}
                  >
                    <div className="flex items-center justify-center gap-1">
                      <span>{col.caption || "(전체)"}</span>
                      {dataFields.length === 1 && <SortIcon field={dataFields[0].field} />}
                    </div>
                    <div
                      className={cn(
                        "absolute right-0 top-0 bottom-0 w-1 cursor-col-resize",
                        "hover:bg-primary/50 transition-colors",
                        resizingColumn === idx && "bg-primary"
                      )}
                      onMouseDown={(e) => handleResizeStart(idx, e)}
                    />
                  </th>
                ))}

                {/* 행 총계 헤더 */}
                {totals?.showRowGrandTotals && (
                  <th
                    className={cn(
                      "border-b border-border",
                      "px-2 py-1 text-center text-xs font-medium",
                      "bg-background sticky top-0 z-10"
                    )}
                    colSpan={dataFields.length || 1}
                    rowSpan={dataFields.length > 1 ? 2 : 1}
                  >
                    총계
                  </th>
                )}
              </tr>
            )}

            {/* 데이터 필드 라벨 (다중 데이터 필드인 경우) */}
            {dataFields.length > 1 && (
              <tr className="bg-background">
                {flatColumns.map((col, colIdx) => (
                  <React.Fragment key={colIdx}>
                    {dataFields.map((df, dfIdx) => (
                      <th
                        key={`${colIdx}-${dfIdx}`}
                        className={cn(
                          "border-r border-b border-border",
                          "px-2 py-0.5 text-center text-xs font-normal",
                          "text-muted-foreground cursor-pointer hover:bg-accent/50"
                        )}
                        onClick={() => handleSort(df.field)}
                      >
                        <div className="flex items-center justify-center gap-1">
                          <span>{df.caption}</span>
                          <SortIcon field={df.field} />
                        </div>
                      </th>
                    ))}
                  </React.Fragment>
                ))}
              </tr>
            )}
          </thead>

          <tbody>
            {/* 열 총계 행 (상단 위치) */}
            {totals?.showColumnGrandTotals && totals?.rowGrandTotalPosition === "top" && (
              <tr className="bg-primary/5 font-medium">
                <td
                  className={cn(
                    "border-r border-b border-border",
                    "px-2 py-1.5 text-left text-sm",
                    "bg-primary/10 sticky left-0"
                  )}
                >
                  총계
                </td>

                {flatColumns.map((col, colIdx) => (
                  <DataCell
                    key={colIdx}
                    values={grandTotals.column.get(pathToKey(col.path)) || []}
                    isTotal
                  />
                ))}

                {/* 대총합 */}
                {totals?.showRowGrandTotals && (
                  <DataCell values={grandTotals.grand} isTotal />
                )}
              </tr>
            )}
            
            {/* 가상 스크롤 상단 여백 */}
            {enableVirtualScroll && virtualScroll.offsetTop > 0 && (
              <tr style={{ height: virtualScroll.offsetTop }}>
                <td colSpan={rowFields.length + flatColumns.length + (totals?.showRowGrandTotals ? dataFields.length : 0)} />
              </tr>
            )}
            
            {(() => {
              // 셀 병합 정보 계산
              const mergeInfo = calculateMergeCells(visibleFlatRows, style?.mergeCells || false);
              
              return visibleFlatRows.map((row, idx) => {
                // 실제 행 인덱스 계산
                const rowIdx = enableVirtualScroll ? virtualScroll.startIndex + idx : idx;
                const cellMerge = mergeInfo.get(idx) || { rowSpan: 1, skip: false };
                
                return (
                  <tr
                    key={rowIdx}
                    className={cn(
                      style?.alternateRowColors &&
                        rowIdx % 2 === 1 &&
                        "bg-muted/20"
                    )}
                    style={{ height: ROW_HEIGHT }}
                  >
                    {/* 행 헤더 (병합되면 skip) */}
                    {!cellMerge.skip && (
                      <RowHeaderCell
                        row={row}
                        rowFields={rowFields}
                        onToggleExpand={handleToggleRowExpand}
                        rowSpan={cellMerge.rowSpan}
                      />
                    )}

                  {/* 데이터 셀 */}
                  {flatColumns.map((col, colIdx) => {
                    const cellKey = `${pathToKey(row.path)}|||${pathToKey(col.path)}`;
                    const values = dataMatrix.get(cellKey) || [];

                    // 조건부 서식 (첫 번째 값 기준)
                    const conditionalStyle =
                      values.length > 0 && values[0].field
                        ? getCellConditionalStyle(values[0].value ?? undefined, values[0].field)
                        : undefined;
                    
                    // 선택 상태 확인 (범위 선택 포함)
                    const isCellSelected = isCellInRange(rowIdx, colIdx);

                    return (
                      <DataCell
                        key={colIdx}
                        values={values}
                        conditionalStyle={conditionalStyle}
                        isSelected={isCellSelected}
                        onClick={(e?: React.MouseEvent) => {
                          handleCellSelect(rowIdx, colIdx, e?.shiftKey || false);
                          if (onCellClick) {
                            handleCellClick(row.path, col.path, values);
                          }
                        }}
                        onDoubleClick={() =>
                          handleCellDoubleClick(row.path, col.path, values)
                        }
                      />
                    );
                  })}

                  {/* 행 총계 */}
                  {totals?.showRowGrandTotals && (
                    <DataCell
                      values={grandTotals.row.get(pathToKey(row.path)) || []}
                      isTotal
                    />
                  )}
                </tr>
                );
              });
            })()}
            
            {/* 가상 스크롤 하단 여백 - 음수 방지 */}
            {enableVirtualScroll && (() => {
              const bottomPadding = Math.max(0, virtualScroll.totalHeight - virtualScroll.offsetTop - (visibleFlatRows.length * ROW_HEIGHT));
              return bottomPadding > 0 ? (
                <tr style={{ height: bottomPadding }}>
                  <td colSpan={rowFields.length + flatColumns.length + (totals?.showRowGrandTotals ? dataFields.length : 0)} />
                </tr>
              ) : null;
            })()}

            {/* 열 총계 행 (하단 위치 - 기본값) */}
            {totals?.showColumnGrandTotals && totals?.rowGrandTotalPosition !== "top" && (
              <tr className="bg-primary/5 font-medium">
                <td
                  className={cn(
                    "border-r border-b border-border",
                    "px-2 py-1.5 text-left text-sm",
                    "bg-primary/10 sticky left-0"
                  )}
                >
                  총계
                </td>

                {flatColumns.map((col, colIdx) => (
                  <DataCell
                    key={colIdx}
                    values={grandTotals.column.get(pathToKey(col.path)) || []}
                    isTotal
                  />
                ))}

                {/* 대총합 */}
                {totals?.showRowGrandTotals && (
                  <DataCell values={grandTotals.grand} isTotal />
                )}
              </tr>
            )}
          </tbody>
        </table>
      </div>

      {/* 차트 */}
      {showChart && chartConfig && pivotResult && (
        <PivotChart
          pivotResult={pivotResult}
          config={{
            ...chartConfig,
            enabled: true,
          }}
          dataFields={dataFields}
        />
      )}

      {/* 필드 선택기 모달 */}
      <FieldChooser
        open={showFieldChooser}
        onOpenChange={setShowFieldChooser}
        availableFields={availableFields}
        selectedFields={fields}
        onFieldsChange={handleFieldsChange}
      />

      {/* Drill Down 모달 */}
      <DrillDownModal
        open={drillDownData.open}
        onOpenChange={(open) => setDrillDownData((prev) => ({ ...prev, open }))}
        cellData={drillDownData.cellData}
        data={data}
        fields={fields}
        rowFields={rowFields}
        columnFields={columnFields}
      />
    </div>
  );
};

export default PivotGridComponent;
