"use client";

import React, { useCallback, useEffect, useMemo, useState, useRef } from "react";
import { FlowComponent } from "@/types/screen-management";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { 
  AlertCircle, 
  Loader2, 
  ChevronUp, 
  Filter, 
  X, 
  Layers, 
  ChevronDown, 
  ChevronRight,
  ChevronLeft,
  Edit,
  FileSpreadsheet,
  FileText,
  Copy,
  RefreshCw,
} from "lucide-react";
import * as XLSX from "xlsx";
import {
  getFlowById,
  getAllStepCounts,
  getStepDataList,
  getFlowSteps,
  getFlowConnections,
  getStepColumnLabels,
} from "@/lib/api/flow";
import type { FlowDefinition, FlowStep } from "@/types/flow";
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
import { Checkbox } from "@/components/ui/checkbox";
import { SingleTableWithSticky } from "@/lib/registry/components/table-list/SingleTableWithSticky";
import type { ColumnConfig } from "@/lib/registry/components/table-list/types";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { toast } from "sonner";
import { showErrorToast } from "@/lib/utils/toastUtils";
import {
  Pagination,
  PaginationContent,
  PaginationItem,
  PaginationLink,
  PaginationNext,
  PaginationPrevious,
} from "@/components/ui/pagination";
import { useFlowStepStore } from "@/stores/flowStepStore";
import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
} from "@/components/ui/dialog";
import { Label } from "@/components/ui/label";
import { Input } from "@/components/ui/input";
import { useScreenPreview } from "@/contexts/ScreenPreviewContext";
import { useAuth } from "@/hooks/useAuth";
import { useTableOptions } from "@/contexts/TableOptionsContext";
import { TableFilter, ColumnVisibility } from "@/types/table-options";

// 그룹화된 데이터 인터페이스
interface GroupedData {
  groupKey: string;
  groupValues: Record<string, any>;
  items: any[];
  count: number;
}

interface FlowWidgetProps {
  component: FlowComponent;
  onStepClick?: (stepId: number, stepName: string) => void;
  onSelectedDataChange?: (selectedData: any[], stepId: number | null) => void;
  flowRefreshKey?: number; // 새로고침 키
  onFlowRefresh?: () => void; // 새로고침 완료 콜백
}

export function FlowWidget({
  component,
  onStepClick,
  onSelectedDataChange,
  flowRefreshKey,
  onFlowRefresh,
}: FlowWidgetProps) {
  const { isPreviewMode } = useScreenPreview(); // 프리뷰 모드 확인
  const { user } = useAuth(); // 사용자 정보 가져오기
  const { registerTable, unregisterTable } = useTableOptions(); // Context 훅

  // TableOptions 상태
  const [filters, setFilters] = useState<TableFilter[]>([]);
  const [grouping, setGrouping] = useState<string[]>([]);
  const [columnVisibility, setColumnVisibility] = useState<ColumnVisibility[]>([]);

  // 숫자 포맷팅 함수
  const formatValue = (value: any): string => {
    if (value === null || value === undefined || value === "") {
      return "-";
    }

    // 숫자 타입이거나 숫자로 변환 가능한 문자열인 경우 포맷팅
    if (typeof value === "number") {
      return value.toLocaleString("ko-KR");
    }

    if (typeof value === "string") {
      const numValue = parseFloat(value);
      // 숫자로 변환 가능하고, 변환 후 원래 값과 같은 경우에만 포맷팅
      if (!isNaN(numValue) && numValue.toString() === value.trim()) {
        return numValue.toLocaleString("ko-KR");
      }
    }

    return String(value);
  };

  // 🆕 전역 상태 관리
  const setSelectedStep = useFlowStepStore((state) => state.setSelectedStep);
  const resetFlow = useFlowStepStore((state) => state.resetFlow);

  const [flowData, setFlowData] = useState<FlowDefinition | null>(null);
  const [steps, setSteps] = useState<FlowStep[]>([]);
  const [stepCounts, setStepCounts] = useState<Record<number, number>>({});
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);
  const [connections, setConnections] = useState<any[]>([]); // 플로우 연결 정보

  // 선택된 스텝의 데이터 리스트 상태
  const [selectedStepId, setSelectedStepId] = useState<number | null>(null);
  const [stepData, setStepData] = useState<any[]>([]);
  const [stepDataColumns, setStepDataColumns] = useState<string[]>([]);
  const [stepDataLoading, setStepDataLoading] = useState(false);
  const [selectedRows, setSelectedRows] = useState<Set<string>>(new Set()); // Primary Key 값으로 선택 관리
  const [columnLabels, setColumnLabels] = useState<Record<string, string>>({}); // 컬럼명 -> 라벨 매핑

  // 🆕 검색 필터 관련 상태
  const [searchFilterColumns, setSearchFilterColumns] = useState<Set<string>>(new Set()); // 검색 필터로 사용할 컬럼
  const [isFilterSettingOpen, setIsFilterSettingOpen] = useState(false); // 필터 설정 다이얼로그
  const [searchValues, setSearchValues] = useState<Record<string, string>>({}); // 검색 값
  const [allAvailableColumns, setAllAvailableColumns] = useState<string[]>([]); // 전체 컬럼 목록
  const [filteredData, setFilteredData] = useState<any[]>([]); // 필터링된 데이터

  // 🆕 그룹 설정 관련 상태
  const [isGroupSettingOpen, setIsGroupSettingOpen] = useState(false); // 그룹 설정 다이얼로그
  const [groupByColumns, setGroupByColumns] = useState<string[]>([]); // 그룹화할 컬럼 목록
  const [collapsedGroups, setCollapsedGroups] = useState<Set<string>>(new Set()); // 접힌 그룹

  /**
   * 🆕 컬럼 표시 결정 함수
   * 1순위: 플로우 스텝 기본 설정 (displayConfig)
   * 2순위: 모든 컬럼 표시
   */
  const getVisibleColumns = (stepId: number, allColumns: string[], stepsArray?: FlowStep[]): string[] => {
    // stepsArray가 제공되지 않으면 state의 steps 사용
    const effectiveSteps = stepsArray || steps;

    // 1순위: 플로우 스텝 기본 설정
    const currentStep = effectiveSteps.find((s) => s.id === stepId);

    if (currentStep?.displayConfig?.visibleColumns && currentStep.displayConfig.visibleColumns.length > 0) {
      return currentStep.displayConfig.visibleColumns;
    }

    // 2순위: 모든 컬럼 표시
    return allColumns;
  };

  // 🆕 스텝 데이터 페이지네이션 상태
  const [stepDataPage, setStepDataPage] = useState(1);
  const [stepDataPageSize, setStepDataPageSize] = useState(10);

  // 🆕 정렬 상태 (SingleTableWithSticky용)
  const [sortColumn, setSortColumn] = useState<string | null>(null);
  const [sortDirection, setSortDirection] = useState<"asc" | "desc">("asc");

  // 🆕 툴바 관련 상태
  const [isSearchPanelOpen, setIsSearchPanelOpen] = useState(false);
  const [globalSearchTerm, setGlobalSearchTerm] = useState("");
  const [searchHighlights, setSearchHighlights] = useState<Set<string>>(new Set());
  const [currentSearchIndex, setCurrentSearchIndex] = useState(0);

  // 🆕 인라인 편집 관련 상태
  const [editingCell, setEditingCell] = useState<{
    rowIndex: number;
    colIndex: number;
    columnName: string;
    originalValue: any;
  } | null>(null);
  const [editingValue, setEditingValue] = useState<string>("");
  const editInputRef = useRef<HTMLInputElement>(null);

  // componentConfig에서 플로우 설정 추출 (DynamicComponentRenderer에서 전달됨)
  const config = (component as any).componentConfig || (component as any).config || {};
  const flowId = config.flowId || component.flowId;
  const flowName = config.flowName || component.flowName;
  const displayMode = config.displayMode || component.displayMode || "horizontal";
  const showStepCount = config.showStepCount !== false && component.showStepCount !== false; // 기본값 true
  const allowDataMove = config.allowDataMove || component.allowDataMove || false;

  // 🆕 플로우 컴포넌트 ID (버튼이 이 플로우를 참조할 때 사용)
  const flowComponentId = component.id;

  // 🆕 localStorage 키 생성 (사용자별로 저장)
  const filterSettingKey = useMemo(() => {
    if (!flowId || selectedStepId === null || !user?.userId) return null;
    return `flowWidget_searchFilters_${user.userId}_${flowId}_${selectedStepId}`;
  }, [flowId, selectedStepId, user?.userId]);

  // 🆕 그룹 설정 localStorage 키 생성
  const groupSettingKey = useMemo(() => {
    if (!selectedStepId) return null;
    return `flowWidget_groupSettings_step_${selectedStepId}`;
  }, [selectedStepId]);

  // 🆕 저장된 필터 설정 불러오기
  useEffect(() => {
    if (!filterSettingKey || stepDataColumns.length === 0 || !user?.userId) return;

    try {
      // 현재 사용자의 필터 설정만 불러오기
      const saved = localStorage.getItem(filterSettingKey);
      if (saved) {
        const savedFilters = JSON.parse(saved);
        // 현재 단계에 표시되는 컬럼만 필터링
        const validFilters = savedFilters.filter((col: string) => stepDataColumns.includes(col));
        setSearchFilterColumns(new Set(validFilters));
      } else {
        // 초기값: 빈 필터 (사용자가 선택해야 함)
        setSearchFilterColumns(new Set());
      }
    } catch (error) {
      console.error("필터 설정 불러오기 실패:", error);
      setSearchFilterColumns(new Set());
    }
  }, [filterSettingKey, stepDataColumns, user?.userId]);

  // 🆕 저장된 그룹 설정 불러오기
  useEffect(() => {
    if (!groupSettingKey || stepDataColumns.length === 0) return;

    try {
      const saved = localStorage.getItem(groupSettingKey);
      if (saved) {
        const savedGroups = JSON.parse(saved);
        // 현재 단계에 표시되는 컬럼만 필터링
        const validGroups = savedGroups.filter((col: string) => stepDataColumns.includes(col));
        setGroupByColumns(validGroups);
      }
    } catch (error) {
      console.error("그룹 설정 불러오기 실패:", error);
      setGroupByColumns([]);
    }
  }, [groupSettingKey, stepDataColumns]);

  // 🆕 필터 설정 저장
  const saveFilterSettings = useCallback(() => {
    if (!filterSettingKey) return;

    try {
      localStorage.setItem(filterSettingKey, JSON.stringify(Array.from(searchFilterColumns)));
      setIsFilterSettingOpen(false);
      toast.success("검색 필터 설정이 저장되었습니다");

      // 검색 값 초기화
      setSearchValues({});
    } catch (error) {
      console.error("필터 설정 저장 실패:", error);
      showErrorToast("필터 설정 저장에 실패했습니다", error, { guidance: "잠시 후 다시 시도해 주세요." });
    }
  }, [filterSettingKey, searchFilterColumns]);

  // 🆕 필터 컬럼 토글
  const toggleFilterColumn = useCallback((columnName: string) => {
    setSearchFilterColumns((prev) => {
      const newSet = new Set(prev);
      if (newSet.has(columnName)) {
        newSet.delete(columnName);
      } else {
        newSet.add(columnName);
      }
      return newSet;
    });
  }, []);

  // 🆕 전체 선택/해제
  const toggleAllFilters = useCallback(() => {
    if (searchFilterColumns.size === stepDataColumns.length) {
      // 전체 해제
      setSearchFilterColumns(new Set());
    } else {
      // 전체 선택
      setSearchFilterColumns(new Set(stepDataColumns));
    }
  }, [searchFilterColumns, stepDataColumns]);

  // 🆕 검색 초기화
  const handleClearSearch = useCallback(() => {
    setSearchValues({});
    setFilteredData([]);
  }, []);

  // 🆕 그룹 설정 저장
  const saveGroupSettings = useCallback(() => {
    if (!groupSettingKey) return;

    try {
      localStorage.setItem(groupSettingKey, JSON.stringify(groupByColumns));
      setIsGroupSettingOpen(false);
      toast.success("그룹 설정이 저장되었습니다");
    } catch (error) {
      console.error("그룹 설정 저장 실패:", error);
      showErrorToast("그룹 설정 저장에 실패했습니다", error, { guidance: "잠시 후 다시 시도해 주세요." });
    }
  }, [groupSettingKey, groupByColumns]);

  // 🆕 그룹 컬럼 토글
  const toggleGroupColumn = useCallback((columnName: string) => {
    setGroupByColumns((prev) => {
      if (prev.includes(columnName)) {
        return prev.filter((col) => col !== columnName);
      } else {
        return [...prev, columnName];
      }
    });
  }, []);

  // 🆕 그룹 펼치기/접기 토글
  const toggleGroupCollapse = useCallback((groupKey: string) => {
    setCollapsedGroups((prev) => {
      const newSet = new Set(prev);
      if (newSet.has(groupKey)) {
        newSet.delete(groupKey);
      } else {
        newSet.add(groupKey);
      }
      return newSet;
    });
  }, []);

  // 🆕 그룹 해제
  const clearGrouping = useCallback(() => {
    setGroupByColumns([]);
    setCollapsedGroups(new Set());
    if (groupSettingKey) {
      localStorage.removeItem(groupSettingKey);
    }
    toast.success("그룹이 해제되었습니다");
  }, [groupSettingKey]);

  // 테이블 등록 (선택된 스텝이 있을 때)
  useEffect(() => {
    if (!selectedStepId || !stepDataColumns || stepDataColumns.length === 0) {
      return;
    }

    const tableId = `flow-widget-${component.id}-step-${selectedStepId}`;
    const currentStep = steps.find((s) => s.id === selectedStepId);

    registerTable({
      tableId,
      label: `${flowName || "플로우"} - ${currentStep?.name || "스텝"}`,
      tableName: "flow_step_data",
      columns: stepDataColumns.map((col) => ({
        columnName: col,
        columnLabel: columnLabels[col] || col,
        inputType: "text",
        visible: true,
        width: 150,
        sortable: true,
        filterable: true,
      })),
      onFilterChange: setFilters,
      onGroupChange: setGrouping,
      onColumnVisibilityChange: setColumnVisibility,
    });

    return () => unregisterTable(tableId);
  }, [selectedStepId, stepDataColumns, columnLabels, flowName, steps, component.id]);

  // 🆕 데이터 그룹화
  const groupedData = useMemo((): GroupedData[] => {
    const dataToGroup = filteredData.length > 0 ? filteredData : stepData;
    
    if (groupByColumns.length === 0 || dataToGroup.length === 0) return [];

    const grouped = new Map<string, any[]>();

    dataToGroup.forEach((item) => {
      // 그룹 키 생성: "통화:KRW > 단위:EA"
      const keyParts = groupByColumns.map((col) => {
        const value = item[col];
        const label = columnLabels[col] || col;
        return `${label}:${value !== null && value !== undefined ? value : "-"}`;
      });
      const groupKey = keyParts.join(" > ");

      if (!grouped.has(groupKey)) {
        grouped.set(groupKey, []);
      }
      grouped.get(groupKey)!.push(item);
    });

    return Array.from(grouped.entries()).map(([groupKey, items]) => {
      const groupValues: Record<string, any> = {};
      groupByColumns.forEach((col) => {
        groupValues[col] = items[0]?.[col];
      });

      return {
        groupKey,
        groupValues,
        items,
        count: items.length,
      };
    });
  }, [filteredData, stepData, groupByColumns, columnLabels]);

  // 🆕 검색 값이 변경될 때마다 자동 검색 (useEffect로 직접 처리)
  useEffect(() => {
    if (!stepData || stepData.length === 0) {
      setFilteredData([]);
      return;
    }

    // 검색 값이 하나라도 있는지 확인
    const hasSearchValue = Object.values(searchValues).some((val) => val && String(val).trim() !== "");

    if (!hasSearchValue) {
      // 검색 값이 없으면 필터링 해제
      setFilteredData([]);
      return;
    }

    // 필터링 실행
    const filtered = stepData.filter((row) => {
      // 모든 검색 조건을 만족하는지 확인
      return Object.entries(searchValues).every(([col, searchValue]) => {
        if (!searchValue || String(searchValue).trim() === "") return true; // 빈 값은 필터링하지 않음

        const cellValue = row[col];
        if (cellValue === null || cellValue === undefined) return false;

        // 문자열로 변환하여 대소문자 무시 검색
        return String(cellValue).toLowerCase().includes(String(searchValue).toLowerCase());
      });
    });

    setFilteredData(filtered);
    console.log("🔍 검색 실행:", {
      totalRows: stepData.length,
      filteredRows: filtered.length,
      searchValues,
      hasSearchValue,
    });
  }, [searchValues, stepData]); // stepData와 searchValues가 변경될 때마다 실행

  // 선택된 스텝의 데이터를 다시 로드하는 함수
  const refreshStepData = async () => {
    if (!flowId) return;

    try {
      // 스텝 카운트는 항상 업데이트 (선택된 스텝 유무와 관계없이)
      const countsResponse = await getAllStepCounts(flowId);

      if (countsResponse.success && countsResponse.data) {
        // Record 형태로 변환
        const countsMap: Record<number, number> = {};
        if (Array.isArray(countsResponse.data)) {
          countsResponse.data.forEach((item: any) => {
            countsMap[item.stepId] = item.count;
          });
        } else if (typeof countsResponse.data === "object") {
          Object.assign(countsMap, countsResponse.data);
        }

        setStepCounts(countsMap);
      }

      // 선택된 스텝이 있으면 해당 스텝의 데이터도 새로고침
      if (selectedStepId) {
        setStepDataLoading(true);

        // 컬럼 라벨 조회
        const labelsResponse = await getStepColumnLabels(flowId, selectedStepId);
        if (labelsResponse.success && labelsResponse.data) {
          setColumnLabels(labelsResponse.data);
        }

        const response = await getStepDataList(flowId, selectedStepId, 1, 100);

        if (!response.success) {
          throw new Error(response.message || "데이터를 불러올 수 없습니다");
        }

        const rows = response.data?.records || [];
        setStepData(rows);

        // 🆕 컬럼 추출 및 우선순위 적용
        if (rows.length > 0) {
          const allColumns = Object.keys(rows[0]);
          setAllAvailableColumns(allColumns); // 전체 컬럼 목록 저장
          const visibleColumns = getVisibleColumns(selectedStepId, allColumns);
          setStepDataColumns(visibleColumns);
        } else {
          setAllAvailableColumns([]);
          setStepDataColumns([]);
        }

        // 선택 초기화
        setSelectedRows(new Set());
        setSearchValues({}); // 검색 값도 초기화
        setFilteredData([]); // 필터링된 데이터 초기화
        onSelectedDataChange?.([], selectedStepId);
      }
    } catch (err: any) {
      console.error("❌ 플로우 새로고침 실패:", err);
      showErrorToast("데이터 새로고침에 실패했습니다", err, { guidance: "네트워크 연결을 확인하고 다시 시도해 주세요." });
    } finally {
      if (selectedStepId) {
        setStepDataLoading(false);
      }
    }
  };

  useEffect(() => {
    if (!flowId) {
      setLoading(false);
      return;
    }

    const loadFlowData = async () => {
      try {
        setLoading(true);
        setError(null);

        // 프리뷰 모드에서는 샘플 데이터만 표시
        if (isPreviewMode) {
          console.log("🔒 프리뷰 모드: 플로우 데이터 로드 차단 - 샘플 데이터 표시");
          setFlowData({
            id: flowId || 0,
            flowName: flowName || "샘플 플로우",
            description: "프리뷰 모드 샘플",
            isActive: true,
            createdAt: new Date().toISOString(),
            updatedAt: new Date().toISOString(),
          } as FlowDefinition);

          const sampleSteps: FlowStep[] = [
            {
              id: 1,
              flowId: flowId || 0,
              stepName: "시작 단계",
              stepOrder: 1,
              stepType: "start",
              stepConfig: {},
              createdAt: new Date().toISOString(),
              updatedAt: new Date().toISOString(),
            },
            {
              id: 2,
              flowId: flowId || 0,
              stepName: "진행 중",
              stepOrder: 2,
              stepType: "process",
              stepConfig: {},
              createdAt: new Date().toISOString(),
              updatedAt: new Date().toISOString(),
            },
            {
              id: 3,
              flowId: flowId || 0,
              stepName: "완료",
              stepOrder: 3,
              stepType: "end",
              stepConfig: {},
              createdAt: new Date().toISOString(),
              updatedAt: new Date().toISOString(),
            },
          ];
          setSteps(sampleSteps);
          setStepCounts({ 1: 5, 2: 3, 3: 2 });
          setConnections([]);
          setLoading(false);
          return;
        }

        // 플로우 정보 조회
        const flowResponse = await getFlowById(flowId!);
        if (!flowResponse.success || !flowResponse.data) {
          throw new Error("플로우를 찾을 수 없습니다");
        }

        setFlowData(flowResponse.data);

        // 스텝 목록 조회
        const stepsResponse = await getFlowSteps(flowId);
        if (!stepsResponse.success) {
          throw new Error("스텝 목록을 불러올 수 없습니다");
        }
        if (stepsResponse.data) {
          const sortedSteps = stepsResponse.data.sort((a: FlowStep, b: FlowStep) => a.stepOrder - b.stepOrder);
          setSteps(sortedSteps);

          // 연결 정보 조회
          const connectionsResponse = await getFlowConnections(flowId);
          if (connectionsResponse.success && connectionsResponse.data) {
            setConnections(connectionsResponse.data);
          }

          // 스텝별 데이터 건수 조회
          if (showStepCount) {
            const countsResponse = await getAllStepCounts(flowId!);
            if (countsResponse.success && countsResponse.data) {
              // 배열을 Record<number, number>로 변환
              const countsMap: Record<number, number> = {};
              countsResponse.data.forEach((item: any) => {
                countsMap[item.stepId] = item.count;
              });
              setStepCounts(countsMap);
            }
          }

          // 🆕 플로우 로드 후 첫 번째 스텝 자동 선택
          if (sortedSteps.length > 0) {
            const firstStep = sortedSteps[0];
            setSelectedStepId(firstStep.id);
            setSelectedStep(flowComponentId, firstStep.id);

            // 첫 번째 스텝의 데이터 로드
            try {
              // 컬럼 라벨 조회
              const labelsResponse = await getStepColumnLabels(flowId!, firstStep.id);
              if (labelsResponse.success && labelsResponse.data) {
                setColumnLabels(labelsResponse.data);
              }

              const response = await getStepDataList(flowId!, firstStep.id, 1, 100);
              if (response.success) {
                const rows = response.data?.records || [];
                setStepData(rows);
                if (rows.length > 0) {
                  const allColumns = Object.keys(rows[0]);
                  setAllAvailableColumns(allColumns); // 전체 컬럼 목록 저장
                  // sortedSteps를 직접 전달하여 타이밍 이슈 해결
                  const visibleColumns = getVisibleColumns(firstStep.id, allColumns, sortedSteps);
                  setStepDataColumns(visibleColumns);
                }
              }
            } catch (err) {
              console.error("첫 번째 스텝 데이터 로드 실패:", err);
            }
          }
        }
      } catch (err: any) {
        console.error("Failed to load flow data:", err);
        setError(err.message || "플로우 데이터를 불러오는데 실패했습니다");
      } finally {
        setLoading(false);
      }
    };

    loadFlowData();
  }, [flowId, showStepCount]);

  // flowRefreshKey가 변경될 때마다 스텝 데이터 새로고침
  useEffect(() => {
    if (flowRefreshKey !== undefined && flowRefreshKey > 0 && flowId) {
      refreshStepData();
    }
  }, [flowRefreshKey]);

  // 🆕 언마운트 시 전역 상태 초기화
  useEffect(() => {
    return () => {
      resetFlow(flowComponentId);
    };
  }, [flowComponentId, resetFlow]);

  // 🆕 스텝 클릭 핸들러 (전역 상태 업데이트 추가)
  const handleStepClick = async (stepId: number, stepName: string) => {
    // 프리뷰 모드에서는 스텝 클릭 차단
    if (isPreviewMode) {
      return;
    }

    // 외부 콜백 실행
    if (onStepClick) {
      onStepClick(stepId, stepName);
    }

    // 같은 스텝을 다시 클릭하면 접기
    if (selectedStepId === stepId) {
      setSelectedStepId(null);
      setSelectedStep(flowComponentId, null); // 🆕 전역 상태 업데이트
      setStepData([]);
      setStepDataColumns([]);
      setSelectedRows(new Set());
      setStepDataPage(1); // 🆕 페이지 리셋
      onSelectedDataChange?.([], null);

      return;
    }

    // 새로운 스텝 선택 - 데이터 로드
    setSelectedStepId(stepId);
    setSelectedStep(flowComponentId, stepId); // 🆕 전역 상태 업데이트
    setStepDataLoading(true);
    setSelectedRows(new Set());
    setStepDataPage(1); // 🆕 페이지 리셋
    onSelectedDataChange?.([], stepId);

    try {
      // 컬럼 라벨 조회
      const labelsResponse = await getStepColumnLabels(flowId!, stepId);
      console.log("🏷️ 컬럼 라벨 조회 결과:", {
        stepId,
        success: labelsResponse.success,
        labelsCount: labelsResponse.data ? Object.keys(labelsResponse.data).length : 0,
        labels: labelsResponse.data,
      });
      if (labelsResponse.success && labelsResponse.data) {
        setColumnLabels(labelsResponse.data);
      } else {
        console.warn("⚠️ 컬럼 라벨 조회 실패 또는 데이터 없음:", labelsResponse);
        setColumnLabels({});
      }

      // 데이터 조회
      const response = await getStepDataList(flowId!, stepId, 1, 100);

      if (!response.success) {
        throw new Error(response.message || "데이터를 불러올 수 없습니다");
      }

      const rows = response.data?.records || [];
      setStepData(rows);

      // 🆕 컬럼 추출 및 우선순위 적용
      if (rows.length > 0) {
        const allColumns = Object.keys(rows[0]);
        setAllAvailableColumns(allColumns); // 전체 컬럼 목록 저장
        const visibleColumns = getVisibleColumns(stepId, allColumns);
        setStepDataColumns(visibleColumns);
      } else {
        setAllAvailableColumns([]);
        setStepDataColumns([]);
      }
    } catch (err: any) {
      console.error("Failed to load step data:", err);
      showErrorToast("스텝 데이터를 불러오는 데 실패했습니다", err, { guidance: "네트워크 연결을 확인해 주세요." });
    } finally {
      setStepDataLoading(false);
    }
  };

  // Primary Key 컬럼명 (플로우 정의에서 가져오거나 기본값 id)
  const primaryKeyColumn = flowData?.primaryKey || "id";

  // 행의 Primary Key 값 가져오기
  const getRowKey = useCallback((row: any): string => {
    const keyValue = row[primaryKeyColumn] || row.id;
    return String(keyValue);
  }, [primaryKeyColumn]);

  // 체크박스 토글 (Primary Key 기반)
  const toggleRowSelection = (row: any) => {
    // 프리뷰 모드에서는 행 선택 차단
    if (isPreviewMode) {
      return;
    }

    const rowKey = getRowKey(row);
    const newSelected = new Set(selectedRows);
    if (newSelected.has(rowKey)) {
      newSelected.delete(rowKey);
    } else {
      newSelected.add(rowKey);
    }
    setSelectedRows(newSelected);

    // 선택된 데이터를 상위로 전달 (stepData에서 선택된 행들 찾기)
    const selectedData = stepData.filter((r) => newSelected.has(getRowKey(r)));
    console.log("🌊 FlowWidget - 체크박스 토글, 상위로 전달:", {
      rowKey,
      newSelectedSize: newSelected.size,
      selectedData,
      selectedStepId,
      hasCallback: !!onSelectedDataChange,
    });
    onSelectedDataChange?.(selectedData, selectedStepId);
  };

  // 전체 선택/해제 (Primary Key 기반)
  const toggleAllRows = () => {
    let newSelected: Set<string>;
    if (selectedRows.size === stepData.length) {
      newSelected = new Set();
    } else {
      newSelected = new Set(stepData.map((row) => getRowKey(row)));
    }
    setSelectedRows(newSelected);

    // 선택된 데이터를 상위로 전달
    const selectedData = stepData.filter((row) => newSelected.has(getRowKey(row)));
    onSelectedDataChange?.(selectedData, selectedStepId);
  };

  // 🆕 표시할 데이터 결정
  // - 검색 값이 있으면 → filteredData 사용 (결과가 0건이어도 filteredData 사용)
  // - 검색 값이 없으면 → stepData 사용 (전체 데이터)
  const hasSearchValue = Object.values(searchValues).some((val) => val && String(val).trim() !== "");
  const displayData = hasSearchValue ? filteredData : stepData;

  // 🆕 정렬된 데이터
  const sortedDisplayData = useMemo(() => {
    if (!sortColumn) return displayData;
    
    return [...displayData].sort((a, b) => {
      const aVal = a[sortColumn];
      const bVal = b[sortColumn];
      
      // null/undefined 처리
      if (aVal == null && bVal == null) return 0;
      if (aVal == null) return sortDirection === "asc" ? 1 : -1;
      if (bVal == null) return sortDirection === "asc" ? -1 : 1;
      
      // 숫자 비교
      if (typeof aVal === "number" && typeof bVal === "number") {
        return sortDirection === "asc" ? aVal - bVal : bVal - aVal;
      }
      
      // 문자열 비교
      const aStr = String(aVal).toLowerCase();
      const bStr = String(bVal).toLowerCase();
      if (sortDirection === "asc") {
        return aStr.localeCompare(bStr, "ko");
      }
      return bStr.localeCompare(aStr, "ko");
    });
  }, [displayData, sortColumn, sortDirection]);

  // 🆕 페이지네이션된 스텝 데이터 (정렬된 데이터 기반)
  const paginatedStepData = sortedDisplayData.slice((stepDataPage - 1) * stepDataPageSize, stepDataPage * stepDataPageSize);
  const totalStepDataPages = Math.ceil(sortedDisplayData.length / stepDataPageSize);

  // 🆕 정렬 핸들러
  const handleSort = useCallback((columnName: string) => {
    if (sortColumn === columnName) {
      // 같은 컬럼 클릭 시 방향 토글
      setSortDirection((prev) => (prev === "asc" ? "desc" : "asc"));
    } else {
      // 다른 컬럼 클릭 시 해당 컬럼으로 오름차순 정렬
      setSortColumn(columnName);
      setSortDirection("asc");
    }
  }, [sortColumn]);

  // 🆕 SingleTableWithSticky용 컬럼 설정 생성
  const tableColumns: ColumnConfig[] = useMemo(() => {
    const cols: ColumnConfig[] = [];
    
    // 체크박스 컬럼 추가 (allowDataMove가 true일 때)
    if (allowDataMove) {
      cols.push({
        columnName: "__checkbox__",
        displayName: "",
        visible: true,
        sortable: false,
        searchable: false,
        width: 50,
        align: "center",
        order: 0,
      });
    }
    
    // 데이터 컬럼들 추가
    stepDataColumns.forEach((col, index) => {
      cols.push({
        columnName: col,
        displayName: columnLabels[col] || col,
        visible: true,
        sortable: true,
        searchable: true,
        width: 150,
        align: "left",
        order: index + 1,
      });
    });
    
    return cols;
  }, [stepDataColumns, columnLabels, allowDataMove]);

  // 🆕 SingleTableWithSticky용 테이블 설정
  const tableConfig = useMemo(() => ({
    stickyHeader: true,
    checkbox: {
      enabled: allowDataMove,
      selectAll: allowDataMove,
      multiple: true,
      position: "left" as const,
    },
    tableStyle: {
      hoverEffect: true,
      alternateRows: false,
    },
  }), [allowDataMove]);

  // 🆕 현재 페이지 기준으로 변환된 검색 하이라이트
  const pageSearchHighlights = useMemo(() => {
    if (searchHighlights.size === 0) return new Set<string>();
    
    const pageStartIndex = (stepDataPage - 1) * stepDataPageSize;
    const pageEndIndex = pageStartIndex + stepDataPageSize;
    const pageHighlights = new Set<string>();
    
    searchHighlights.forEach((key) => {
      const [rowIndexStr, colIndexStr] = key.split("-");
      const rowIndex = parseInt(rowIndexStr);
      
      // 현재 페이지에 해당하는 항목만 포함
      if (rowIndex >= pageStartIndex && rowIndex < pageEndIndex) {
        // 페이지 내 상대 인덱스로 변환
        const pageRowIndex = rowIndex - pageStartIndex;
        pageHighlights.add(`${pageRowIndex}-${colIndexStr}`);
      }
    });
    
    return pageHighlights;
  }, [searchHighlights, stepDataPage, stepDataPageSize]);

  // 🆕 현재 페이지 기준 검색 인덱스
  const pageCurrentSearchIndex = useMemo(() => {
    if (searchHighlights.size === 0) return 0;
    
    const highlightArray = Array.from(searchHighlights);
    const currentKey = highlightArray[currentSearchIndex];
    if (!currentKey) return -1;
    
    const [rowIndexStr, colIndexStr] = currentKey.split("-");
    const rowIndex = parseInt(rowIndexStr);
    const pageStartIndex = (stepDataPage - 1) * stepDataPageSize;
    const pageRowIndex = rowIndex - pageStartIndex;
    
    // 현재 페이지에 있는지 확인
    if (pageRowIndex < 0 || pageRowIndex >= stepDataPageSize) return -1;
    
    // pageSearchHighlights에서의 인덱스 찾기
    const pageKey = `${pageRowIndex}-${colIndexStr}`;
    const pageHighlightArray = Array.from(pageSearchHighlights);
    return pageHighlightArray.indexOf(pageKey);
  }, [searchHighlights, currentSearchIndex, stepDataPage, stepDataPageSize, pageSearchHighlights]);

  // 🆕 컬럼 너비 계산 함수
  const getColumnWidth = useCallback((column: ColumnConfig) => {
    if (column.columnName === "__checkbox__") return 50;
    return column.width || 150;
  }, []);

  // 🆕 셀 값 포맷팅 함수
  const formatCellValue = useCallback((value: any, format?: string, columnName?: string) => {
    return formatValue(value);
  }, []);

  // 🆕 전체 선택 핸들러 (Primary Key 기반)
  const handleSelectAll = useCallback((checked: boolean) => {
    if (checked) {
      const allKeys = new Set(sortedDisplayData.map((row) => getRowKey(row)));
      setSelectedRows(allKeys);
      // 선택된 데이터를 상위로 전달
      onSelectedDataChange?.(sortedDisplayData, selectedStepId);
    } else {
      setSelectedRows(new Set());
      onSelectedDataChange?.([], selectedStepId);
    }
  }, [sortedDisplayData, getRowKey, onSelectedDataChange, selectedStepId]);

  // 🆕 행 클릭 핸들러
  const handleRowClick = useCallback((row: any) => {
    // 필요 시 행 클릭 로직 추가
  }, []);

  // 🆕 체크박스 셀 렌더링 (Primary Key 기반)
  // index 파라미터는 SingleTableWithSticky 인터페이스 호환을 위해 유지하지만 사용하지 않음
  const renderCheckboxCell = useCallback((row: any, _index: number) => {
    const rowKey = getRowKey(row);
    return (
      <Checkbox
        checked={selectedRows.has(rowKey)}
        onCheckedChange={() => toggleRowSelection(row)}
      />
    );
  }, [selectedRows, toggleRowSelection, getRowKey]);

  // 🆕 Excel 내보내기 (Primary Key 기반)
  const exportToExcel = useCallback(() => {
    try {
      const exportData = selectedRows.size > 0 
        ? sortedDisplayData.filter((row) => selectedRows.has(getRowKey(row)))
        : sortedDisplayData;

      if (exportData.length === 0) {
        toast.warning("내보낼 데이터가 없습니다.");
        return;
      }

      // 컬럼 라벨 적용
      const formattedData = exportData.map((row) => {
        const newRow: Record<string, any> = {};
        stepDataColumns.forEach((col) => {
          const label = columnLabels[col] || col;
          newRow[label] = row[col];
        });
        return newRow;
      });

      const ws = XLSX.utils.json_to_sheet(formattedData);
      const wb = XLSX.utils.book_new();
      XLSX.utils.book_append_sheet(wb, ws, "Data");

      const _fxd = new Date();
      const fileName = `${flowName || "flow"}_data_${_fxd.getFullYear()}-${String(_fxd.getMonth() + 1).padStart(2, "0")}-${String(_fxd.getDate()).padStart(2, "0")}.xlsx`;
      XLSX.writeFile(wb, fileName);

      toast.success(`${exportData.length}개 행이 Excel로 내보내기 되었습니다.`);
    } catch (error) {
      console.error("Excel 내보내기 오류:", error);
      showErrorToast("Excel 파일 내보내기에 실패했습니다", error, { guidance: "데이터를 확인하고 다시 시도해 주세요." });
    }
  }, [sortedDisplayData, selectedRows, stepDataColumns, columnLabels, flowName, getRowKey]);

  // 🆕 PDF 내보내기 (html2canvas 사용으로 한글 지원)
  const exportToPdf = useCallback(async () => {
    try {
      const exportData = selectedRows.size > 0 
        ? sortedDisplayData.filter((row) => selectedRows.has(getRowKey(row)))
        : sortedDisplayData;

      if (exportData.length === 0) {
        toast.warning("내보낼 데이터가 없습니다.");
        return;
      }

      toast.loading("PDF 생성 중...", { id: "pdf-export" });

      // html2canvas와 jspdf 동적 로드
      const [{ default: html2canvas }, { default: jsPDF }] = await Promise.all([
        import("html2canvas"),
        import("jspdf"),
      ]);

      // 임시 테이블 HTML 생성
      const tempContainer = document.createElement("div");
      tempContainer.style.cssText = `
        position: absolute;
        left: -9999px;
        top: 0;
        background: white;
        padding: 20px;
        font-family: 'Malgun Gothic', 'Apple SD Gothic Neo', sans-serif;
      `;

      // 제목
      const title = document.createElement("h2");
      title.textContent = flowName || "Flow Data";
      title.style.cssText = "margin-bottom: 10px; font-size: 18px; color: #333;";
      tempContainer.appendChild(title);

      // 날짜
      const dateInfo = document.createElement("p");
      dateInfo.textContent = `내보내기 일시: ${new Date().toLocaleString("ko-KR")}`;
      dateInfo.style.cssText = "margin-bottom: 15px; font-size: 12px; color: #666;";
      tempContainer.appendChild(dateInfo);

      // 테이블 생성
      const table = document.createElement("table");
      table.style.cssText = `
        border-collapse: collapse;
        width: 100%;
        font-size: 11px;
      `;

      // 헤더
      const thead = document.createElement("thead");
      const headerRow = document.createElement("tr");
      stepDataColumns.forEach((col) => {
        const th = document.createElement("th");
        th.textContent = columnLabels[col] || col;
        th.style.cssText = `
          background: #4a90d9;
          color: white;
          padding: 8px 12px;
          text-align: left;
          border: 1px solid #3a7bc8;
          white-space: nowrap;
        `;
        headerRow.appendChild(th);
      });
      thead.appendChild(headerRow);
      table.appendChild(thead);

      // 바디
      const tbody = document.createElement("tbody");
      exportData.forEach((row, idx) => {
        const tr = document.createElement("tr");
        tr.style.cssText = idx % 2 === 0 ? "background: #fff;" : "background: #f9f9f9;";
        stepDataColumns.forEach((col) => {
          const td = document.createElement("td");
          td.textContent = String(row[col] ?? "");
          td.style.cssText = `
            padding: 6px 12px;
            border: 1px solid #ddd;
            white-space: nowrap;
          `;
          tr.appendChild(td);
        });
        tbody.appendChild(tr);
      });
      table.appendChild(tbody);
      tempContainer.appendChild(table);

      document.body.appendChild(tempContainer);

      // HTML을 캔버스로 변환
      const canvas = await html2canvas(tempContainer, {
        scale: 2,
        useCORS: true,
        logging: false,
        backgroundColor: "#ffffff",
      });

      document.body.removeChild(tempContainer);

      // 캔버스를 PDF로 변환
      const imgData = canvas.toDataURL("image/png");
      const imgWidth = canvas.width;
      const imgHeight = canvas.height;

      // A4 가로 방향 (297mm x 210mm)
      const pdfWidth = 297;
      const pdfHeight = 210;
      const ratio = Math.min(pdfWidth / (imgWidth / 3.78), pdfHeight / (imgHeight / 3.78));
      
      const doc = new jsPDF({
        orientation: imgWidth > imgHeight ? "landscape" : "portrait",
        unit: "mm",
        format: "a4",
      });

      const scaledWidth = (imgWidth / 3.78) * ratio * 0.9;
      const scaledHeight = (imgHeight / 3.78) * ratio * 0.9;

      // 이미지가 페이지보다 크면 여러 페이지로 분할
      const pageWidth = doc.internal.pageSize.getWidth();
      const pageHeight = doc.internal.pageSize.getHeight();
      
      if (scaledHeight <= pageHeight - 20) {
        // 한 페이지에 들어가는 경우
        doc.addImage(imgData, "PNG", 10, 10, scaledWidth, scaledHeight);
      } else {
        // 여러 페이지로 분할
        let remainingHeight = scaledHeight;
        let yOffset = 0;
        let pageNum = 0;

        while (remainingHeight > 0) {
          if (pageNum > 0) {
            doc.addPage();
          }
          
          const drawHeight = Math.min(pageHeight - 20, remainingHeight);
          doc.addImage(
            imgData, 
            "PNG", 
            10, 
            10 - yOffset, 
            scaledWidth, 
            scaledHeight
          );
          
          remainingHeight -= (pageHeight - 20);
          yOffset += (pageHeight - 20);
          pageNum++;
        }
      }

      const _fpd = new Date();
      const fileName = `${flowName || "flow"}_data_${_fpd.getFullYear()}-${String(_fpd.getMonth() + 1).padStart(2, "0")}-${String(_fpd.getDate()).padStart(2, "0")}.pdf`;
      doc.save(fileName);

      toast.success(`${exportData.length}개 행이 PDF로 내보내기 되었습니다.`, { id: "pdf-export" });
    } catch (error) {
      console.error("PDF 내보내기 오류:", error);
      showErrorToast("PDF 파일 내보내기에 실패했습니다", error, { guidance: "데이터를 확인하고 다시 시도해 주세요." });
    }
  }, [sortedDisplayData, selectedRows, stepDataColumns, columnLabels, flowName, getRowKey]);

  // 🆕 복사 기능 (Primary Key 기반)
  const handleCopy = useCallback(() => {
    try {
      const copyData = selectedRows.size > 0 
        ? sortedDisplayData.filter((row) => selectedRows.has(getRowKey(row)))
        : [];

      if (copyData.length === 0) {
        toast.warning("복사할 데이터를 선택해주세요.");
        return;
      }

      // 헤더 + 데이터를 탭 구분 텍스트로 변환
      const headers = stepDataColumns.map((col) => columnLabels[col] || col).join("\t");
      const rows = copyData.map((row) => 
        stepDataColumns.map((col) => String(row[col] ?? "")).join("\t")
      ).join("\n");

      const text = `${headers}\n${rows}`;
      navigator.clipboard.writeText(text);

      toast.success(`${copyData.length}개 행이 클립보드에 복사되었습니다.`);
    } catch (error) {
      console.error("복사 오류:", error);
      showErrorToast("클립보드 복사에 실패했습니다", error, { guidance: "브라우저 권한을 확인해 주세요." });
    }
  }, [sortedDisplayData, selectedRows, stepDataColumns, columnLabels, getRowKey]);

  // 🆕 통합 검색 실행
  const executeGlobalSearch = useCallback((term: string) => {
    if (!term.trim()) {
      setSearchHighlights(new Set());
      return;
    }

    const highlights = new Set<string>();
    const lowerTerm = term.toLowerCase();

    // 전체 데이터에서 검색하여 페이지 이동 및 하이라이트 정보 저장
    sortedDisplayData.forEach((row, rowIndex) => {
      stepDataColumns.forEach((col, colIndex) => {
        const value = String(row[col] ?? "").toLowerCase();
        if (value.includes(lowerTerm)) {
          // 체크박스 컬럼 offset 고려 (allowDataMove가 true면 +1)
          const adjustedColIndex = allowDataMove ? colIndex + 1 : colIndex;
          highlights.add(`${rowIndex}-${adjustedColIndex}`);
        }
      });
    });

    setSearchHighlights(highlights);
    setCurrentSearchIndex(0);

    if (highlights.size === 0) {
      toast.info("검색 결과가 없습니다.");
    } else {
      // 첫 번째 검색 결과가 있는 페이지로 이동
      const firstHighlight = Array.from(highlights)[0];
      const [rowIndexStr] = firstHighlight.split("-");
      const rowIndex = parseInt(rowIndexStr);
      const targetPage = Math.floor(rowIndex / stepDataPageSize) + 1;
      setStepDataPage(targetPage);
      
      toast.success(`${highlights.size}개 결과를 찾았습니다.`);
    }
  }, [sortedDisplayData, stepDataColumns, allowDataMove, stepDataPageSize]);

  // 🆕 검색 결과 이동
  const goToNextSearchResult = useCallback(() => {
    if (searchHighlights.size === 0) return;
    const newIndex = (currentSearchIndex + 1) % searchHighlights.size;
    setCurrentSearchIndex(newIndex);
    
    // 해당 검색 결과가 있는 페이지로 이동
    const highlightArray = Array.from(searchHighlights);
    const [rowIndexStr] = highlightArray[newIndex].split("-");
    const rowIndex = parseInt(rowIndexStr);
    const targetPage = Math.floor(rowIndex / stepDataPageSize) + 1;
    if (targetPage !== stepDataPage) {
      setStepDataPage(targetPage);
    }
  }, [searchHighlights, currentSearchIndex, stepDataPageSize, stepDataPage]);

  const goToPrevSearchResult = useCallback(() => {
    if (searchHighlights.size === 0) return;
    const newIndex = (currentSearchIndex - 1 + searchHighlights.size) % searchHighlights.size;
    setCurrentSearchIndex(newIndex);
    
    // 해당 검색 결과가 있는 페이지로 이동
    const highlightArray = Array.from(searchHighlights);
    const [rowIndexStr] = highlightArray[newIndex].split("-");
    const rowIndex = parseInt(rowIndexStr);
    const targetPage = Math.floor(rowIndex / stepDataPageSize) + 1;
    if (targetPage !== stepDataPage) {
      setStepDataPage(targetPage);
    }
  }, [searchHighlights, currentSearchIndex, stepDataPageSize, stepDataPage]);


  // 🆕 검색 초기화
  const clearGlobalSearch = useCallback(() => {
    setGlobalSearchTerm("");
    setSearchHighlights(new Set());
    setIsSearchPanelOpen(false);
    setCurrentSearchIndex(0);
  }, []);

  // 🆕 새로고침
  const handleRefresh = useCallback(async () => {
    if (!selectedStepId) return;
    
    setStepDataLoading(true);
    try {
      const response = await getStepDataList(selectedStepId);
      if (response.success && response.data) {
        setStepData(response.data.data || []);
        if (response.data.columns) {
          const currentStep = steps.find((s) => s.id === selectedStepId);
          const visibleCols = getVisibleColumns(selectedStepId, response.data.columns, steps);
          setStepDataColumns(visibleCols);
          setAllAvailableColumns(response.data.columns);
        }
      }
      toast.success("데이터를 새로고침했습니다.");
    } catch (error) {
      console.error("새로고침 오류:", error);
      showErrorToast("데이터 새로고침에 실패했습니다", error, { guidance: "잠시 후 다시 시도해 주세요." });
    } finally {
      setStepDataLoading(false);
    }
  }, [selectedStepId, steps, getVisibleColumns]);

  // 🆕 셀 더블클릭 시 편집 모드 진입
  const handleCellDoubleClick = useCallback((rowIndex: number, colIndex: number, columnName: string, value: any) => {
    // 체크박스 컬럼은 편집 불가
    if (columnName === "__checkbox__") return;
    
    setEditingCell({ rowIndex, colIndex, columnName, originalValue: value });
    setEditingValue(value !== null && value !== undefined ? String(value) : "");
  }, []);

  // 🆕 편집 취소
  const cancelEditing = useCallback(() => {
    setEditingCell(null);
    setEditingValue("");
  }, []);

  // 🆕 편집 저장 (플로우 스텝 데이터 업데이트)
  const saveEditing = useCallback(async () => {
    if (!editingCell || !selectedStepId || !flowId) return;

    const { rowIndex, columnName, originalValue } = editingCell;
    const newValue = editingValue;

    // 값이 변경되지 않았으면 그냥 닫기
    if (String(originalValue ?? "") === newValue) {
      cancelEditing();
      return;
    }

    try {
      // 페이지네이션을 고려한 실제 인덱스 계산
      const actualIndex = (stepDataPage - 1) * stepDataPageSize + rowIndex;
      
      // 현재 행의 데이터 가져오기 (정렬된 전체 데이터에서)
      const currentRow = paginatedStepData[rowIndex];
      if (!currentRow) {
        toast.error("데이터를 찾을 수 없습니다.");
        cancelEditing();
        return;
      }

      // Primary Key 값 찾기 (일반적으로 id 또는 첫 번째 컬럼)
      // 플로우 정의에서 primaryKey를 가져오거나, 기본값으로 id 사용
      const primaryKeyColumn = flowData?.primaryKey || "id";
      const recordId = currentRow[primaryKeyColumn] || currentRow.id;
      
      if (!recordId) {
        toast.error("레코드 ID를 찾을 수 없습니다. Primary Key 설정을 확인해주세요.");
        cancelEditing();
        return;
      }

      // API 호출하여 데이터 업데이트
      const { updateFlowStepData } = await import("@/lib/api/flow");
      const response = await updateFlowStepData(flowId, selectedStepId, recordId, { [columnName]: newValue });

      if (response.success) {
        // 로컬 상태 업데이트
        setStepData((prev) => {
          const newData = [...prev];
          // 원본 데이터에서 해당 레코드 찾기
          const targetIndex = newData.findIndex((row) => {
            const rowRecordId = row[primaryKeyColumn] || row.id;
            return rowRecordId === recordId;
          });
          if (targetIndex !== -1) {
            newData[targetIndex] = { ...newData[targetIndex], [columnName]: newValue };
          }
          return newData;
        });
        toast.success("데이터가 저장되었습니다.");
      } else {
        toast.error(response.error || "저장에 실패했습니다.");
      }
    } catch (error) {
      console.error("편집 저장 오류:", error);
      showErrorToast("데이터 저장에 실패했습니다", error, { guidance: "입력 데이터를 확인하고 다시 시도해 주세요." });
    }

    cancelEditing();
  }, [editingCell, editingValue, selectedStepId, flowId, flowData, paginatedStepData, stepDataPage, stepDataPageSize, cancelEditing]);

  // 🆕 편집 키보드 핸들러
  const handleEditKeyDown = useCallback((e: React.KeyboardEvent<HTMLInputElement>) => {
    switch (e.key) {
      case "Enter":
        e.preventDefault();
        saveEditing();
        break;
      case "Escape":
        e.preventDefault();
        cancelEditing();
        break;
      case "Tab":
        e.preventDefault();
        saveEditing();
        break;
    }
  }, [saveEditing, cancelEditing]);

  // 🆕 편집 입력 필드 자동 포커스
  useEffect(() => {
    if (editingCell && editInputRef.current) {
      editInputRef.current.focus();
      editInputRef.current.select();
    }
  }, [editingCell]);

  if (loading) {
    return (
      <div className="flex items-center justify-center p-8">
        <Loader2 className="text-muted-foreground h-6 w-6 animate-spin" />
        <span className="text-muted-foreground ml-2 text-sm">플로우 로딩 중...</span>
      </div>
    );
  }

  if (error) {
    return (
      <div className="border-destructive/50 bg-destructive/10 flex items-center gap-2 rounded-lg border p-4">
        <AlertCircle className="text-destructive h-5 w-5" />
        <span className="text-destructive text-sm">{error}</span>
      </div>
    );
  }

  if (!flowId || !flowData) {
    return (
      <div className="border-muted-foreground/25 flex items-center justify-center rounded-lg border-2 border-dashed p-8">
        <span className="text-muted-foreground text-sm">플로우를 선택해주세요</span>
      </div>
    );
  }

  if (steps.length === 0) {
    return (
      <div className="border-muted flex items-center justify-center rounded-lg border p-8">
        <span className="text-muted-foreground text-sm">플로우에 스텝이 없습니다</span>
      </div>
    );
  }

  // 반응형 컨테이너 클래스
  const containerClass =
    displayMode === "horizontal"
      ? "flex flex-col sm:flex-row sm:flex-wrap items-center justify-center gap-3 sm:gap-4"
      : "flex flex-col items-center gap-4";

  return (
    <div className="@container flex w-full flex-col p-2 sm:p-4 lg:p-6">
      {/* 플로우 스텝 목록 */}
      <div className={`${containerClass} flex-shrink-0`}>
        {steps.map((step, index) => (
          <React.Fragment key={step.id}>
            {/* 스텝 카드 */}
            <div
              className="group relative w-full cursor-pointer pb-4 transition-all duration-300 sm:w-auto sm:min-w-[200px] lg:min-w-[240px]"
              onClick={() => handleStepClick(step.id, step.stepName)}
            >
              {/* 콘텐츠 */}
              <div className="relative flex flex-col items-center justify-center gap-2 pb-5 sm:gap-2.5 sm:pb-6">
              {/* 스텝 이름 */}
                <h4
                  className={`text-base font-semibold leading-tight transition-colors duration-300 sm:text-lg lg:text-xl ${
                    selectedStepId === step.id ? "text-primary" : "text-foreground group-hover:text-primary/80"
                  }`}
                >
                {step.stepName}
              </h4>

              {/* 데이터 건수 */}
              {showStepCount && (
                  <div
                    className={`flex items-center gap-1.5 transition-all duration-300 ${
                      selectedStepId === step.id
                        ? "text-primary"
                        : "text-muted-foreground group-hover:text-primary"
                    }`}
                  >
                    <span className="text-sm font-medium sm:text-base">
                      {(stepCounts[step.id] || 0).toLocaleString("ko-KR")}
                    </span>
                    <span className="text-xs font-normal sm:text-sm">건</span>
                </div>
              )}
              </div>

              {/* 하단 선 */}
              <div
                className={`h-0.5 transition-all duration-300 ${
                  selectedStepId === step.id
                    ? "bg-primary"
                    : "bg-border group-hover:bg-primary/50"
                }`}
              />
            </div>

            {/* 화살표 (마지막 스텝 제외) */}
            {index < steps.length - 1 && (
              <div className="flex shrink-0 items-center justify-center py-2 sm:py-0">
                {displayMode === "horizontal" ? (
                  <div className="flex items-center gap-1">
                    <div className="h-0.5 w-6 bg-border sm:w-8" />
                  <svg
                      className="h-4 w-4 text-muted-foreground sm:h-5 sm:w-5"
                    fill="none"
                    viewBox="0 0 24 24"
                    stroke="currentColor"
                  >
                    <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
                  </svg>
                    <div className="h-0.5 w-6 bg-border sm:w-8" />
                  </div>
                ) : (
                  <div className="flex flex-col items-center gap-1">
                    <div className="h-6 w-0.5 bg-border sm:h-8" />
                    <svg
                      className="h-4 w-4 text-muted-foreground sm:h-5 sm:w-5"
                      fill="none"
                      viewBox="0 0 24 24"
                      stroke="currentColor"
                    >
                    <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
                  </svg>
                    <div className="h-6 w-0.5 bg-border sm:h-8" />
                  </div>
                )}
              </div>
            )}
          </React.Fragment>
        ))}
      </div>

      {/* 선택된 스텝의 데이터 리스트 */}
      {selectedStepId !== null && (
        <div className="mt-4 flex w-full flex-col sm:mt-6 lg:mt-8 border rounded-lg overflow-hidden">
          {/* 🆕 DevExpress 스타일 기능 툴바 */}
          {stepDataColumns.length > 0 && (
            <>
              <div className="border-border bg-muted/20 flex flex-wrap items-center gap-1 border-b px-2 py-1.5 sm:gap-2 sm:px-4 sm:py-2">
                {/* 내보내기 버튼들 */}
                <div className="flex items-center gap-1 border-r border-border pr-2">
                  <Button
                    variant="ghost"
                    size="sm"
                    onClick={exportToExcel}
                    className="h-7 text-xs"
                    title="Excel 내보내기"
                  >
                    <FileSpreadsheet className="mr-1 h-3 w-3 text-emerald-600" />
                    Excel
                  </Button>
                  <Button
                    variant="ghost"
                    size="sm"
                    onClick={exportToPdf}
                    className="h-7 text-xs"
                    title="PDF 내보내기"
                  >
                    <FileText className="mr-1 h-3 w-3 text-destructive" />
                    PDF
                  </Button>
                </div>

                {/* 복사 버튼 */}
                <div className="flex items-center gap-1 border-r border-border pr-2">
                  <Button
                    variant="ghost"
                    size="sm"
                    onClick={handleCopy}
                    disabled={selectedRows.size === 0}
                    className="h-7 text-xs"
                    title="복사 (Ctrl+C)"
                  >
                    <Copy className="mr-1 h-3 w-3" />
                    복사
                  </Button>
                </div>

                {/* 선택 정보 */}
                {selectedRows.size > 0 && (
                  <div className="flex items-center gap-1 border-r border-border pr-2">
                    <span className="bg-primary/10 text-primary rounded px-2 py-0.5 text-xs">
                      {selectedRows.size}개 선택됨
                    </span>
                    <Button
                      variant="ghost"
                      size="sm"
                      onClick={() => setSelectedRows(new Set())}
                      className="h-6 w-6 p-0"
                      title="선택 해제"
                    >
                      <X className="h-3 w-3" />
                    </Button>
                  </div>
                )}

                {/* 🆕 통합 검색 패널 */}
                <div className="flex items-center gap-1 border-r border-border pr-2">
                  {isSearchPanelOpen ? (
                    <div className="flex items-center gap-1">
                      <input
                        type="text"
                        value={globalSearchTerm}
                        onChange={(e) => setGlobalSearchTerm(e.target.value)}
                        onKeyDown={(e) => {
                          if (e.key === "Enter") {
                            executeGlobalSearch(globalSearchTerm);
                          } else if (e.key === "Escape") {
                            clearGlobalSearch();
                          }
                        }}
                        placeholder="검색어 입력... (Enter)"
                        className="border-input bg-background h-7 w-32 rounded border px-2 text-xs focus:outline-none focus:ring-1 focus:ring-primary sm:w-48"
                        autoFocus
                      />
                      {searchHighlights.size > 0 && (
                        <span className="text-muted-foreground text-xs whitespace-nowrap">
                          {currentSearchIndex + 1}/{searchHighlights.size}
                        </span>
                      )}
                      <Button
                        variant="ghost"
                        size="sm"
                        onClick={goToPrevSearchResult}
                        disabled={searchHighlights.size === 0}
                        className="h-6 w-6 p-0"
                        title="이전"
                      >
                        <ChevronLeft className="h-3 w-3" />
                      </Button>
                      <Button
                        variant="ghost"
                        size="sm"
                        onClick={goToNextSearchResult}
                        disabled={searchHighlights.size === 0}
                        className="h-6 w-6 p-0"
                        title="다음"
                      >
                        <ChevronRight className="h-3 w-3" />
                      </Button>
                      <Button
                        variant="ghost"
                        size="sm"
                        onClick={clearGlobalSearch}
                        className="h-6 w-6 p-0"
                        title="닫기 (Esc)"
                      >
                        <X className="h-3 w-3" />
                      </Button>
                    </div>
                  ) : (
                    <Button
                      variant="ghost"
                      size="sm"
                      onClick={() => setIsSearchPanelOpen(true)}
                      className="h-7 text-xs"
                      title="통합 검색 (Ctrl+F)"
                    >
                      <Filter className="mr-1 h-3 w-3" />
                      검색
                        </Button>
                      )}
                </div>
                  
                  {/* 필터/그룹 설정 버튼 */}
                <div className="flex items-center gap-1 border-r border-border pr-2">
                    <Button
                    variant={searchFilterColumns.size > 0 ? "default" : "ghost"}
                      size="sm"
                      onClick={() => {
                      if (!isPreviewMode) {
                        setIsFilterSettingOpen(true);
                      }
                      }}
                      disabled={isPreviewMode}
                    className="h-7 text-xs"
                    title="검색 필터 설정"
                    >
                    <Filter className="mr-1 h-3 w-3" />
                    필터
                      {searchFilterColumns.size > 0 && (
                      <span className="ml-1 rounded-full bg-white/20 px-1.5 text-[10px]">
                          {searchFilterColumns.size}
                      </span>
                      )}
                    </Button>
                  <Button
                    variant={groupByColumns.length > 0 ? "default" : "ghost"}
                      size="sm"
                      onClick={() => {
                      if (!isPreviewMode) {
                        setIsGroupSettingOpen(true);
                      }
                      }}
                      disabled={isPreviewMode}
                    className="h-7 text-xs"
                    title="그룹 설정"
                    >
                    <Layers className="mr-1 h-3 w-3" />
                    그룹
                      {groupByColumns.length > 0 && (
                      <span className="ml-1 rounded-full bg-white/20 px-1.5 text-[10px]">
                          {groupByColumns.length}
                      </span>
                    )}
                  </Button>
                  </div>

                {/* 새로고침 */}
                <div className="ml-auto flex items-center gap-1">
                  <Button
                    variant="ghost"
                    size="sm"
                    onClick={handleRefresh}
                    disabled={stepDataLoading}
                    className="h-7 text-xs"
                    title="새로고침"
                  >
                    <RefreshCw className={`mr-1 h-3 w-3 ${stepDataLoading ? "animate-spin" : ""}`} />
                    새로고침
                  </Button>
                </div>
          </div>

              {/* 검색 필터 입력 영역 */}
              {searchFilterColumns.size > 0 && (
                <div className="bg-background flex-shrink-0 border-b px-4 py-2">
                  <div className="flex items-center gap-2 flex-wrap">
                    {Array.from(searchFilterColumns).map((col) => (
                      <Input
                        key={col}
                        value={searchValues[col] || ""}
                        onChange={(e) =>
                          setSearchValues((prev) => ({
                            ...prev,
                            [col]: e.target.value,
                          }))
                        }
                        placeholder={`${columnLabels[col] || col} 검색...`}
                        className="h-8 text-xs w-40"
                      />
                    ))}
                    {Object.keys(searchValues).length > 0 && (
                      <Button variant="ghost" size="sm" onClick={handleClearSearch} className="h-8 text-xs">
                        <X className="mr-1 h-3 w-3" />
                        초기화
                      </Button>
                    )}
                  </div>
                </div>
              )}

              {/* 🆕 그룹 표시 배지 */}
              {groupByColumns.length > 0 && (
              <div className="border-b border-border bg-muted/30 px-4 py-2">
                <div className="flex items-center gap-2 text-xs sm:text-sm">
                  <span className="text-muted-foreground">그룹:</span>
                  <div className="flex flex-wrap items-center gap-2">
                    {groupByColumns.map((col, idx) => (
                      <span key={col} className="flex items-center">
                        {idx > 0 && <span className="text-muted-foreground mx-1">→</span>}
                        <span className="bg-primary/10 text-primary rounded px-2 py-1 text-xs font-medium">
                          {columnLabels[col] || col}
                        </span>
                      </span>
                    ))}
                  </div>
                  <button
                    onClick={() => {
                      if (!isPreviewMode) {
                        clearGrouping();
                      }
                    }}
                    disabled={isPreviewMode}
                    className="hover:bg-destructive/10 text-destructive ml-auto rounded p-1"
                    title="그룹 해제"
                  >
                    <X className="h-4 w-4" />
                  </button>
                </div>
              </div>
            )}
            </>
          )}

          {/* 데이터 영역 - 고정 높이 + 스크롤 */}
          {stepDataLoading ? (
            <div className="flex h-64 items-center justify-center">
              <Loader2 className="text-primary h-6 w-6 animate-spin sm:h-8 sm:w-8" />
              <span className="text-muted-foreground ml-2 text-sm">데이터 로딩 중...</span>
            </div>
          ) : stepData.length === 0 ? (
            <div className="flex h-64 flex-col items-center justify-center">
              <svg
                className="text-muted-foreground/50 mb-3 h-12 w-12"
                fill="none"
                viewBox="0 0 24 24"
                stroke="currentColor"
              >
                <path
                  strokeLinecap="round"
                  strokeLinejoin="round"
                  strokeWidth={1.5}
                  d="M20 13V6a2 2 0 00-2-2H6a2 2 0 00-2 2v7m16 0v5a2 2 0 01-2 2H6a2 2 0 01-2-2v-5m16 0h-2.586a1 1 0 00-.707.293l-2.414 2.414a1 1 0 01-.707.293h-3.172a1 1 0 01-.707-.293l-2.414-2.414A1 1 0 006.586 13H4"
                />
              </svg>
              <span className="text-muted-foreground text-sm">데이터가 없습니다</span>
            </div>
          ) : (
            <>
              {/* 모바일: 카드 뷰 - 고정 높이 + 스크롤 */}
              <div className="overflow-y-auto @sm:hidden" style={{ height: "450px" }}>
                <div className="space-y-2 p-3">
                  {paginatedStepData.map((row, pageIndex) => {
                    const actualIndex = (stepDataPage - 1) * stepDataPageSize + pageIndex;
                    return (
                      <div
                        key={actualIndex}
                        className={`bg-card rounded-md border p-3 transition-colors ${
                          selectedRows.has(getRowKey(row)) ? "bg-primary/5 border-primary/30" : ""
                        }`}
                      >
                    {allowDataMove && (
                      <div className="mb-2 flex items-center justify-between border-b pb-2">
                        <span className="text-muted-foreground text-xs font-medium">선택</span>
                            <Checkbox
                              checked={selectedRows.has(getRowKey(row))}
                              onCheckedChange={() => toggleRowSelection(row)}
                            />
                      </div>
                    )}
                        <div className="space-y-1.5">
                      {stepDataColumns.map((col) => (
                            <div key={col} className="flex justify-between gap-2 text-xs">
                              <span className="text-muted-foreground font-medium">{columnLabels[col] || col}:</span>
                              <span className="text-foreground truncate">{formatValue(row[col])}</span>
                        </div>
                      ))}
                    </div>
                  </div>
                    );
                  })}
                </div>
              </div>

              {/* 데스크톱: 테이블 뷰 - SingleTableWithSticky 사용 */}
              <div className="relative hidden @sm:block">
                {groupByColumns.length > 0 && groupedData.length > 0 ? (
                  // 그룹화된 렌더링 (기존 방식 유지)
                  <div className="overflow-x-auto">
                <Table noWrapper>
                      <TableHeader className="sticky top-0 z-30 bg-background shadow-sm">
                    <TableRow className="hover:bg-muted/50">
                      {allowDataMove && (
                            <TableHead className="bg-background sticky left-0 z-40 w-12 border-b px-6 py-3 text-center">
                          <Checkbox
                            checked={selectedRows.size === stepData.length && stepData.length > 0}
                            onCheckedChange={toggleAllRows}
                          />
                        </TableHead>
                      )}
                      {stepDataColumns.map((col) => (
                        <TableHead
                          key={col}
                              className="bg-background border-b px-6 py-3 text-sm font-semibold whitespace-nowrap cursor-pointer hover:bg-muted/50"
                              onClick={() => handleSort(col)}
                            >
                              <div className="flex items-center gap-2">
                                <span>{columnLabels[col] || col}</span>
                                {sortColumn === col && (
                                  <span className="text-primary">
                                    {sortDirection === "asc" ? "↑" : "↓"}
                                  </span>
                                )}
                              </div>
                        </TableHead>
                      ))}
                    </TableRow>
                  </TableHeader>
                  <TableBody>
                        {groupedData.flatMap((group) => {
                        const isCollapsed = collapsedGroups.has(group.groupKey);
                        const groupRows = [
                          <TableRow key={`group-${group.groupKey}`}>
                            <TableCell
                              colSpan={stepDataColumns.length + (allowDataMove ? 1 : 0)}
                              className="bg-muted/50 border-b"
                            >
                              <div
                                className="flex items-center gap-3 p-2 cursor-pointer hover:bg-muted"
                                onClick={() => toggleGroupCollapse(group.groupKey)}
                              >
                                {isCollapsed ? (
                                  <ChevronRight className="h-4 w-4 flex-shrink-0" />
                                ) : (
                                  <ChevronDown className="h-4 w-4 flex-shrink-0" />
                                )}
                                <span className="font-medium text-sm flex-1">{group.groupKey}</span>
                                <span className="text-muted-foreground text-xs">({group.count}건)</span>
                              </div>
                            </TableCell>
                          </TableRow>,
                        ];

                        if (!isCollapsed) {
                          const dataRows = group.items.map((row, itemIndex) => {
                              const actualIndex = sortedDisplayData.indexOf(row);
                            return (
                      <TableRow
                                key={`${group.groupKey}-${itemIndex}`}
                                className={`h-16 transition-colors hover:bg-muted/50 ${selectedRows.has(getRowKey(row)) ? "bg-primary/5" : ""}`}
                      >
                        {allowDataMove && (
                                  <TableCell className="bg-background sticky left-0 z-10 border-b px-6 py-3 text-center">
                            <Checkbox
                                      checked={selectedRows.has(getRowKey(row))}
                                      onCheckedChange={() => toggleRowSelection(row)}
                            />
                          </TableCell>
                        )}
                        {stepDataColumns.map((col) => (
                                  <TableCell key={col} className="h-16 border-b px-6 py-3 text-sm whitespace-nowrap">
                                    {formatValue(row[col])}
                                  </TableCell>
                                ))}
                              </TableRow>
                            );
                          });
                          groupRows.push(...dataRows);
                        }

                        return groupRows;
                        })}
                      </TableBody>
                    </Table>
                  </div>
                    ) : (
                  // 일반 렌더링 - SingleTableWithSticky 사용
                  <SingleTableWithSticky
                    visibleColumns={tableColumns}
                    columns={tableColumns}
                    data={paginatedStepData}
                    columnLabels={columnLabels}
                    sortColumn={sortColumn}
                    sortDirection={sortDirection}
                    tableConfig={tableConfig}
                    isDesignMode={false}
                    isAllSelected={selectedRows.size === sortedDisplayData.length && sortedDisplayData.length > 0}
                    onSort={handleSort}
                    handleSelectAll={handleSelectAll}
                    handleRowClick={handleRowClick}
                    renderCheckboxCell={renderCheckboxCell}
                    formatCellValue={formatCellValue}
                    getColumnWidth={getColumnWidth}
                    loading={stepDataLoading}
                    // 인라인 편집 props
                    onCellDoubleClick={handleCellDoubleClick}
                    editingCell={editingCell}
                    editingValue={editingValue}
                    onEditingValueChange={setEditingValue}
                    onEditKeyDown={handleEditKeyDown}
                    editInputRef={editInputRef}
                    // 검색 하이라이트 props (현재 페이지 기준으로 변환된 값)
                    searchHighlights={pageSearchHighlights}
                    currentSearchIndex={pageCurrentSearchIndex}
                    searchTerm={globalSearchTerm}
                  />
                )}
              </div>
            </>
          )}

          {/* 페이지네이션 - 항상 하단에 고정 */}
          {!stepDataLoading && stepData.length > 0 && (
            <div className="bg-background flex-shrink-0 border-t px-4 py-3 sm:px-6">
              <div className="flex flex-col items-center justify-between gap-3 sm:flex-row">
                {/* 왼쪽: 페이지 정보 + 페이지 크기 선택 */}
                <div className="flex flex-col items-center gap-2 sm:flex-row sm:gap-4">
                  <div className="text-muted-foreground text-xs sm:text-sm">
                    페이지 {stepDataPage} / {totalStepDataPages} (총 {stepData.length.toLocaleString("ko-KR")}건)
        </div>
                  <div className="flex items-center gap-2">
                    <span className="text-muted-foreground text-xs">표시 개수:</span>
                    <Select
                      value={stepDataPageSize.toString()}
                      onValueChange={(value) => {
                        setStepDataPageSize(Number(value));
                        setStepDataPage(1); // 페이지 크기 변경 시 첫 페이지로
                      }}
                    >
                      <SelectTrigger className="h-6 w-full px-2 py-0 text-xs">
                        <SelectValue />
                      </SelectTrigger>
                      <SelectContent>
                        <SelectItem value="10">10개</SelectItem>
                        <SelectItem value="20">20개</SelectItem>
                        <SelectItem value="50">50개</SelectItem>
                        <SelectItem value="100">100개</SelectItem>
                      </SelectContent>
                    </Select>
                  </div>
                </div>

                {/* 오른쪽: 페이지네이션 */}
                {totalStepDataPages > 1 && (
                  <Pagination>
                    <PaginationContent>
                      <PaginationItem>
                        <PaginationPrevious
                          onClick={() => {
                            if (isPreviewMode) {
                              return;
                            }
                            setStepDataPage((p) => Math.max(1, p - 1));
                          }}
                          className={
                            stepDataPage === 1 || isPreviewMode ? "pointer-events-none opacity-50" : "cursor-pointer"
                          }
                        />
                      </PaginationItem>
                      {totalStepDataPages <= 7 ? (
                        Array.from({ length: totalStepDataPages }, (_, i) => i + 1).map((page) => (
                          <PaginationItem key={page}>
                            <PaginationLink
                              onClick={() => {
                                if (isPreviewMode) {
                                  return;
                                }
                                setStepDataPage(page);
                              }}
                              isActive={stepDataPage === page}
                              className={isPreviewMode ? "pointer-events-none opacity-50" : "cursor-pointer"}
                            >
                              {page}
                            </PaginationLink>
                          </PaginationItem>
                        ))
                      ) : (
                        <>
                          {Array.from({ length: totalStepDataPages }, (_, i) => i + 1)
                            .filter((page) => {
                              return (
                                page === 1 ||
                                page === totalStepDataPages ||
                                (page >= stepDataPage - 2 && page <= stepDataPage + 2)
                              );
                            })
                            .map((page, idx, arr) => (
                              <React.Fragment key={page}>
                                {idx > 0 && arr[idx - 1] !== page - 1 && (
                                  <PaginationItem>
                                    <span className="text-muted-foreground px-2">...</span>
                                  </PaginationItem>
                                )}
                                <PaginationItem>
                                  <PaginationLink
                                    onClick={() => {
                                      if (isPreviewMode) {
                                        return;
                                      }
                                      setStepDataPage(page);
                                    }}
                                    isActive={stepDataPage === page}
                                    className={isPreviewMode ? "pointer-events-none opacity-50" : "cursor-pointer"}
                                  >
                                    {page}
                                  </PaginationLink>
                                </PaginationItem>
                              </React.Fragment>
                            ))}
                        </>
                      )}
                      <PaginationItem>
                        <PaginationNext
                          onClick={() => {
                            if (isPreviewMode) {
                              return;
                            }
                            setStepDataPage((p) => Math.min(totalStepDataPages, p + 1));
                          }}
                          className={
                            stepDataPage === totalStepDataPages || isPreviewMode
                              ? "pointer-events-none opacity-50"
                              : "cursor-pointer"
                          }
                        />
                      </PaginationItem>
                    </PaginationContent>
                  </Pagination>
                )}
              </div>
            </div>
          )}
        </div>
      )}

      {/* 🆕 검색 필터 설정 다이얼로그 */}
      <Dialog open={isFilterSettingOpen} onOpenChange={setIsFilterSettingOpen}>
        <DialogContent className="max-w-[95vw] sm:max-w-[500px]">
          <DialogHeader>
            <DialogTitle className="text-base sm:text-lg">검색 필터 설정</DialogTitle>
            <DialogDescription className="text-xs sm:text-sm">
              검색 필터로 사용할 컬럼을 선택하세요. 선택한 컬럼의 검색 입력 필드가 표시됩니다.
            </DialogDescription>
          </DialogHeader>

          <div className="space-y-3 sm:space-y-4">
            {/* 전체 선택/해제 */}
            <div className="bg-muted/50 flex items-center gap-3 rounded border p-3">
              <Checkbox
                id="select-all-filters"
                checked={searchFilterColumns.size === stepDataColumns.length && stepDataColumns.length > 0}
                onCheckedChange={toggleAllFilters}
              />
              <Label htmlFor="select-all-filters" className="flex-1 cursor-pointer text-xs font-semibold sm:text-sm">
                전체 선택/해제
              </Label>
              <span className="text-muted-foreground text-xs">
                {searchFilterColumns.size} / {stepDataColumns.length}개
              </span>
            </div>

            {/* 컬럼 목록 */}
            <div className="max-h-[50vh] space-y-2 overflow-y-auto rounded border p-2">
              {stepDataColumns.map((col) => (
                <div key={col} className="hover:bg-muted/50 flex items-center gap-3 rounded p-2">
                  <Checkbox
                    id={`filter-${col}`}
                    checked={searchFilterColumns.has(col)}
                    onCheckedChange={() => toggleFilterColumn(col)}
                  />
                  <Label htmlFor={`filter-${col}`} className="flex-1 cursor-pointer text-xs font-normal sm:text-sm">
                    {columnLabels[col] || col}
                  </Label>
                </div>
              ))}
            </div>

            {/* 선택된 컬럼 개수 안내 */}
            <div className="text-muted-foreground bg-muted/30 rounded p-3 text-center text-xs">
              {searchFilterColumns.size === 0 ? (
                <span>검색 필터를 사용하려면 최소 1개 이상의 컬럼을 선택하세요</span>
              ) : (
                <span>
                  총 <span className="text-primary font-semibold">{searchFilterColumns.size}개</span>의 검색 필터가
                  표시됩니다
                </span>
              )}
            </div>
          </div>

          <DialogFooter className="gap-2 sm:gap-0">
            <Button
              variant="outline"
              onClick={() => setIsFilterSettingOpen(false)}
              className="h-8 flex-1 text-xs sm:h-10 sm:flex-none sm:text-sm"
            >
              취소
            </Button>
            <Button onClick={saveFilterSettings} className="h-8 flex-1 text-xs sm:h-10 sm:flex-none sm:text-sm">
              저장
            </Button>
          </DialogFooter>
        </DialogContent>
      </Dialog>

      {/* 🆕 그룹 설정 다이얼로그 */}
      <Dialog open={isGroupSettingOpen} onOpenChange={setIsGroupSettingOpen}>
        <DialogContent className="max-w-[95vw] sm:max-w-[500px]">
          <DialogHeader>
            <DialogTitle className="text-base sm:text-lg">그룹 설정</DialogTitle>
            <DialogDescription className="text-xs sm:text-sm">
              데이터를 그룹화할 컬럼을 선택하세요. 여러 컬럼을 선택하면 계층적으로 그룹화됩니다.
            </DialogDescription>
          </DialogHeader>

          <div className="space-y-3 sm:space-y-4">
            {/* 컬럼 목록 */}
            <div className="max-h-[50vh] space-y-2 overflow-y-auto rounded border p-2">
              {stepDataColumns.map((col) => (
                <div key={col} className="hover:bg-muted/50 flex items-center gap-3 rounded p-2">
                  <Checkbox
                    id={`group-${col}`}
                    checked={groupByColumns.includes(col)}
                    onCheckedChange={() => toggleGroupColumn(col)}
                  />
                  <Label htmlFor={`group-${col}`} className="flex-1 cursor-pointer text-xs font-normal sm:text-sm">
                    {columnLabels[col] || col}
                  </Label>
                </div>
              ))}
            </div>

            {/* 선택된 그룹 안내 */}
            <div className="text-muted-foreground bg-muted/30 rounded p-3 text-xs">
              {groupByColumns.length === 0 ? (
                <span>그룹화할 컬럼을 선택하세요</span>
              ) : (
                <span>
                  선택된 그룹:{" "}
                  <span className="text-primary font-semibold">
                    {groupByColumns.map((col) => columnLabels[col] || col).join(" → ")}
                  </span>
                </span>
              )}
            </div>
          </div>

          <DialogFooter className="gap-2 sm:gap-0">
            <Button
              variant="outline"
              onClick={() => setIsGroupSettingOpen(false)}
              className="h-8 flex-1 text-xs sm:h-10 sm:flex-none sm:text-sm"
            >
              취소
            </Button>
            <Button onClick={saveGroupSettings} className="h-8 flex-1 text-xs sm:h-10 sm:flex-none sm:text-sm">
              적용
            </Button>
          </DialogFooter>
        </DialogContent>
      </Dialog>
    </div>
  );
}
