"use client";

import React, { useState, useEffect, useMemo, useCallback, useRef } from "react";
import { TableListConfig, ColumnConfig } from "./types";
import { WebType } from "@/types/common";
import { tableTypeApi } from "@/lib/api/screen";
import { entityJoinApi } from "@/lib/api/entityJoin";
import { codeCache } from "@/lib/caching/codeCache";
import { useEntityJoinOptimization } from "@/lib/hooks/useEntityJoinOptimization";
import { getFullImageUrl } from "@/lib/api/client";
import { Button } from "@/components/ui/button";
import {
  ChevronLeft,
  ChevronRight,
  ChevronsLeft,
  ChevronsRight,
  RefreshCw,
  ArrowUp,
  ArrowDown,
  TableIcon,
  Settings,
  X,
  Layers,
  ChevronDown,
} from "lucide-react";
import { Checkbox } from "@/components/ui/checkbox";
import { cn } from "@/lib/utils";
import { toast } from "sonner";
import { tableDisplayStore } from "@/stores/tableDisplayStore";
import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
} from "@/components/ui/dialog";
import {
  Popover,
  PopoverContent,
  PopoverTrigger,
} from "@/components/ui/popover";
import { Label } from "@/components/ui/label";
import { AdvancedSearchFilters } from "@/components/screen/filters/AdvancedSearchFilters";
import { SingleTableWithSticky } from "./SingleTableWithSticky";
import { CardModeRenderer } from "./CardModeRenderer";
import { TableOptionsModal } from "@/components/common/TableOptionsModal";
import { useTableOptions } from "@/contexts/TableOptionsContext";
import { TableFilter, ColumnVisibility } from "@/types/table-options";

// ========================================
// 인터페이스
// ========================================

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

// ========================================
// 캐시 및 유틸리티
// ========================================

const tableColumnCache = new Map<string, { columns: any[]; timestamp: number }>();
const tableInfoCache = new Map<string, { tables: any[]; timestamp: number }>();
const TABLE_CACHE_TTL = 5 * 60 * 1000; // 5분

const cleanupTableCache = () => {
  const now = Date.now();
  for (const [key, entry] of tableColumnCache.entries()) {
    if (now - entry.timestamp > TABLE_CACHE_TTL) {
      tableColumnCache.delete(key);
    }
  }
  for (const [key, entry] of tableInfoCache.entries()) {
    if (now - entry.timestamp > TABLE_CACHE_TTL) {
      tableInfoCache.delete(key);
    }
  }
};

if (typeof window !== "undefined") {
  setInterval(cleanupTableCache, 10 * 60 * 1000);
}

const debounceTimers = new Map<string, NodeJS.Timeout>();
const activeRequests = new Map<string, Promise<any>>();

const debouncedApiCall = <T extends any[], R>(key: string, fn: (...args: T) => Promise<R>, delay: number = 300) => {
  return (...args: T): Promise<R> => {
    const activeRequest = activeRequests.get(key);
    if (activeRequest) {
      return activeRequest as Promise<R>;
    }

    return new Promise((resolve, reject) => {
      const existingTimer = debounceTimers.get(key);
      if (existingTimer) {
        clearTimeout(existingTimer);
      }

      const timer = setTimeout(async () => {
        try {
          const requestPromise = fn(...args);
          activeRequests.set(key, requestPromise);
          const result = await requestPromise;
          resolve(result);
        } catch (error) {
          reject(error);
        } finally {
          debounceTimers.delete(key);
          activeRequests.delete(key);
        }
      }, delay);

      debounceTimers.set(key, timer);
    });
  };
};

// ========================================
// Props 인터페이스
// ========================================

export interface TableListComponentProps {
  component: any;
  isDesignMode?: boolean;
  isSelected?: boolean;
  isInteractive?: boolean;
  onClick?: () => void;
  onDragStart?: (e: React.DragEvent) => void;
  onDragEnd?: (e: React.DragEvent) => void;
  className?: string;
  style?: React.CSSProperties;
  formData?: Record<string, any>;
  onFormDataChange?: (data: any) => void;
  config?: TableListConfig;
  size?: { width: number; height: number };
  position?: { x: number; y: number; z?: number };
  componentConfig?: any;
  selectedScreen?: any;
  onZoneComponentDrop?: any;
  onZoneClick?: any;
  tableName?: string;
  onRefresh?: () => void;
  onClose?: () => void;
  screenId?: string;
  userId?: string; // 사용자 ID (컬럼 순서 저장용)
  onSelectedRowsChange?: (
    selectedRows: any[],
    selectedRowsData: any[],
    sortBy?: string,
    sortOrder?: "asc" | "desc",
    columnOrder?: string[],
    tableDisplayData?: any[],
  ) => void;
  onConfigChange?: (config: any) => void;
  refreshKey?: number;
}

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

export const TableListComponent: React.FC<TableListComponentProps> = ({
  component,
  isDesignMode = false,
  isSelected = false,
  onClick,
  onDragStart,
  onDragEnd,
  config,
  className,
  style,
  onFormDataChange,
  componentConfig,
  onSelectedRowsChange,
  onConfigChange,
  refreshKey,
  tableName,
  userId,
}) => {
  // ========================================
  // 설정 및 스타일
  // ========================================

  const tableConfig = {
    ...config,
    ...component.config,
    ...componentConfig,
  } as TableListConfig;

  // selectedTable 안전하게 추출 (문자열인지 확인)
  let finalSelectedTable =
    componentConfig?.selectedTable || component.config?.selectedTable || config?.selectedTable || tableName;

  // 디버그 로그 제거 (성능 최적화)

  // 객체인 경우 tableName 속성 추출 시도
  if (typeof finalSelectedTable === "object" && finalSelectedTable !== null) {
    console.warn("⚠️ selectedTable이 객체입니다:", finalSelectedTable);
    finalSelectedTable = (finalSelectedTable as any).tableName || (finalSelectedTable as any).name || tableName;
    console.log("✅ 객체에서 추출한 테이블명:", finalSelectedTable);
  }

  tableConfig.selectedTable = finalSelectedTable;

  // 디버그 로그 제거 (성능 최적화)

  const buttonColor = component.style?.labelColor || "#212121";
  const buttonTextColor = component.config?.buttonTextColor || "#ffffff";

  const gridColumns = component.gridColumns || 1;
  let calculatedWidth: string;

  if (isDesignMode) {
    if (gridColumns === 1) {
      calculatedWidth = "400px";
    } else if (gridColumns === 2) {
      calculatedWidth = "800px";
    } else {
      calculatedWidth = "100%";
    }
  } else {
    calculatedWidth = "100%";
  }

  const componentStyle: React.CSSProperties = {
    width: calculatedWidth,
    height: isDesignMode ? "auto" : "100%",
    minHeight: isDesignMode ? "300px" : "100%",
    display: "flex",
    flexDirection: "column",
    backgroundColor: "hsl(var(--background))",
    overflow: "hidden",
    ...style,
    // width는 항상 100%로 고정 (부모 컨테이너가 gridColumns로 크기 제어)
    width: "100%",
  };

  // ========================================
  // 상태 관리
  // ========================================

  // TableOptions Context
  const { registerTable, unregisterTable } = useTableOptions();
  const [filters, setFilters] = useState<TableFilter[]>([]);
  const [grouping, setGrouping] = useState<string[]>([]);
  const [columnVisibility, setColumnVisibility] = useState<ColumnVisibility[]>([]);

  const [data, setData] = useState<Record<string, any>[]>([]);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [currentPage, setCurrentPage] = useState(1);
  const [totalPages, setTotalPages] = useState(0);
  const [totalItems, setTotalItems] = useState(0);
  const [searchTerm, setSearchTerm] = useState("");
  const [sortColumn, setSortColumn] = useState<string | null>(null);
  const [sortDirection, setSortDirection] = useState<"asc" | "desc">("asc");
  const [columnLabels, setColumnLabels] = useState<Record<string, string>>({});
  const [tableLabel, setTableLabel] = useState<string>("");
  const [localPageSize, setLocalPageSize] = useState<number>(tableConfig.pagination?.pageSize || 20);
  const [displayColumns, setDisplayColumns] = useState<ColumnConfig[]>([]);
  const [joinColumnMapping, setJoinColumnMapping] = useState<Record<string, string>>({});
  const [columnMeta, setColumnMeta] = useState<
    Record<string, { webType?: string; codeCategory?: string; inputType?: string }>
  >({});
  const [categoryMappings, setCategoryMappings] = useState<
    Record<string, Record<string, { label: string; color?: string }>>
  >({});
  const [categoryMappingsKey, setCategoryMappingsKey] = useState(0); // 강제 리렌더링용
  const [searchValues, setSearchValues] = useState<Record<string, any>>({});
  const [selectedRows, setSelectedRows] = useState<Set<string>>(new Set());
  const [columnWidths, setColumnWidths] = useState<Record<string, number>>({});
  const [refreshTrigger, setRefreshTrigger] = useState(0);
  const [columnOrder, setColumnOrder] = useState<string[]>([]);
  const columnRefs = useRef<Record<string, HTMLTableCellElement | null>>({});
  const [isAllSelected, setIsAllSelected] = useState(false);
  const hasInitializedWidths = useRef(false);
  const isResizing = useRef(false);

  // 필터 설정 관련 상태
  const [isFilterSettingOpen, setIsFilterSettingOpen] = useState(false);
  const [visibleFilterColumns, setVisibleFilterColumns] = useState<Set<string>>(new Set());

  // 그룹 설정 관련 상태
  const [groupByColumns, setGroupByColumns] = useState<string[]>([]);
  const [collapsedGroups, setCollapsedGroups] = useState<Set<string>>(new Set());

  // 사용자 옵션 모달 관련 상태
  const [isTableOptionsOpen, setIsTableOptionsOpen] = useState(false);
  const [showGridLines, setShowGridLines] = useState(true);
  const [viewMode, setViewMode] = useState<"table" | "card" | "grouped-card">("table");
  const [frozenColumns, setFrozenColumns] = useState<string[]>([]);

  // 테이블 등록 (Context에 등록)
  const tableId = `table-list-${component.id}`;
  
  useEffect(() => {
    if (!tableConfig.selectedTable || !displayColumns || displayColumns.length === 0) {
      return;
    }

    registerTable({
      tableId,
      label: tableLabel || tableConfig.selectedTable,
      tableName: tableConfig.selectedTable,
      columns: displayColumns.map((col) => ({
        columnName: col.field,
        columnLabel: columnLabels[col.field] || col.label || col.field,
        inputType: columnMeta[col.field]?.inputType || "text",
        visible: col.visible !== false,
        width: columnWidths[col.field] || col.width || 150,
        sortable: col.sortable !== false,
        filterable: col.filterable !== false,
      })),
      onFilterChange: setFilters,
      onGroupChange: setGrouping,
      onColumnVisibilityChange: setColumnVisibility,
    });

    return () => unregisterTable(tableId);
  }, [
    component.id,
    tableConfig.selectedTable,
    displayColumns,
    columnLabels,
    columnMeta,
    columnWidths,
    tableLabel,
  ]);

  // 🆕 초기 로드 시 localStorage에서 컬럼 순서 불러오기
  useEffect(() => {
    if (!tableConfig.selectedTable || !userId) return;

    const userKey = userId || "guest";
    const storageKey = `table_column_order_${tableConfig.selectedTable}_${userKey}`;
    const savedOrder = localStorage.getItem(storageKey);

    if (savedOrder) {
      try {
        const parsedOrder = JSON.parse(savedOrder);
        console.log("📂 localStorage에서 컬럼 순서 불러오기:", { storageKey, columnOrder: parsedOrder });
        setColumnOrder(parsedOrder);

        // 부모 컴포넌트에 초기 컬럼 순서 전달
        if (onSelectedRowsChange && parsedOrder.length > 0) {
          console.log("✅ 초기 컬럼 순서 전달:", parsedOrder);

          // 초기 데이터도 함께 전달 (컬럼 순서대로 재정렬)
          const initialData = data.map((row: any) => {
            const reordered: any = {};
            parsedOrder.forEach((colName: string) => {
              if (colName in row) {
                reordered[colName] = row[colName];
              }
            });
            // 나머지 컬럼 추가
            Object.keys(row).forEach((key) => {
              if (!(key in reordered)) {
                reordered[key] = row[key];
              }
            });
            return reordered;
          });

          // 전역 저장소에 데이터 저장
          if (tableConfig.selectedTable) {
            // 컬럼 라벨 매핑 생성
            const labels: Record<string, string> = {};
            visibleColumns.forEach((col) => {
              labels[col.columnName] = columnLabels[col.columnName] || col.columnName;
            });

            tableDisplayStore.setTableData(
              tableConfig.selectedTable,
              initialData,
              parsedOrder.filter((col) => col !== "__checkbox__"),
              sortColumn,
              sortDirection,
              {
                filterConditions: Object.keys(searchValues).length > 0 ? searchValues : undefined,
                searchTerm: searchTerm || undefined,
                visibleColumns: visibleColumns.map((col) => col.columnName),
                columnLabels: labels,
                currentPage: currentPage,
                pageSize: localPageSize,
                totalItems: totalItems,
              },
            );
          }

          onSelectedRowsChange([], [], sortColumn, sortDirection, parsedOrder, initialData);
        }
      } catch (error) {
        console.error("❌ 컬럼 순서 파싱 실패:", error);
      }
    }
  }, [tableConfig.selectedTable, userId, data.length]); // data.length 추가 (데이터 로드 후 실행)

  const { optimizedConvertCode } = useEntityJoinOptimization(columnMeta, {
    enableBatchLoading: true,
    preloadCommonCodes: true,
    maxBatchSize: 5,
  });

  // ========================================
  // 컬럼 라벨 가져오기
  // ========================================

  const fetchColumnLabels = useCallback(async () => {
    if (!tableConfig.selectedTable) return;

    try {
      // 🔥 FIX: 캐시 키에 회사 코드 포함 (멀티테넌시 지원)
      const currentUser = JSON.parse(localStorage.getItem("currentUser") || "{}");
      const companyCode = currentUser.companyCode || "UNKNOWN";
      const cacheKey = `columns_${tableConfig.selectedTable}_${companyCode}`;
      const cached = tableColumnCache.get(cacheKey);
      if (cached && Date.now() - cached.timestamp < TABLE_CACHE_TTL) {
        const labels: Record<string, string> = {};
        const meta: Record<string, { webType?: string; codeCategory?: string; inputType?: string }> = {};

        // 캐시된 inputTypes 맵 생성
        const inputTypeMap: Record<string, string> = {};
        if (cached.inputTypes) {
          cached.inputTypes.forEach((col: any) => {
            inputTypeMap[col.columnName] = col.inputType;
          });
        }

        cached.columns.forEach((col: any) => {
          labels[col.columnName] = col.displayName || col.comment || col.columnName;
          meta[col.columnName] = {
            webType: col.webType,
            codeCategory: col.codeCategory,
            inputType: inputTypeMap[col.columnName], // 캐시된 inputType 사용!
          };
        });

        setColumnLabels(labels);
        setColumnMeta(meta);
        return;
      }

      const columns = await tableTypeApi.getColumns(tableConfig.selectedTable);

      // 컬럼 입력 타입 정보 가져오기
      const inputTypes = await tableTypeApi.getColumnInputTypes(tableConfig.selectedTable);
      const inputTypeMap: Record<string, string> = {};
      inputTypes.forEach((col: any) => {
        inputTypeMap[col.columnName] = col.inputType;
      });

      tableColumnCache.set(cacheKey, {
        columns,
        inputTypes,
        timestamp: Date.now(),
      });

      const labels: Record<string, string> = {};
      const meta: Record<string, { webType?: string; codeCategory?: string; inputType?: string }> = {};

      columns.forEach((col: any) => {
        labels[col.columnName] = col.displayName || col.comment || col.columnName;
        meta[col.columnName] = {
          webType: col.webType,
          codeCategory: col.codeCategory,
          inputType: inputTypeMap[col.columnName],
        };
      });

      setColumnLabels(labels);
      setColumnMeta(meta);
    } catch (error) {
      console.error("컬럼 라벨 가져오기 실패:", error);
    }
  }, [tableConfig.selectedTable]);

  // ========================================
  // 테이블 라벨 가져오기
  // ========================================

  const fetchTableLabel = useCallback(async () => {
    if (!tableConfig.selectedTable) return;

    try {
      const cacheKey = `table_info_${tableConfig.selectedTable}`;
      const cached = tableInfoCache.get(cacheKey);
      if (cached && Date.now() - cached.timestamp < TABLE_CACHE_TTL) {
        const tables = cached.tables || [];
        const tableInfo = tables.find((t: any) => t.tableName === tableConfig.selectedTable);
        const label =
          tableInfo?.displayName || (tableInfo as any)?.comment || tableInfo?.description || tableConfig.selectedTable;
        setTableLabel(label);
        return;
      }

      const tables = await tableTypeApi.getTables();

      tableInfoCache.set(cacheKey, {
        tables,
        timestamp: Date.now(),
      });

      const tableInfo = tables.find((t: any) => t.tableName === tableConfig.selectedTable);
      const label =
        tableInfo?.displayName || (tableInfo as any)?.comment || tableInfo?.description || tableConfig.selectedTable;
      setTableLabel(label);
    } catch (error) {
      console.error("테이블 라벨 가져오기 실패:", error);
    }
  }, [tableConfig.selectedTable]);

  // ========================================
  // 카테고리 값 매핑 로드
  // ========================================

  // 카테고리 컬럼 목록 추출 (useMemo로 최적화)
  const categoryColumns = useMemo(() => {
    const cols = Object.entries(columnMeta)
      .filter(([_, meta]) => meta.inputType === "category")
      .map(([columnName, _]) => columnName);
    
    console.log("🔍 [TableList] 카테고리 컬럼 추출:", {
      columnMeta,
      categoryColumns: cols,
      columnMetaKeys: Object.keys(columnMeta),
    });
    
    return cols;
  }, [columnMeta]);

  // 카테고리 매핑 로드 (columnMeta 변경 시 즉시 실행)
  useEffect(() => {
    const loadCategoryMappings = async () => {
      console.log("🔄 [TableList] loadCategoryMappings 트리거:", {
        hasTable: !!tableConfig.selectedTable,
        table: tableConfig.selectedTable,
        categoryColumnsLength: categoryColumns.length,
        categoryColumns,
        columnMetaKeys: Object.keys(columnMeta),
      });

      if (!tableConfig.selectedTable) {
        console.log("⏭️ [TableList] 테이블 선택 안됨, 카테고리 매핑 로드 스킵");
        return;
      }

      if (categoryColumns.length === 0) {
        console.log("⏭️ [TableList] 카테고리 컬럼 없음, 카테고리 매핑 로드 스킵");
        setCategoryMappings({});
        return;
      }
      
      console.log("🚀 [TableList] 카테고리 매핑 로드 시작:", {
        table: tableConfig.selectedTable,
        categoryColumns,
        columnMetaKeys: Object.keys(columnMeta),
      });

      try {
        const mappings: Record<string, Record<string, { label: string; color?: string }>> = {};

        for (const columnName of categoryColumns) {
          try {
            console.log(`📡 [TableList] API 호출 시작 [${columnName}]:`, {
              url: `/table-categories/${tableConfig.selectedTable}/${columnName}/values`,
            });

            const apiClient = (await import("@/lib/api/client")).apiClient;
            const response = await apiClient.get(`/table-categories/${tableConfig.selectedTable}/${columnName}/values`);

            console.log(`📡 [TableList] API 응답 [${columnName}]:`, {
              success: response.data.success,
              dataLength: response.data.data?.length,
              rawData: response.data,
              items: response.data.data,
            });

            if (response.data.success && response.data.data && Array.isArray(response.data.data)) {
              const mapping: Record<string, { label: string; color?: string }> = {};
              
              response.data.data.forEach((item: any) => {
                // valueCode를 문자열로 변환하여 키로 사용
                const key = String(item.valueCode);
                mapping[key] = {
                  label: item.valueLabel,
                  color: item.color,
                };
                console.log(`  🔑 [${columnName}] "${key}" => "${item.valueLabel}" (색상: ${item.color})`);
              });
              
              if (Object.keys(mapping).length > 0) {
                mappings[columnName] = mapping;
                console.log(`✅ [TableList] 카테고리 매핑 로드 완료 [${columnName}]:`, {
                  columnName,
                  mappingCount: Object.keys(mapping).length,
                  mappingKeys: Object.keys(mapping),
                  mapping,
                });
              } else {
                console.warn(`⚠️ [TableList] 매핑 데이터가 비어있음 [${columnName}]`);
              }
            } else {
              console.warn(`⚠️ [TableList] 카테고리 값 없음 [${columnName}]:`, {
                success: response.data.success,
                hasData: !!response.data.data,
                isArray: Array.isArray(response.data.data),
                response: response.data,
              });
            }
          } catch (error: any) {
            console.error(`❌ [TableList] 카테고리 값 로드 실패 [${columnName}]:`, {
              error: error.message,
              stack: error.stack,
              response: error.response?.data,
              status: error.response?.status,
            });
          }
        }

        console.log("📊 [TableList] 전체 카테고리 매핑 설정:", {
          mappingsCount: Object.keys(mappings).length,
          mappingsKeys: Object.keys(mappings),
          mappings,
        });
        
        if (Object.keys(mappings).length > 0) {
          setCategoryMappings(mappings);
          setCategoryMappingsKey((prev) => prev + 1);
          console.log("✅ [TableList] setCategoryMappings 호출 완료");
        } else {
          console.warn("⚠️ [TableList] 매핑이 비어있어 상태 업데이트 스킵");
        }
      } catch (error) {
        console.error("❌ [TableList] 카테고리 매핑 로드 실패:", error);
      }
    };

    loadCategoryMappings();
  }, [tableConfig.selectedTable, categoryColumns.length, JSON.stringify(categoryColumns)]); // 더 명확한 의존성

  // ========================================
  // 데이터 가져오기
  // ========================================

  const fetchTableDataInternal = useCallback(async () => {
    if (!tableConfig.selectedTable || isDesignMode) {
      setData([]);
      setTotalPages(0);
      setTotalItems(0);
      return;
    }

    // 테이블명 확인 로그 (개발 시에만)
    // console.log("🔍 fetchTableDataInternal - selectedTable:", tableConfig.selectedTable);
    // console.log("🔍 selectedTable 타입:", typeof tableConfig.selectedTable);
    // console.log("🔍 전체 tableConfig:", tableConfig);

    setLoading(true);
    setError(null);

    try {
      const page = tableConfig.pagination?.currentPage || currentPage;
      const pageSize = localPageSize;
      const sortBy = sortColumn || undefined;
      const sortOrder = sortDirection;
      const search = searchTerm || undefined;
      const filters = Object.keys(searchValues).length > 0 ? searchValues : undefined;

      const entityJoinColumns = (tableConfig.columns || [])
        .filter((col) => col.additionalJoinInfo)
        .map((col) => ({
          sourceTable: col.additionalJoinInfo!.sourceTable,
          sourceColumn: col.additionalJoinInfo!.sourceColumn,
          joinAlias: col.additionalJoinInfo!.joinAlias,
          referenceTable: col.additionalJoinInfo!.referenceTable,
        }));

      // 🎯 항상 entityJoinApi 사용 (writer 컬럼 자동 조인 지원)
      const response = await entityJoinApi.getTableDataWithJoins(tableConfig.selectedTable, {
        page,
        size: pageSize,
        sortBy,
        sortOrder,
        search: filters,
        enableEntityJoin: true,
        additionalJoinColumns: entityJoinColumns.length > 0 ? entityJoinColumns : undefined,
      });

      setData(response.data || []);
      setTotalPages(response.totalPages || 0);
      setTotalItems(response.total || 0);
      setError(null);
      
      // 🎯 Store에 필터 조건 저장 (엑셀 다운로드용)
      const labels: Record<string, string> = {};
      visibleColumns.forEach((col) => {
        labels[col.columnName] = columnLabels[col.columnName] || col.columnName;
      });
      
      tableDisplayStore.setTableData(
        tableConfig.selectedTable,
        response.data || [],
        visibleColumns.map((col) => col.columnName),
        sortBy,
        sortOrder,
        {
          filterConditions: filters,
          searchTerm: search,
          visibleColumns: visibleColumns.map((col) => col.columnName),
          columnLabels: labels,
          currentPage: page,
          pageSize: pageSize,
          totalItems: response.total || 0,
        }
      );
    } catch (err: any) {
      console.error("데이터 가져오기 실패:", err);
      setData([]);
      setTotalPages(0);
      setTotalItems(0);
      setError(err.message || "데이터를 불러오지 못했습니다.");
    } finally {
      setLoading(false);
    }
  }, [
    tableConfig.selectedTable,
    tableConfig.pagination?.currentPage,
    tableConfig.columns,
    currentPage,
    localPageSize,
    sortColumn,
    sortDirection,
    searchTerm,
    searchValues,
    isDesignMode,
  ]);

  const fetchTableDataDebounced = useCallback(
    (...args: Parameters<typeof fetchTableDataInternal>) => {
      const key = `fetchData_${tableConfig.selectedTable}_${currentPage}_${sortColumn}_${sortDirection}`;
      return debouncedApiCall(key, fetchTableDataInternal, 300)(...args);
    },
    [fetchTableDataInternal, tableConfig.selectedTable, currentPage, sortColumn, sortDirection],
  );

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

  const handlePageChange = (newPage: number) => {
    if (newPage < 1 || newPage > totalPages) return;
    setCurrentPage(newPage);
    if (tableConfig.pagination) {
      tableConfig.pagination.currentPage = newPage;
    }
    if (onConfigChange) {
      onConfigChange({ ...tableConfig, pagination: { ...tableConfig.pagination, currentPage: newPage } });
    }
  };

  const handleSort = (column: string) => {
    console.log("🔄 정렬 클릭:", { column, currentSortColumn: sortColumn, currentSortDirection: sortDirection });

    let newSortColumn = column;
    let newSortDirection: "asc" | "desc" = "asc";

    if (sortColumn === column) {
      newSortDirection = sortDirection === "asc" ? "desc" : "asc";
      setSortDirection(newSortDirection);
    } else {
      setSortColumn(column);
      setSortDirection("asc");
      newSortColumn = column;
      newSortDirection = "asc";
    }

    console.log("📊 새로운 정렬 정보:", { newSortColumn, newSortDirection });
    console.log("🔍 onSelectedRowsChange 존재 여부:", !!onSelectedRowsChange);

    // 정렬 변경 시 선택 정보와 함께 정렬 정보도 전달
    if (onSelectedRowsChange) {
      const selectedRowsData = data.filter((row, index) => selectedRows.has(getRowKey(row, index)));

      // 1단계: 데이터를 정렬
      const sortedData = [...data].sort((a, b) => {
        const aVal = a[newSortColumn];
        const bVal = b[newSortColumn];

        // null/undefined 처리
        if (aVal == null && bVal == null) return 0;
        if (aVal == null) return 1;
        if (bVal == null) return -1;

        // 숫자 비교 (문자열이어도 숫자로 변환 가능하면 숫자로 비교)
        const aNum = Number(aVal);
        const bNum = Number(bVal);

        // 둘 다 유효한 숫자이고, 원본 값이 빈 문자열이 아닌 경우
        if (!isNaN(aNum) && !isNaN(bNum) && aVal !== "" && bVal !== "") {
          return newSortDirection === "desc" ? bNum - aNum : aNum - bNum;
        }

        // 문자열 비교 (대소문자 구분 없이, 숫자 포함 문자열도 자연스럽게 정렬)
        const aStr = String(aVal).toLowerCase();
        const bStr = String(bVal).toLowerCase();

        // 자연스러운 정렬 (숫자 포함 문자열)
        const comparison = aStr.localeCompare(bStr, undefined, { numeric: true, sensitivity: "base" });
        return newSortDirection === "desc" ? -comparison : comparison;
      });

      // 2단계: 정렬된 데이터를 컬럼 순서대로 재정렬
      const reorderedData = sortedData.map((row: any) => {
        const reordered: any = {};
        visibleColumns.forEach((col) => {
          if (col.columnName in row) {
            reordered[col.columnName] = row[col.columnName];
          }
        });
        // 나머지 컬럼 추가
        Object.keys(row).forEach((key) => {
          if (!(key in reordered)) {
            reordered[key] = row[key];
          }
        });
        return reordered;
      });

      console.log("✅ 정렬 정보 전달:", {
        selectedRowsCount: selectedRows.size,
        selectedRowsDataCount: selectedRowsData.length,
        sortBy: newSortColumn,
        sortOrder: newSortDirection,
        columnOrder: columnOrder.length > 0 ? columnOrder : undefined,
        tableDisplayDataCount: reorderedData.length,
        firstRowAfterSort: reorderedData[0]?.[newSortColumn],
        lastRowAfterSort: reorderedData[reorderedData.length - 1]?.[newSortColumn],
      });
      onSelectedRowsChange(
        Array.from(selectedRows),
        selectedRowsData,
        newSortColumn,
        newSortDirection,
        columnOrder.length > 0 ? columnOrder : undefined,
        reorderedData,
      );

      // 전역 저장소에 정렬된 데이터 저장
      if (tableConfig.selectedTable) {
        const cleanColumnOrder = (
          columnOrder.length > 0 ? columnOrder : visibleColumns.map((c) => c.columnName)
        ).filter((col) => col !== "__checkbox__");
        
        // 컬럼 라벨 정보도 함께 저장
        const labels: Record<string, string> = {};
        visibleColumns.forEach((col) => {
          labels[col.columnName] = columnLabels[col.columnName] || col.columnName;
        });
        
        tableDisplayStore.setTableData(
          tableConfig.selectedTable,
          reorderedData,
          cleanColumnOrder,
          newSortColumn,
          newSortDirection,
          {
            filterConditions: Object.keys(searchValues).length > 0 ? searchValues : undefined,
            searchTerm: searchTerm || undefined,
            visibleColumns: visibleColumns.map((col) => col.columnName),
            columnLabels: labels,
            currentPage: currentPage,
            pageSize: localPageSize,
            totalItems: totalItems,
          },
        );
      }
    } else {
      console.warn("⚠️ onSelectedRowsChange 콜백이 없습니다!");
    }
  };

  const handleSearchValueChange = (columnName: string, value: any) => {
    setSearchValues((prev) => ({ ...prev, [columnName]: value }));
  };

  const handleAdvancedSearch = () => {
    setCurrentPage(1);
    fetchTableDataDebounced();
  };

  const handleClearAdvancedFilters = useCallback(() => {
    console.log("🔄 필터 초기화 시작", { 이전searchValues: searchValues });

    // 상태를 초기화하고 useEffect로 데이터 새로고침
    setSearchValues({});
    setCurrentPage(1);

    // 강제로 데이터 새로고침 트리거
    setRefreshTrigger((prev) => prev + 1);
  }, [searchValues]);

  const handleRefresh = () => {
    fetchTableDataDebounced();
  };

  const getRowKey = (row: any, index: number) => {
    return row.id || row.uuid || `row-${index}`;
  };

  const handleRowSelection = (rowKey: string, checked: boolean) => {
    const newSelectedRows = new Set(selectedRows);
    if (checked) {
      newSelectedRows.add(rowKey);
    } else {
      newSelectedRows.delete(rowKey);
    }
    setSelectedRows(newSelectedRows);

    const selectedRowsData = data.filter((row, index) => newSelectedRows.has(getRowKey(row, index)));
    if (onSelectedRowsChange) {
      onSelectedRowsChange(Array.from(newSelectedRows), selectedRowsData, sortColumn || undefined, sortDirection);
    }
    if (onFormDataChange) {
      onFormDataChange({
        selectedRows: Array.from(newSelectedRows),
        selectedRowsData,
      });
    }

    const allRowsSelected = data.every((row, index) => newSelectedRows.has(getRowKey(row, index)));
    setIsAllSelected(allRowsSelected && data.length > 0);
  };

  const handleSelectAll = (checked: boolean) => {
    if (checked) {
      const allKeys = data.map((row, index) => getRowKey(row, index));
      const newSelectedRows = new Set(allKeys);
      setSelectedRows(newSelectedRows);
      setIsAllSelected(true);

      if (onSelectedRowsChange) {
        onSelectedRowsChange(Array.from(newSelectedRows), data, sortColumn || undefined, sortDirection);
      }
      if (onFormDataChange) {
        onFormDataChange({
          selectedRows: Array.from(newSelectedRows),
          selectedRowsData: data,
        });
      }
    } else {
      setSelectedRows(new Set());
      setIsAllSelected(false);

      if (onSelectedRowsChange) {
        onSelectedRowsChange([], [], sortColumn || undefined, sortDirection);
      }
      if (onFormDataChange) {
        onFormDataChange({ selectedRows: [], selectedRowsData: [] });
      }
    }
  };

  const handleRowClick = (row: any, index: number, e: React.MouseEvent) => {
    // 체크박스 클릭은 무시 (이미 handleRowSelection에서 처리됨)
    const target = e.target as HTMLElement;
    if (target.closest('input[type="checkbox"]')) {
      return;
    }

    // 행 선택/해제 토글
    const rowKey = getRowKey(row, index);
    const isCurrentlySelected = selectedRows.has(rowKey);

    handleRowSelection(rowKey, !isCurrentlySelected);

    console.log("행 클릭:", { row, index, isSelected: !isCurrentlySelected });
  };

  // 컬럼 드래그앤드롭 기능 제거됨 (테이블 옵션 모달에서 컬럼 순서 변경 가능)

  const handleClick = (e: React.MouseEvent) => {
    e.stopPropagation();
    onClick?.();
  };

  // ========================================
  // 컬럼 관련
  // ========================================

  const visibleColumns = useMemo(() => {
    let cols = (tableConfig.columns || []).filter((col) => col.visible !== false);

    if (tableConfig.checkbox?.enabled) {
      const checkboxCol: ColumnConfig = {
        columnName: "__checkbox__",
        displayName: "",
        visible: true,
        sortable: false,
        searchable: false,
        width: 50,
        align: "center",
        order: -1,
      };

      if (tableConfig.checkbox.position === "right") {
        cols = [...cols, checkboxCol];
      } else {
        cols = [checkboxCol, ...cols];
      }
    }

    // columnOrder 상태가 있으면 그 순서대로 정렬
    if (columnOrder.length > 0) {
      const orderedCols = columnOrder
        .map((colName) => cols.find((c) => c.columnName === colName))
        .filter(Boolean) as ColumnConfig[];

      // columnOrder에 없는 새로운 컬럼들 추가
      const remainingCols = cols.filter((c) => !columnOrder.includes(c.columnName));

      console.log("🔄 columnOrder 기반 정렬:", {
        columnOrder,
        orderedColsCount: orderedCols.length,
        remainingColsCount: remainingCols.length,
      });

      return [...orderedCols, ...remainingCols];
    }

    return cols.sort((a, b) => (a.order || 0) - (b.order || 0));
  }, [tableConfig.columns, tableConfig.checkbox, columnOrder]);

  // 🆕 visibleColumns가 변경될 때마다 현재 컬럼 순서를 부모에게 전달
  const lastColumnOrderRef = useRef<string>("");

  useEffect(() => {
    console.log("🔍 [컬럼 순서 전달 useEffect] 실행됨:", {
      hasCallback: !!onSelectedRowsChange,
      visibleColumnsLength: visibleColumns.length,
      visibleColumnsNames: visibleColumns.map((c) => c.columnName),
    });

    if (!onSelectedRowsChange) {
      console.warn("⚠️ onSelectedRowsChange 콜백이 없습니다!");
      return;
    }

    if (visibleColumns.length === 0) {
      console.warn("⚠️ visibleColumns가 비어있습니다!");
      return;
    }

    const currentColumnOrder = visibleColumns.map((col) => col.columnName).filter((name) => name !== "__checkbox__"); // 체크박스 컬럼 제외

    console.log("🔍 [컬럼 순서] 체크박스 제외 후:", currentColumnOrder);

    // 컬럼 순서가 실제로 변경되었을 때만 전달 (무한 루프 방지)
    const columnOrderString = currentColumnOrder.join(",");
    console.log("🔍 [컬럼 순서] 비교:", {
      current: columnOrderString,
      last: lastColumnOrderRef.current,
      isDifferent: columnOrderString !== lastColumnOrderRef.current,
    });

    if (columnOrderString === lastColumnOrderRef.current) {
      console.log("⏭️ 컬럼 순서 변경 없음, 전달 스킵");
      return;
    }

    lastColumnOrderRef.current = columnOrderString;
    console.log("📊 현재 화면 컬럼 순서 전달:", currentColumnOrder);

    // 선택된 행 데이터 가져오기
    const selectedRowsData = data.filter((row, index) => selectedRows.has(getRowKey(row, index)));

    // 화면에 표시된 데이터를 컬럼 순서대로 재정렬
    const reorderedData = data.map((row: any) => {
      const reordered: any = {};
      visibleColumns.forEach((col) => {
        if (col.columnName in row) {
          reordered[col.columnName] = row[col.columnName];
        }
      });
      // 나머지 컬럼 추가
      Object.keys(row).forEach((key) => {
        if (!(key in reordered)) {
          reordered[key] = row[key];
        }
      });
      return reordered;
    });

    onSelectedRowsChange(
      Array.from(selectedRows),
      selectedRowsData,
      sortColumn,
      sortDirection,
      currentColumnOrder,
      reorderedData,
    );
  }, [visibleColumns.length, visibleColumns.map((c) => c.columnName).join(",")]); // 의존성 단순화

  const getColumnWidth = (column: ColumnConfig) => {
    if (column.columnName === "__checkbox__") return 50;
    if (column.width) return column.width;

    switch (column.format) {
      case "date":
        return 120;
      case "number":
      case "currency":
        return 100;
      case "boolean":
        return 80;
      default:
        return 150;
    }
  };

  const renderCheckboxHeader = () => {
    if (!tableConfig.checkbox?.selectAll) return null;

    return <Checkbox checked={isAllSelected} onCheckedChange={handleSelectAll} aria-label="전체 선택" />;
  };

  const renderCheckboxCell = (row: any, index: number) => {
    const rowKey = getRowKey(row, index);
    const isChecked = selectedRows.has(rowKey);

    return (
      <Checkbox
        checked={isChecked}
        onCheckedChange={(checked) => handleRowSelection(rowKey, checked as boolean)}
        aria-label={`행 ${index + 1} 선택`}
      />
    );
  };

  const formatCellValue = useCallback(
    (value: any, column: ColumnConfig, rowData?: Record<string, any>) => {
      if (value === null || value === undefined) return "-";

      // 🎯 writer 컬럼 자동 변환: user_id -> user_name
      if (column.columnName === "writer" && rowData && rowData.writer_name) {
        return rowData.writer_name;
      }

      // 🎯 엔티티 컬럼 표시 설정이 있는 경우
      if (column.entityDisplayConfig && rowData) {
        // displayColumns 또는 selectedColumns 둘 다 체크
        const displayColumns = column.entityDisplayConfig.displayColumns || column.entityDisplayConfig.selectedColumns;
        const separator = column.entityDisplayConfig.separator;

        if (displayColumns && displayColumns.length > 0) {
          // 선택된 컬럼들의 값을 구분자로 조합
          const values = displayColumns
            .map((colName) => {
              const cellValue = rowData[colName];
              if (cellValue === null || cellValue === undefined) return "";
              return String(cellValue);
            })
            .filter((v) => v !== ""); // 빈 값 제외

          return values.join(separator || " - ");
        }
      }

      const meta = columnMeta[column.columnName];

      // inputType 기반 포맷팅 (columnMeta에서 가져온 inputType 우선)
      const inputType = meta?.inputType || column.inputType;

      // 🖼️ 이미지 타입: 작은 썸네일 표시
      if (inputType === "image" && value && typeof value === "string") {
        const imageUrl = getFullImageUrl(value);
        return (
          <img
            src={imageUrl}
            alt="이미지"
            className="h-10 w-10 rounded object-cover"
            onError={(e) => {
              e.currentTarget.src =
                "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='40' height='40'%3E%3Crect width='40' height='40' fill='%23f3f4f6'/%3E%3C/svg%3E";
            }}
          />
        );
      }

      // 카테고리 타입: 배지로 표시
      if (inputType === "category") {
        if (!value) return "";

        const mapping = categoryMappings[column.columnName];
        const categoryData = mapping?.[String(value)];

        // console.log(`🎨 [카테고리 배지] ${column.columnName}:`, {
        //   value,
        //   stringValue: String(value),
        //   mapping,
        //   categoryData,
        //   hasMapping: !!mapping,
        //   hasCategoryData: !!categoryData,
        //   allCategoryMappings: categoryMappings, // 전체 매핑 확인
        //   categoryMappingsKeys: Object.keys(categoryMappings),
        // });

        // 매핑 데이터가 있으면 라벨과 색상 사용, 없으면 코드값과 기본색상
        const displayLabel = categoryData?.label || String(value);
        const displayColor = categoryData?.color || "#64748b"; // 기본 slate 색상

        const { Badge } = require("@/components/ui/badge");
        return (
          <Badge
            style={{
              backgroundColor: displayColor,
              borderColor: displayColor,
            }}
            className="text-white"
          >
            {displayLabel}
          </Badge>
        );
      }

      // 코드 타입: 코드 값 → 코드명 변환
      if (inputType === "code" && meta?.codeCategory && value) {
        try {
          // optimizedConvertCode(categoryCode, codeValue) 순서 주의!
          const convertedValue = optimizedConvertCode(meta.codeCategory, value);
          // 변환에 성공했으면 변환된 코드명 반환
          if (convertedValue && convertedValue !== value) {
            return convertedValue;
          }
        } catch (error) {
          console.error(`코드 변환 실패: ${column.columnName}, 카테고리: ${meta.codeCategory}, 값: ${value}`, error);
        }
        // 변환 실패 시 원본 코드 값 반환
        return String(value);
      }

      // 날짜 타입 포맷팅 (yyyy-mm-dd)
      if (inputType === "date" || inputType === "datetime") {
        if (value) {
          try {
            const date = new Date(value);
            const year = date.getFullYear();
            const month = String(date.getMonth() + 1).padStart(2, '0');
            const day = String(date.getDate()).padStart(2, '0');
            return `${year}-${month}-${day}`;
          } catch {
            return String(value);
          }
        }
        return "-";
      }

      // 숫자 타입 포맷팅
      if (inputType === "number" || inputType === "decimal") {
        if (value !== null && value !== undefined && value !== "") {
          const numValue = typeof value === "string" ? parseFloat(value) : value;
          if (!isNaN(numValue)) {
            return numValue.toLocaleString("ko-KR");
          }
        }
        return String(value);
      }

      switch (column.format) {
        case "number":
          if (value !== null && value !== undefined && value !== "") {
            const numValue = typeof value === "string" ? parseFloat(value) : value;
            if (!isNaN(numValue)) {
              return numValue.toLocaleString("ko-KR");
            }
          }
          return String(value);
        case "date":
          if (value) {
            try {
              const date = new Date(value);
              const year = date.getFullYear();
              const month = String(date.getMonth() + 1).padStart(2, '0');
              const day = String(date.getDate()).padStart(2, '0');
              return `${year}-${month}-${day}`;
            } catch {
              return value;
            }
          }
          return "-";
        case "number":
          return typeof value === "number" ? value.toLocaleString() : value;
        case "currency":
          return typeof value === "number" ? `₩${value.toLocaleString()}` : value;
        case "boolean":
          return value ? "예" : "아니오";
        default:
          return String(value);
      }
    },
    [columnMeta, optimizedConvertCode, categoryMappings],
  );

  // ========================================
  // useEffect 훅
  // ========================================

  // 필터 설정 localStorage 키 생성
  const filterSettingKey = useMemo(() => {
    if (!tableConfig.selectedTable) return null;
    return `tableList_filterSettings_${tableConfig.selectedTable}`;
  }, [tableConfig.selectedTable]);

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

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

    try {
      const saved = localStorage.getItem(filterSettingKey);
      if (saved) {
        const savedFilters = JSON.parse(saved);
        setVisibleFilterColumns(new Set(savedFilters));
      } else {
        // 초기값: 빈 Set (아무것도 선택 안 함)
        setVisibleFilterColumns(new Set());
      }
    } catch (error) {
      console.error("필터 설정 불러오기 실패:", error);
      setVisibleFilterColumns(new Set());
    }
  }, [filterSettingKey, visibleColumns]);

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

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

      // 검색 값 초기화
      setSearchValues({});
    } catch (error) {
      console.error("필터 설정 저장 실패:", error);
      toast.error("설정 저장에 실패했습니다");
    }
  }, [filterSettingKey, visibleFilterColumns]);

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

  // 전체 선택/해제
  const toggleAllFilters = useCallback(() => {
    const filterableColumns = visibleColumns.filter((col) => col.columnName !== "__checkbox__");
    const columnNames = filterableColumns.map((col) => col.columnName);

    if (visibleFilterColumns.size === columnNames.length) {
      // 전체 해제
      setVisibleFilterColumns(new Set());
    } else {
      // 전체 선택
      setVisibleFilterColumns(new Set(columnNames));
    }
  }, [visibleFilterColumns, visibleColumns]);

  // 표시할 필터 목록 (선택된 컬럼만)
  const activeFilters = useMemo(() => {
    return visibleColumns
      .filter((col) => col.columnName !== "__checkbox__" && visibleFilterColumns.has(col.columnName))
      .map((col) => ({
        columnName: col.columnName,
        label: columnLabels[col.columnName] || col.displayName || col.columnName,
        type: col.format || "text",
      }));
  }, [visibleColumns, visibleFilterColumns, columnLabels]);

  // 그룹 설정 자동 저장 (localStorage)
  useEffect(() => {
    if (!groupSettingKey) return;

    try {
      localStorage.setItem(groupSettingKey, JSON.stringify(groupByColumns));
    } catch (error) {
      console.error("그룹 설정 저장 실패:", error);
    }
  }, [groupSettingKey, groupByColumns]);

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

  // 사용자 옵션 저장 핸들러
  const handleTableOptionsSave = useCallback(
    (config: {
      columns: Array<{ columnName: string; label: string; visible: boolean; width?: number; frozen?: boolean }>;
      showGridLines: boolean;
      viewMode: "table" | "card" | "grouped-card";
    }) => {
      // 컬럼 순서 업데이트
      const newColumnOrder = config.columns.map((col) => col.columnName);
      setColumnOrder(newColumnOrder);

      // 컬럼 너비 업데이트
      const newWidths: Record<string, number> = {};
      config.columns.forEach((col) => {
        if (col.width) {
          newWidths[col.columnName] = col.width;
        }
      });
      setColumnWidths(newWidths);

      // 틀고정 컬럼 업데이트
      const newFrozenColumns = config.columns.filter((col) => col.frozen).map((col) => col.columnName);
      setFrozenColumns(newFrozenColumns);

      // 그리드선 표시 업데이트
      setShowGridLines(config.showGridLines);

      // 보기 모드 업데이트
      setViewMode(config.viewMode);

      // 컬럼 표시/숨기기 업데이트
      const newDisplayColumns = displayColumns.map((col) => {
        const configCol = config.columns.find((c) => c.columnName === col.columnName);
        if (configCol) {
          return { ...col, visible: configCol.visible };
        }
        return col;
      });
      setDisplayColumns(newDisplayColumns);

      toast.success("테이블 옵션이 저장되었습니다");
    },
    [displayColumns],
  );

  // 그룹 펼치기/접기 토글
  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]);

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

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

    data.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,
      };
    });
  }, [data, groupByColumns, columnLabels]);

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

    try {
      const saved = localStorage.getItem(groupSettingKey);
      if (saved) {
        const savedGroups = JSON.parse(saved);
        setGroupByColumns(savedGroups);
      }
    } catch (error) {
      console.error("그룹 설정 불러오기 실패:", error);
    }
  }, [groupSettingKey, visibleColumns]);

  useEffect(() => {
    fetchColumnLabels();
    fetchTableLabel();
  }, [tableConfig.selectedTable, fetchColumnLabels, fetchTableLabel]);

  useEffect(() => {
    if (!isDesignMode && tableConfig.selectedTable) {
      fetchTableDataDebounced();
    }
  }, [
    tableConfig.selectedTable,
    currentPage,
    localPageSize,
    sortColumn,
    sortDirection,
    searchTerm,
    searchValues, // 필터 값 변경 시에도 데이터 새로고침
    refreshKey,
    refreshTrigger, // 강제 새로고침 트리거
    isDesignMode,
    fetchTableDataDebounced,
  ]);

  useEffect(() => {
    if (tableConfig.refreshInterval && !isDesignMode) {
      const interval = setInterval(() => {
        fetchTableDataDebounced();
      }, tableConfig.refreshInterval * 1000);

      return () => clearInterval(interval);
    }
  }, [tableConfig.refreshInterval, isDesignMode]);

  // 초기 컬럼 너비 측정 (한 번만)
  useEffect(() => {
    if (!hasInitializedWidths.current && visibleColumns.length > 0) {
      // 약간의 지연을 두고 DOM이 완전히 렌더링된 후 측정
      const timer = setTimeout(() => {
        const newWidths: Record<string, number> = {};
        let hasAnyWidth = false;

        visibleColumns.forEach((column) => {
          // 체크박스 컬럼은 제외 (고정 48px)
          if (column.columnName === "__checkbox__") return;

          const thElement = columnRefs.current[column.columnName];
          if (thElement) {
            const measuredWidth = thElement.offsetWidth;
            if (measuredWidth > 0) {
              newWidths[column.columnName] = measuredWidth;
              hasAnyWidth = true;
            }
          }
        });

        if (hasAnyWidth) {
          setColumnWidths(newWidths);
          hasInitializedWidths.current = true;
        }
      }, 100);

      return () => clearTimeout(timer);
    }
  }, [visibleColumns]);

  // ========================================
  // 페이지네이션 JSX
  // ========================================

  const paginationJSX = useMemo(() => {
    if (!tableConfig.pagination?.enabled || isDesignMode) return null;

    return (
      <div className="border-border bg-background relative flex h-14 w-full flex-shrink-0 items-center justify-center border-t-2 px-4 sm:h-[60px] sm:px-6">
        {/* 중앙 페이지네이션 컨트롤 */}
        <div className="flex items-center gap-2 sm:gap-4">
          <Button
            variant="outline"
            size="sm"
            onClick={() => handlePageChange(1)}
            disabled={currentPage === 1 || loading}
            className="h-8 w-8 p-0 sm:h-9 sm:w-auto sm:px-3"
          >
            <ChevronsLeft className="h-3 w-3 sm:h-4 sm:w-4" />
          </Button>
          <Button
            variant="outline"
            size="sm"
            onClick={() => handlePageChange(currentPage - 1)}
            disabled={currentPage === 1 || loading}
            className="h-8 w-8 p-0 sm:h-9 sm:w-auto sm:px-3"
          >
            <ChevronLeft className="h-3 w-3 sm:h-4 sm:w-4" />
          </Button>

          <span className="text-foreground min-w-[60px] text-center text-xs font-medium sm:min-w-[80px] sm:text-sm">
            {currentPage} / {totalPages || 1}
          </span>

          <Button
            variant="outline"
            size="sm"
            onClick={() => handlePageChange(currentPage + 1)}
            disabled={currentPage >= totalPages || loading}
            className="h-8 w-8 p-0 sm:h-9 sm:w-auto sm:px-3"
          >
            <ChevronRight className="h-3 w-3 sm:h-4 sm:w-4" />
          </Button>
          <Button
            variant="outline"
            size="sm"
            onClick={() => handlePageChange(totalPages)}
            disabled={currentPage >= totalPages || loading}
            className="h-8 w-8 p-0 sm:h-9 sm:w-auto sm:px-3"
          >
            <ChevronsRight className="h-3 w-3 sm:h-4 sm:w-4" />
          </Button>
        </div>

        {/* 우측 새로고침 버튼 */}
        <Button
          variant="ghost"
          size="sm"
          onClick={handleRefresh}
          disabled={loading}
          className="absolute right-2 h-8 w-8 p-0 sm:right-6 sm:h-9 sm:w-auto sm:px-3"
        >
          <RefreshCw className={cn("h-3 w-3", loading && "animate-spin")} />
        </Button>
      </div>
    );
  }, [tableConfig.pagination, isDesignMode, currentPage, totalPages, totalItems, loading]);

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

  const domProps = {
    onClick: handleClick,
    onDragStart: isDesignMode ? onDragStart : undefined,
    onDragEnd: isDesignMode ? onDragEnd : undefined,
    draggable: isDesignMode,
    className: cn(className, isDesignMode && "cursor-move"),
    style: componentStyle,
  };

  // 카드 모드
  if (tableConfig.displayMode === "card" && !isDesignMode) {
    return (
      <div {...domProps}>
        <CardModeRenderer
          data={data}
          loading={loading}
          error={error}
          cardConfig={tableConfig.cardConfig}
          onRowClick={handleRowClick}
        />
        {paginationJSX}
      </div>
    );
  }

  // SingleTableWithSticky 모드
  if (tableConfig.stickyHeader && !isDesignMode) {
    return (
      <div {...domProps}>
        {tableConfig.filter?.enabled && (
          <div className="border-border border-b px-4 py-2 sm:px-6 sm:py-2">
            <div className="flex flex-col gap-2 sm:flex-row sm:items-start sm:gap-3">
              <div className="flex-1">
                <AdvancedSearchFilters
                  filters={activeFilters}
                  searchValues={searchValues}
                  onSearchValueChange={handleSearchValueChange}
                  onSearch={handleAdvancedSearch}
                  onClearFilters={handleClearAdvancedFilters}
                />
              </div>
              <div className="flex items-center gap-2">
                {/* 전체 개수 */}
                <div className="hidden sm:block text-sm text-muted-foreground whitespace-nowrap">
                  전체 <span className="font-semibold text-foreground">{totalItems.toLocaleString()}</span>개
                </div>
                
                <Button
                  variant="outline"
                  size="sm"
                  onClick={() => setIsTableOptionsOpen(true)}
                  className="w-full flex-shrink-0 sm:mt-1 sm:w-auto"
                >
                  <TableIcon className="mr-2 h-4 w-4" />
                  테이블 옵션
                </Button>
                <Button
                  variant="outline"
                  size="sm"
                  onClick={() => setIsFilterSettingOpen(true)}
                  className="w-full flex-shrink-0 sm:mt-1 sm:w-auto"
                >
                  <Settings className="mr-2 h-4 w-4" />
                  필터 설정
                </Button>
                <Popover>
                  <PopoverTrigger asChild>
                    <Button
                      variant="outline"
                      size="sm"
                      className="flex-shrink-0 w-full sm:w-auto sm:mt-1"
                    >
                      <Layers className="mr-2 h-4 w-4" />
                      그룹 설정
                      {groupByColumns.length > 0 && (
                        <span className="ml-2 rounded-full bg-primary px-2 py-0.5 text-[10px] font-semibold text-primary-foreground">
                          {groupByColumns.length}
                        </span>
                      )}
                    </Button>
                  </PopoverTrigger>
                  <PopoverContent className="w-80 p-0" align="end">
                    <div className="space-y-3 p-4">
                      <div className="space-y-1">
                        <h4 className="text-sm font-semibold">그룹 설정</h4>
                        <p className="text-xs text-muted-foreground">
                          데이터를 그룹화할 컬럼을 선택하세요
                        </p>
                      </div>

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

                      {/* 선택된 그룹 안내 */}
                      {groupByColumns.length > 0 && (
                        <div className="rounded bg-muted/30 p-2 text-xs text-muted-foreground">
                          <span className="font-semibold text-foreground">
                            {groupByColumns.map((col) => columnLabels[col] || col).join(" → ")}
                          </span>
                        </div>
                      )}

                      {/* 초기화 버튼 */}
                      {groupByColumns.length > 0 && (
                        <Button
                          variant="outline"
                          size="sm"
                          onClick={() => {
                            setGroupByColumns([]);
                            if (groupSettingKey) {
                              localStorage.removeItem(groupSettingKey);
                            }
                            toast.success("그룹 설정이 초기화되었습니다");
                          }}
                          className="w-full text-xs"
                        >
                          초기화
                        </Button>
                      )}
                    </div>
                  </PopoverContent>
                </Popover>
              </div>
            </div>
          </div>
        )}

        {/* 그룹 표시 배지 */}
        {groupByColumns.length > 0 && (
          <div className="border-border bg-muted/30 border-b px-4 py-1.5 sm:px-6">
            <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={clearGrouping}
                className="hover:bg-destructive/10 text-destructive ml-auto rounded p-1"
                title="그룹 해제"
              >
                <X className="h-4 w-4" />
              </button>
            </div>
          </div>
        )}

        <div style={{ marginTop: `${tableConfig.filter?.bottomSpacing ?? 8}px`, flex: 1, overflow: "hidden" }}>
          <SingleTableWithSticky
            data={data}
            columns={visibleColumns}
            loading={loading}
            error={error}
            sortColumn={sortColumn}
            sortDirection={sortDirection}
            onSort={handleSort}
            tableConfig={tableConfig}
            isDesignMode={isDesignMode}
            isAllSelected={isAllSelected}
            handleSelectAll={handleSelectAll}
            handleRowClick={handleRowClick}
            columnLabels={columnLabels}
            renderCheckboxHeader={renderCheckboxHeader}
            renderCheckboxCell={renderCheckboxCell}
            formatCellValue={(value: any, format?: string, columnName?: string, rowData?: Record<string, any>) => {
              const column = visibleColumns.find((c) => c.columnName === columnName);
              return column ? formatCellValue(value, column, rowData) : String(value);
            }}
            getColumnWidth={getColumnWidth}
            containerWidth={calculatedWidth}
          />
        </div>

        {paginationJSX}
      </div>
    );
  }

  // 일반 테이블 모드 (네이티브 HTML 테이블)
  return (
    <>
      <div {...domProps}>
        {/* 필터 */}
        {tableConfig.filter?.enabled && (
          <div className="border-border flex-shrink-0 border-b px-4 py-3 sm:px-6 sm:py-4">
            <div className="flex flex-col gap-3 sm:flex-row sm:items-start sm:gap-4">
              <div className="flex-1">
                <AdvancedSearchFilters
                  filters={activeFilters}
                  searchValues={searchValues}
                  onSearchValueChange={handleSearchValueChange}
                  onSearch={handleAdvancedSearch}
                  onClearFilters={handleClearAdvancedFilters}
                />
              </div>
              <div className="flex items-center gap-2">
                {/* 전체 개수 */}
                <div className="hidden sm:block text-sm text-muted-foreground whitespace-nowrap">
                  전체 <span className="font-semibold text-foreground">{totalItems.toLocaleString()}</span>개
                </div>
                
                <Button
                  variant="outline"
                  size="sm"
                  onClick={() => setIsTableOptionsOpen(true)}
                  className="w-full flex-shrink-0 sm:mt-1 sm:w-auto"
                >
                  <TableIcon className="mr-2 h-4 w-4" />
                  테이블 옵션
                </Button>
                <Button
                  variant="outline"
                  size="sm"
                  onClick={() => setIsFilterSettingOpen(true)}
                  className="w-full flex-shrink-0 sm:mt-1 sm:w-auto"
                >
                  <Settings className="mr-2 h-4 w-4" />
                  필터 설정
                </Button>
                <Popover>
                  <PopoverTrigger asChild>
                    <Button
                      variant="outline"
                      size="sm"
                      className="flex-shrink-0 w-full sm:w-auto sm:mt-1"
                    >
                      <Layers className="mr-2 h-4 w-4" />
                      그룹 설정
                      {groupByColumns.length > 0 && (
                        <span className="ml-2 rounded-full bg-primary px-2 py-0.5 text-[10px] font-semibold text-primary-foreground">
                          {groupByColumns.length}
                        </span>
                      )}
                    </Button>
                  </PopoverTrigger>
                  <PopoverContent className="w-80 p-0" align="end">
                    <div className="space-y-3 p-4">
                      <div className="space-y-1">
                        <h4 className="text-sm font-semibold">그룹 설정</h4>
                        <p className="text-xs text-muted-foreground">
                          데이터를 그룹화할 컬럼을 선택하세요
                        </p>
                      </div>

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

                      {/* 선택된 그룹 안내 */}
                      {groupByColumns.length > 0 && (
                        <div className="rounded bg-muted/30 p-2 text-xs text-muted-foreground">
                          <span className="font-semibold text-foreground">
                            {groupByColumns.map((col) => columnLabels[col] || col).join(" → ")}
                          </span>
                        </div>
                      )}

                      {/* 초기화 버튼 */}
                      {groupByColumns.length > 0 && (
                        <Button
                          variant="outline"
                          size="sm"
                          onClick={() => {
                            setGroupByColumns([]);
                            if (groupSettingKey) {
                              localStorage.removeItem(groupSettingKey);
                            }
                            toast.success("그룹 설정이 초기화되었습니다");
                          }}
                          className="w-full text-xs"
                        >
                          초기화
                        </Button>
                      )}
                    </div>
                  </PopoverContent>
                </Popover>
              </div>
            </div>
          </div>
        )}

        {/* 그룹 표시 배지 */}
        {groupByColumns.length > 0 && (
          <div className="border-border bg-muted/30 border-b px-4 py-2 sm:px-6">
            <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={clearGrouping}
                className="hover:bg-destructive/10 text-destructive ml-auto rounded p-1"
                title="그룹 해제"
              >
                <X className="h-4 w-4" />
              </button>
            </div>
          </div>
        )}

        {/* 테이블 컨테이너 */}
        <div
          className="flex w-full max-w-full flex-1 flex-col overflow-hidden"
          style={{ marginTop: `${tableConfig.filter?.bottomSpacing ?? 8}px` }}
        >
          {/* 스크롤 영역 */}
          <div
            className="bg-background flex-1 w-full max-w-full overflow-x-auto overflow-y-auto"
            style={{ position: "relative" }}
          >
            {/* 테이블 */}
            <table
              className={cn("table-mobile-fixed w-full max-w-full", !showGridLines && "hide-grid")}
              style={{
                borderCollapse: "collapse",
                width: "100%",
                tableLayout: "fixed",
              }}
            >
              {/* 헤더 (sticky) */}
              <thead
                className="sticky z-50"
                style={{
                  position: "sticky",
                  top: "-2px",
                  zIndex: 50,
                  backgroundColor: "hsl(var(--background))",
                }}
              >
                <tr
                  className="border-primary/20 bg-muted h-10 border-b-2 sm:h-12"
                  style={{
                    backgroundColor: "hsl(var(--muted))",
                  }}
                >
                  {visibleColumns.map((column, columnIndex) => {
                    const columnWidth = columnWidths[column.columnName];
                    const isFrozen = frozenColumns.includes(column.columnName);
                    const frozenIndex = frozenColumns.indexOf(column.columnName);

                    // 틀고정된 컬럼의 left 위치 계산
                    let leftPosition = 0;
                    if (isFrozen && frozenIndex > 0) {
                      for (let i = 0; i < frozenIndex; i++) {
                        const frozenCol = frozenColumns[i];
                        const frozenColWidth = columnWidths[frozenCol] || 150;
                        leftPosition += frozenColWidth;
                      }
                    }

                    return (
                      <th
                        key={column.columnName}
                        ref={(el) => (columnRefs.current[column.columnName] = el)}
                        className={cn(
                          "text-foreground/90 relative h-8 overflow-hidden text-xs font-bold text-ellipsis whitespace-nowrap select-none sm:h-10 sm:text-sm",
                          column.columnName === "__checkbox__" ? "px-0 py-1" : "px-2 py-1 sm:px-4 sm:py-2",
                          column.sortable !== false &&
                            column.columnName !== "__checkbox__" &&
                            "hover:bg-muted/70 cursor-pointer transition-colors",
                          isFrozen && "sticky z-60 shadow-[2px_0_4px_rgba(0,0,0,0.1)]",
                        )}
                        style={{
                          textAlign: column.columnName === "__checkbox__" ? "center" : "center",
                          width:
                            column.columnName === "__checkbox__"
                              ? "48px"
                              : columnWidth
                                ? `${columnWidth}px`
                                : undefined,
                          minWidth: column.columnName === "__checkbox__" ? "48px" : undefined,
                          maxWidth: column.columnName === "__checkbox__" ? "48px" : undefined,
                          userSelect: "none",
                          backgroundColor: "hsl(var(--muted))",
                          ...(isFrozen && { left: `${leftPosition}px` }),
                        }}
                        onClick={() => {
                          if (isResizing.current) return;
                          if (column.sortable !== false && column.columnName !== "__checkbox__") {
                            handleSort(column.columnName);
                          }
                        }}
                      >
                        {column.columnName === "__checkbox__" ? (
                          renderCheckboxHeader()
                        ) : (
                          <div style={{ display: "flex", alignItems: "center", gap: "8px" }}>
                            <span>{columnLabels[column.columnName] || column.displayName}</span>
                            {column.sortable !== false && sortColumn === column.columnName && (
                              <span>{sortDirection === "asc" ? "↑" : "↓"}</span>
                            )}
                          </div>
                        )}
                        {/* 리사이즈 핸들 (체크박스 제외) */}
                        {columnIndex < visibleColumns.length - 1 && column.columnName !== "__checkbox__" && (
                          <div
                            className="absolute top-0 right-0 z-20 h-full w-2 cursor-col-resize hover:bg-blue-500"
                            style={{ marginRight: "-4px", paddingLeft: "4px", paddingRight: "4px" }}
                            onClick={(e) => e.stopPropagation()} // 정렬 클릭 방지
                            onMouseDown={(e) => {
                              e.preventDefault();
                              e.stopPropagation();

                              const thElement = columnRefs.current[column.columnName];
                              if (!thElement) return;

                              isResizing.current = true;

                              const startX = e.clientX;
                              const startWidth = columnWidth || thElement.offsetWidth;

                              // 드래그 중 텍스트 선택 방지
                              document.body.style.userSelect = "none";
                              document.body.style.cursor = "col-resize";

                              const handleMouseMove = (moveEvent: MouseEvent) => {
                                moveEvent.preventDefault();

                                const diff = moveEvent.clientX - startX;
                                const newWidth = Math.max(80, startWidth + diff);

                                // 직접 DOM 스타일 변경 (리렌더링 없음)
                                if (thElement) {
                                  thElement.style.width = `${newWidth}px`;
                                }
                              };

                              const handleMouseUp = () => {
                                // 최종 너비를 state에 저장
                                if (thElement) {
                                  const finalWidth = Math.max(80, thElement.offsetWidth);
                                  setColumnWidths((prev) => ({ ...prev, [column.columnName]: finalWidth }));
                                }

                                // 텍스트 선택 복원
                                document.body.style.userSelect = "";
                                document.body.style.cursor = "";

                                // 약간의 지연 후 리사이즈 플래그 해제 (클릭 이벤트가 먼저 처리되지 않도록)
                                setTimeout(() => {
                                  isResizing.current = false;
                                }, 100);

                                document.removeEventListener("mousemove", handleMouseMove);
                                document.removeEventListener("mouseup", handleMouseUp);
                              };

                              document.addEventListener("mousemove", handleMouseMove);
                              document.addEventListener("mouseup", handleMouseUp);
                            }}
                          />
                        )}
                      </th>
                    );
                  })}
                </tr>
              </thead>

              {/* 바디 (스크롤) */}
              <tbody key={`tbody-${categoryMappingsKey}`} style={{ position: "relative" }}>
                {loading ? (
                  <tr>
                    <td colSpan={visibleColumns.length} className="p-12 text-center">
                      <div className="flex flex-col items-center gap-3">
                        <RefreshCw className="text-muted-foreground h-8 w-8 animate-spin" />
                        <div className="text-muted-foreground text-sm font-medium">로딩 중...</div>
                      </div>
                    </td>
                  </tr>
                ) : error ? (
                  <tr>
                    <td colSpan={visibleColumns.length} className="p-12 text-center">
                      <div className="flex flex-col items-center gap-3">
                        <div className="text-destructive text-sm font-medium">오류 발생</div>
                        <div className="text-muted-foreground text-xs">{error}</div>
                      </div>
                    </td>
                  </tr>
                ) : data.length === 0 ? (
                  <tr>
                    <td colSpan={visibleColumns.length} className="p-12 text-center">
                      <div className="flex flex-col items-center gap-3">
                        <TableIcon className="text-muted-foreground/50 h-12 w-12" />
                        <div className="text-muted-foreground text-sm font-medium">데이터가 없습니다</div>
                        <div className="text-muted-foreground text-xs">
                          조건을 변경하거나 새로운 데이터를 추가해보세요
                        </div>
                      </div>
                    </td>
                  </tr>
                ) : groupByColumns.length > 0 && groupedData.length > 0 ? (
                  // 그룹화된 렌더링
                  groupedData.map((group) => {
                    const isCollapsed = collapsedGroups.has(group.groupKey);
                    return (
                      <React.Fragment key={group.groupKey}>
                        {/* 그룹 헤더 */}
                        <tr>
                          <td
                            colSpan={visibleColumns.length}
                            className="bg-muted/50 border-border sticky top-[48px] z-[5] border-b"
                            style={{ top: "48px" }}
                          >
                            <div
                              className="hover:bg-muted flex cursor-pointer items-center gap-3 p-3"
                              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="flex-1 text-sm font-medium">{group.groupKey}</span>
                              <span className="text-muted-foreground text-xs">({group.count}건)</span>
                            </div>
                          </td>
                        </tr>
                        {/* 그룹 데이터 */}
                        {!isCollapsed &&
                          group.items.map((row, index) => (
                            <tr
                              key={index}
                              className={cn(
                                "bg-background hover:bg-muted/50 cursor-pointer border-b transition-colors",
                              )}
                              onClick={(e) => handleRowClick(row, index, e)}
                            >
                              {visibleColumns.map((column, colIndex) => {
                                const mappedColumnName = joinColumnMapping[column.columnName] || column.columnName;
                                const cellValue = row[mappedColumnName];

                                const meta = columnMeta[column.columnName];
                                const inputType = meta?.inputType || column.inputType;
                                const isNumeric = inputType === "number" || inputType === "decimal";

                                const isFrozen = frozenColumns.includes(column.columnName);
                                const frozenIndex = frozenColumns.indexOf(column.columnName);

                                // 틀고정된 컬럼의 left 위치 계산
                                let leftPosition = 0;
                                if (isFrozen && frozenIndex > 0) {
                                  for (let i = 0; i < frozenIndex; i++) {
                                    const frozenCol = frozenColumns[i];
                                    const frozenColWidth = columnWidths[frozenCol] || 150;
                                    leftPosition += frozenColWidth;
                                  }
                                }

                                return (
                                  <td
                                    key={column.columnName}
                                    className={cn(
                                      "text-foreground overflow-hidden text-xs text-ellipsis whitespace-nowrap font-normal sm:text-sm",
                                      column.columnName === "__checkbox__" ? "px-0 py-1" : "px-2 py-1 sm:px-4 sm:py-1.5",
                                      isFrozen && "bg-background sticky z-10 shadow-[2px_0_4px_rgba(0,0,0,0.05)]",
                                    )}
                                    style={{
                                      textAlign:
                                        column.columnName === "__checkbox__"
                                          ? "center"
                                          : isNumeric
                                            ? "right"
                                            : column.align || "left",
                                      width:
                                        column.columnName === "__checkbox__"
                                          ? "48px"
                                          : `${100 / visibleColumns.length}%`,
                                      minWidth: column.columnName === "__checkbox__" ? "48px" : undefined,
                                      maxWidth: column.columnName === "__checkbox__" ? "48px" : undefined,
                                      ...(isFrozen && { left: `${leftPosition}px` }),
                                    }}
                                  >
                                    {column.columnName === "__checkbox__"
                                      ? renderCheckboxCell(row, index)
                                      : formatCellValue(cellValue, column, row)}
                                  </td>
                                );
                              })}
                            </tr>
                          ))}
                      </React.Fragment>
                    );
                  })
                ) : (
                  // 일반 렌더링 (그룹 없음)
                  data.map((row, index) => (
                    <tr
                      key={index}
                      className={cn(
                        "bg-background hover:bg-muted/50 cursor-pointer border-b transition-colors",
                      )}
                      onClick={(e) => handleRowClick(row, index, e)}
                    >
                      {visibleColumns.map((column, colIndex) => {
                        const mappedColumnName = joinColumnMapping[column.columnName] || column.columnName;
                        const cellValue = row[mappedColumnName];

                        const meta = columnMeta[column.columnName];
                        const inputType = meta?.inputType || column.inputType;
                        const isNumeric = inputType === "number" || inputType === "decimal";

                        const isFrozen = frozenColumns.includes(column.columnName);
                        const frozenIndex = frozenColumns.indexOf(column.columnName);

                        // 틀고정된 컬럼의 left 위치 계산
                        let leftPosition = 0;
                        if (isFrozen && frozenIndex > 0) {
                          for (let i = 0; i < frozenIndex; i++) {
                            const frozenCol = frozenColumns[i];
                            const frozenColWidth = columnWidths[frozenCol] || 150;
                            leftPosition += frozenColWidth;
                          }
                        }

                        return (
                          <td
                            key={column.columnName}
                            className={cn(
                              "text-foreground overflow-hidden text-xs text-ellipsis whitespace-nowrap font-normal sm:text-sm",
                              column.columnName === "__checkbox__" ? "px-0 py-1" : "px-2 py-1 sm:px-4 sm:py-1.5",
                              isFrozen && "bg-background sticky z-10 shadow-[2px_0_4px_rgba(0,0,0,0.05)]",
                            )}
                            style={{
                              textAlign:
                                column.columnName === "__checkbox__"
                                  ? "center"
                                  : isNumeric
                                    ? "right"
                                    : column.align || "left",
                              width: column.columnName === "__checkbox__" ? "48px" : `${100 / visibleColumns.length}%`,
                              minWidth: column.columnName === "__checkbox__" ? "48px" : undefined,
                              maxWidth: column.columnName === "__checkbox__" ? "48px" : undefined,
                              ...(isFrozen && { left: `${leftPosition}px` }),
                            }}
                          >
                            {column.columnName === "__checkbox__"
                              ? renderCheckboxCell(row, index)
                              : formatCellValue(cellValue, column, row)}
                          </td>
                        );
                      })}
                    </tr>
                  ))
                )}
              </tbody>
            </table>
          </div>
        </div>

        {/* 페이지네이션 */}
        {paginationJSX}
      </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={
                  visibleFilterColumns.size ===
                    visibleColumns.filter((col) => col.columnName !== "__checkbox__").length &&
                  visibleColumns.filter((col) => col.columnName !== "__checkbox__").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">
                {visibleFilterColumns.size} / {visibleColumns.filter((col) => col.columnName !== "__checkbox__").length}
                개
              </span>
            </div>

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

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

      {/* 테이블 옵션 모달 */}
      <TableOptionsModal
        isOpen={isTableOptionsOpen}
        onClose={() => setIsTableOptionsOpen(false)}
        columns={visibleColumns.map((col) => ({
          columnName: col.columnName,
          label: columnLabels[col.columnName] || col.displayName || col.columnName,
          visible: col.visible !== false,
          width: columnWidths[col.columnName],
          frozen: frozenColumns.includes(col.columnName),
        }))}
        onSave={handleTableOptionsSave}
        tableName={tableConfig.selectedTable || "table"}
        userId={userId}
      />
    </>
  );
};

export const TableListWrapper: React.FC<TableListComponentProps> = (props) => {
  return <TableListComponent {...props} />;
};
