"use client";

import React, { useEffect, useState, useMemo, useCallback, useRef } from "react";
import { ComponentRendererProps } from "@/types/component";
import { CardDisplayConfig } from "./types";
import { tableTypeApi } from "@/lib/api/screen";
import { entityJoinApi } from "@/lib/api/entityJoin";
import { getFullImageUrl, apiClient } from "@/lib/api/client";
import { filterDOMProps } from "@/lib/utils/domPropsFilter";
import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
import { useScreenContextOptional } from "@/contexts/ScreenContext";
import { useSplitPanelContext } from "@/contexts/SplitPanelContext";
import { useModalDataStore } from "@/stores/modalDataStore";
import { useTableOptions } from "@/contexts/TableOptionsContext";
import { TableFilter, ColumnVisibility, TableColumn } from "@/types/table-options";

export interface CardDisplayComponentProps extends ComponentRendererProps {
  config?: CardDisplayConfig;
  tableData?: any[];
  tableColumns?: any[];
}

/**
 * CardDisplay 컴포넌트
 * 테이블 데이터를 카드 형태로 표시하는 컴포넌트
 */
export const CardDisplayComponent: React.FC<CardDisplayComponentProps> = ({
  component,
  isDesignMode = false,
  isSelected = false,
  isInteractive = false,
  onClick,
  onDragStart,
  onDragEnd,
  config,
  className,
  style,
  formData,
  onFormDataChange,
  screenId,
  tableName,
  tableData = [],
  tableColumns = [],
  ...props
}) => {
  // 컨텍스트 (선택적 - 디자인 모드에서는 없을 수 있음)
  const screenContext = useScreenContextOptional();
  const splitPanelContext = useSplitPanelContext();
  const splitPanelPosition = screenContext?.splitPanelPosition;

  // TableOptions Context (검색 필터 위젯 연동용)
  let tableOptionsContext: ReturnType<typeof useTableOptions> | null = null;
  try {
    tableOptionsContext = useTableOptions();
  } catch (e) {
    // Context가 없으면 (디자이너 모드) 무시
  }

  // 테이블 데이터 상태 관리
  const [loadedTableData, setLoadedTableData] = useState<any[]>([]);
  const [loadedTableColumns, setLoadedTableColumns] = useState<any[]>([]);
  const [loading, setLoading] = useState(true); // 초기 로딩 상태를 true로 설정
  const [initialLoadDone, setInitialLoadDone] = useState(false); // 초기 로드 완료 여부
  const [hasEverSelectedLeftData, setHasEverSelectedLeftData] = useState(false); // 좌측 데이터 선택 이력
  
  // 필터 상태 (검색 필터 위젯에서 전달받은 필터)
  const [filters, setFiltersInternal] = useState<TableFilter[]>([]);
  
  // 새로고침 트리거 (refreshCardDisplay 이벤트 수신 시 증가)
  const [refreshKey, setRefreshKey] = useState(0);
  
  // refreshCardDisplay 이벤트 리스너
  useEffect(() => {
    const handleRefreshCardDisplay = () => {
      console.log("📍 [CardDisplay] refreshCardDisplay 이벤트 수신 - 데이터 새로고침");
      setRefreshKey((prev) => prev + 1);
    };
    
    window.addEventListener("refreshCardDisplay", handleRefreshCardDisplay);
    
    return () => {
      window.removeEventListener("refreshCardDisplay", handleRefreshCardDisplay);
    };
  }, []);
  
  // 필터 상태 변경 래퍼
  const setFilters = useCallback((newFilters: TableFilter[]) => {
    setFiltersInternal(newFilters);
  }, []);
  
  // 카테고리 매핑 상태 (카테고리 코드 -> 라벨/색상)
  const [columnMeta, setColumnMeta] = useState<
    Record<string, { webType?: string; codeCategory?: string; inputType?: string }>
  >({});
  const [categoryMappings, setCategoryMappings] = useState<
    Record<string, Record<string, { label: string; color?: string }>>
  >({});

  // 선택된 카드 상태 (Set으로 변경하여 테이블 리스트와 동일하게)
  const [selectedRows, setSelectedRows] = useState<Set<string>>(new Set());

  // 상세보기 모달 상태
  const [viewModalOpen, setViewModalOpen] = useState(false);
  const [selectedData, setSelectedData] = useState<any>(null);

  // 편집 모달 상태
  const [editModalOpen, setEditModalOpen] = useState(false);
  const [editData, setEditData] = useState<any>(null);

  // 카드 액션 핸들러
  const handleCardView = (data: any) => {
    // console.log("👀 상세보기 클릭:", data);
    setSelectedData(data);
    setViewModalOpen(true);
  };

  const handleCardEdit = (data: any) => {
    // console.log("✏️ 편집 클릭:", data);
    setEditData({ ...data }); // 복사본 생성
    setEditModalOpen(true);
  };

  // 삭제 핸들러
  const handleCardDelete = async (data: any, index: number) => {
    // 사용자 확인
    if (!confirm("정말로 이 항목을 삭제하시겠습니까?")) {
      return;
    }

    try {
      const tableNameToUse = tableName || component.componentConfig?.tableName;
      if (!tableNameToUse) {
        alert("테이블 정보가 없습니다.");
        return;
      }

      // 삭제할 데이터를 배열로 감싸기 (API가 배열을 기대함)
      const deleteData = [data];


      // API 호출로 데이터 삭제 (POST 방식으로 변경 - DELETE는 body 전달이 불안정)
      // 백엔드 API는 DELETE /api/table-management/tables/:tableName/delete 이지만
      // axios에서 DELETE body 전달 문제가 있어 직접 request 설정 사용
      const response = await apiClient.request({
        method: 'DELETE',
        url: `/table-management/tables/${tableNameToUse}/delete`,
        data: deleteData,
        headers: {
          'Content-Type': 'application/json',
        },
      });

      if (response.data.success) {
        alert("삭제되었습니다.");
        
        // 로컬 상태에서 삭제된 항목 제거
        setLoadedTableData(prev => prev.filter((item, idx) => idx !== index));
        
        // 선택된 항목이면 선택 해제
        const cardKey = getCardKey(data, index);
        if (selectedRows.has(cardKey)) {
          const newSelectedRows = new Set(selectedRows);
          newSelectedRows.delete(cardKey);
          setSelectedRows(newSelectedRows);
        }
      } else {
        alert(`삭제 실패: ${response.data.message || response.data.error || "알 수 없는 오류"}`);
      }
    } catch (error: any) {
      const errorMessage = error.response?.data?.message || error.message || "알 수 없는 오류";
      alert(`삭제 중 오류가 발생했습니다: ${errorMessage}`);
    }
  };

  // 편집 폼 데이터 변경 핸들러
  const handleEditFormChange = (key: string, value: string) => {
    setEditData((prev: any) => ({
      ...prev,
      [key]: value
    }));
  };

  // 편집 저장 핸들러
  const handleEditSave = async () => {
    // console.log("💾 편집 저장:", editData);
    
    try {
      // TODO: 실제 API 호출로 데이터 업데이트
      // await tableTypeApi.updateTableData(tableName, editData);
      
      // console.log("✅ 편집 저장 완료");
      alert("✅ 저장되었습니다!");
      
      // 모달 닫기
      setEditModalOpen(false);
      setEditData(null);
      
      // 데이터 새로고침 (필요시)
      // loadTableData();
      
    } catch (error) {
      alert("저장에 실패했습니다.");
    }
  };

  // 테이블 데이터 로딩
  useEffect(() => {
    const loadTableData = async () => {
      // 디자인 모드에서는 테이블 데이터를 로드하지 않음
      if (isDesignMode) {
        setLoading(false);
        setInitialLoadDone(true);
        return;
      }

      // 우측 패널인 경우, 좌측 데이터가 선택되지 않으면 데이터 로드하지 않음 (깜빡임 방지)
      // splitPanelPosition이 "right"이면 분할 패널 내부이므로 연결 필터가 있을 가능성이 높음
      const isRightPanelEarly = splitPanelPosition === "right";
      const hasSelectedLeftDataEarly = splitPanelContext?.selectedLeftData && 
        Object.keys(splitPanelContext.selectedLeftData).length > 0;
      
      if (isRightPanelEarly && !hasSelectedLeftDataEarly) {
        // 우측 패널이고 좌측 데이터가 선택되지 않은 경우 - 기존 데이터 유지 (깜빡임 방지)
        // 초기 로드가 아닌 경우에는 데이터를 지우지 않음
        if (!initialLoadDone) {
          setLoadedTableData([]);
        }
        setLoading(false);
        setInitialLoadDone(true);
        return;
      }

      // tableName 확인 (props에서 전달받은 tableName 사용)
      const tableNameToUse = tableName || component.componentConfig?.tableName || 'user_info'; // 기본 테이블명 설정

      if (!tableNameToUse) {
        setLoading(false);
        setInitialLoadDone(true);
        return;
      }

      // 연결 필터 확인 (분할 패널 내부일 때)
      let linkedFilterValues: Record<string, any> = {};
      let hasLinkedFiltersConfigured = false;
      let hasSelectedLeftData = false;

      if (splitPanelContext) {
        // 연결 필터 설정 여부 확인 (현재 테이블에 해당하는 필터가 있는지)
        const linkedFiltersConfig = splitPanelContext.linkedFilters || [];
        hasLinkedFiltersConfigured = linkedFiltersConfig.some(
          (filter) => filter.targetColumn?.startsWith(tableNameToUse + ".") ||
                      filter.targetColumn === tableNameToUse
        );

        // 좌측 데이터 선택 여부 확인
        hasSelectedLeftData = splitPanelContext.selectedLeftData && 
                              Object.keys(splitPanelContext.selectedLeftData).length > 0;

        linkedFilterValues = splitPanelContext.getLinkedFilterValues();
        // 현재 테이블에 해당하는 필터만 추출 (테이블명.컬럼명 형식에서)
        // 연결 필터는 코드 값이므로 정확한 매칭(equals)을 사용해야 함
        const tableSpecificFilters: Record<string, any> = {};
        for (const [key, value] of Object.entries(linkedFilterValues)) {
          // key가 "테이블명.컬럼명" 형식인 경우
          if (key.includes(".")) {
            const [tblName, columnName] = key.split(".");
            if (tblName === tableNameToUse) {
              // 연결 필터는 코드 값이므로 equals 연산자 사용
              tableSpecificFilters[columnName] = { value, operator: "equals" };
              hasLinkedFiltersConfigured = true;
            }
          } else {
            // 테이블명 없이 컬럼명만 있는 경우 그대로 사용 (equals)
            tableSpecificFilters[key] = { value, operator: "equals" };
          }
        }
        linkedFilterValues = tableSpecificFilters;
        
      }

      // 우측 패널이고 연결 필터가 설정되어 있지만 좌측에서 데이터가 선택되지 않은 경우 빈 데이터 표시
      // 또는 우측 패널이고 linkedFilters 설정이 있으면 좌측 선택 필수
      // splitPanelPosition은 screenContext에서 가져오거나, splitPanelContext에서 screenId로 확인
      const isRightPanelFromContext = splitPanelPosition === "right";
      const isRightPanelFromSplitContext = screenId && splitPanelContext?.getPositionByScreenId 
        ? splitPanelContext.getPositionByScreenId(screenId as number) === "right"
        : false;
      const isRightPanel = isRightPanelFromContext || isRightPanelFromSplitContext;
      const hasLinkedFiltersInConfig = splitPanelContext?.linkedFilters && splitPanelContext.linkedFilters.length > 0;
      
      
      if (isRightPanel && (hasLinkedFiltersConfigured || hasLinkedFiltersInConfig) && !hasSelectedLeftData) {
        setLoadedTableData([]);
        setLoading(false);
        setInitialLoadDone(true);
        return;
      }

      try {
        setLoading(true);

        // API 호출 파라미터에 연결 필터 추가 (search 객체 안에 넣어야 함)
        const apiParams: Record<string, any> = {
          page: 1,
          size: 50, // 카드 표시용으로 적당한 개수
          search: Object.keys(linkedFilterValues).length > 0 ? linkedFilterValues : undefined,
        };

        // 조인 컬럼 설정 가져오기 (componentConfig에서)
        const joinColumnsConfig = component.componentConfig?.joinColumns || [];
        const entityJoinColumns = joinColumnsConfig
          .filter((col: any) => col.isJoinColumn)
          .map((col: any) => ({
            columnName: col.columnName,
            sourceColumn: col.sourceColumn,
            referenceTable: col.referenceTable,
            referenceColumn: col.referenceColumn,
            displayColumn: col.referenceColumn,
            label: col.label,
            joinAlias: col.columnName, // 백엔드에서 필요한 joinAlias 추가
            sourceTable: tableNameToUse, // 기준 테이블
          }));

        // 테이블 데이터, 컬럼 정보, 입력 타입 정보를 병렬로 로드
        // 조인 컬럼이 있으면 entityJoinApi 사용
        let dataResponse;
        if (entityJoinColumns.length > 0) {
          console.log("🔗 [CardDisplay] 엔티티 조인 API 사용:", entityJoinColumns);
          dataResponse = await entityJoinApi.getTableDataWithJoins(tableNameToUse, {
            ...apiParams,
            additionalJoinColumns: entityJoinColumns,
          });
        } else {
          dataResponse = await tableTypeApi.getTableData(tableNameToUse, apiParams);
        }

        const [columnsResponse, inputTypesResponse] = await Promise.all([
          tableTypeApi.getColumns(tableNameToUse),
          tableTypeApi.getColumnInputTypes(tableNameToUse),
        ]);

        setLoadedTableData(dataResponse.data);
        setLoadedTableColumns(columnsResponse);
        
        // 컬럼 메타 정보 설정 (inputType 포함)
        const meta: Record<string, { webType?: string; codeCategory?: string; inputType?: string }> = {};
        inputTypesResponse.forEach((item: any) => {
          meta[item.columnName || item.column_name] = {
            webType: item.webType || item.web_type,
            inputType: item.inputType || item.input_type,
            codeCategory: item.codeCategory || item.code_category,
          };
        });
        setColumnMeta(meta);
        
        // 카테고리 타입 컬럼 찾기 및 매핑 로드
        const categoryColumns = Object.entries(meta)
          .filter(([_, m]) => m.inputType === "category")
          .map(([columnName]) => columnName);
        
        
        if (categoryColumns.length > 0) {
          const mappings: Record<string, Record<string, { label: string; color?: string }>> = {};
          
          for (const columnName of categoryColumns) {
            try {
              const response = await apiClient.get(`/table-categories/${tableNameToUse}/${columnName}/values?includeInactive=true`);
              
              
              if (response.data.success && response.data.data) {
                const mapping: Record<string, { label: string; color?: string }> = {};
                response.data.data.forEach((item: any) => {
                  // API 응답 형식: valueCode, valueLabel (camelCase)
                  const code = item.valueCode || item.value_code || item.category_code || item.code || item.value;
                  const label = item.valueLabel || item.value_label || item.category_name || item.name || item.label || code;
                  // color가 null/undefined/"none"이면 undefined로 유지 (배지 없음)
                  const rawColor = item.color ?? item.badge_color;
                  const color = (rawColor && rawColor !== "none") ? rawColor : undefined;
                  mapping[code] = { label, color };
                });
                mappings[columnName] = mapping;
              }
            } catch (error) {
              // 카테고리 매핑 로드 실패 시 무시
            }
          }
          
          setCategoryMappings(mappings);
        }
      } catch (error) {
        setLoadedTableData([]);
        setLoadedTableColumns([]);
      } finally {
        setLoading(false);
        setInitialLoadDone(true);
      }
    };

    loadTableData();
  }, [isDesignMode, tableName, component.componentConfig?.tableName, splitPanelContext?.selectedLeftData, splitPanelPosition, refreshKey]);

  // 컴포넌트 설정 (기본값 보장)
  const componentConfig = {
    cardsPerRow: 3, // 기본값 3 (한 행당 카드 수)
    cardSpacing: 16,
    cardStyle: {
      showTitle: true,
      showSubtitle: true,
      showDescription: true,
      showImage: false,
      showActions: true,
      maxDescriptionLength: 100,
      imagePosition: "top",
      imageSize: "medium",
    },
    columnMapping: {},
    dataSource: "table",
    staticData: [],
    ...config,
    ...component.config,
    ...component.componentConfig,
  } as CardDisplayConfig;

  // 컴포넌트 기본 스타일
  const componentStyle: React.CSSProperties = {
    width: "100%",
    height: "100%",
    position: "relative",
    backgroundColor: "transparent",
  };
  
  // width는 항상 100%로 고정 (부모 컨테이너가 gridColumns로 크기 제어)
  // 카드 컴포넌트는 ...style 스프레드가 없으므로 여기서 명시적으로 설정

  if (isDesignMode) {
    componentStyle.border = "1px dashed hsl(var(--border))";
    componentStyle.borderColor = isSelected ? "hsl(var(--ring))" : "hsl(var(--border))";
  }

  // 우측 패널 + 좌측 미선택 상태 체크를 위한 값들 (displayData 외부에서 계산)
  const isRightPanelForDisplay = splitPanelPosition === "right" || 
    (screenId && splitPanelContext?.getPositionByScreenId?.(screenId as number) === "right");
  const hasLinkedFiltersForDisplay = splitPanelContext?.linkedFilters && splitPanelContext.linkedFilters.length > 0;
  const selectedLeftDataForDisplay = splitPanelContext?.selectedLeftData;
  const hasSelectedLeftDataForDisplay = selectedLeftDataForDisplay && 
    Object.keys(selectedLeftDataForDisplay).length > 0;
  
  // 좌측 데이터가 한 번이라도 선택된 적이 있으면 기록
  useEffect(() => {
    if (hasSelectedLeftDataForDisplay) {
      setHasEverSelectedLeftData(true);
    }
  }, [hasSelectedLeftDataForDisplay]);
  
  // 우측 패널이고 연결 필터가 있고, 좌측 데이터가 한 번도 선택된 적이 없는 경우에만 "선택해주세요" 표시
  // 한 번이라도 선택된 적이 있으면 깜빡임 방지를 위해 기존 데이터 유지
  const shouldHideDataForRightPanel = isRightPanelForDisplay && 
    !hasEverSelectedLeftData && 
    !hasSelectedLeftDataForDisplay;

  // 표시할 데이터 결정 (로드된 테이블 데이터 우선 사용)
  const displayData = useMemo(() => {
    // 우측 패널이고 linkedFilters가 설정되어 있지만 좌측 데이터가 선택되지 않은 경우 빈 배열 반환
    if (shouldHideDataForRightPanel) {
      return [];
    }
    
    // 로드된 테이블 데이터가 있으면 항상 우선 사용 (dataSource 설정 무시)
    if (loadedTableData.length > 0) {
      return loadedTableData;
    }

    // props로 전달받은 테이블 데이터가 있으면 사용
    if (tableData.length > 0) {
      return tableData;
    }

    if (componentConfig.staticData && componentConfig.staticData.length > 0) {
      return componentConfig.staticData;
    }

    // 데이터가 없으면 빈 배열 반환
    return [];
  }, [shouldHideDataForRightPanel, loadedTableData, tableData, componentConfig.staticData]);

  // 실제 사용할 테이블 컬럼 정보 (로드된 컬럼 우선 사용)
  const actualTableColumns = loadedTableColumns.length > 0 ? loadedTableColumns : tableColumns;

  // 카드 ID 가져오기 함수 (훅은 조기 리턴 전에 선언)
  const getCardKey = useCallback((data: any, index: number): string => {
    return String(data.id || data.objid || data.ID || index);
  }, []);

  // 카드 선택 핸들러 (단일 선택 - 다른 카드 선택 시 기존 선택 해제)
  const handleCardSelection = useCallback((cardKey: string, data: any, checked: boolean) => {
    // 단일 선택: 새로운 Set 생성 (기존 선택 초기화)
    const newSelectedRows = new Set<string>();
    
    if (checked) {
      // 선택 시 해당 카드만 선택
      newSelectedRows.add(cardKey);
    }
    // checked가 false면 빈 Set (선택 해제)
    
    setSelectedRows(newSelectedRows);

    // 선택된 카드 데이터 계산
    const selectedRowsData = displayData.filter((item, index) => 
      newSelectedRows.has(getCardKey(item, index))
    );

    // onFormDataChange 호출
    if (onFormDataChange) {
      onFormDataChange({
        selectedRows: Array.from(newSelectedRows),
        selectedRowsData,
      });
    }

    // modalDataStore에 선택된 데이터 저장
    const tableNameToUse = componentConfig.dataSource?.tableName || tableName;
    if (tableNameToUse && selectedRowsData.length > 0) {
      const modalItems = selectedRowsData.map((row, idx) => ({
        id: getCardKey(row, idx),
        originalData: row,
        additionalData: {},
      }));
      useModalDataStore.getState().setData(tableNameToUse, modalItems);
    } else if (tableNameToUse && selectedRowsData.length === 0) {
      useModalDataStore.getState().clearData(tableNameToUse);
    }

    // 분할 패널 컨텍스트에 선택된 데이터 저장 (좌측 화면인 경우)
    // disableAutoDataTransfer가 true이면 자동 전달 비활성화 (버튼 클릭으로만 전달)
    if (splitPanelContext && splitPanelPosition === "left" && !splitPanelContext.disableAutoDataTransfer) {
      if (checked) {
        splitPanelContext.setSelectedLeftData(data);
      } else {
        splitPanelContext.setSelectedLeftData(null);
      }
    }
  }, [displayData, getCardKey, onFormDataChange, componentConfig.dataSource?.tableName, tableName, splitPanelContext, splitPanelPosition]);

  const handleCardClick = useCallback((data: any, index: number) => {
    const cardKey = getCardKey(data, index);
    const isCurrentlySelected = selectedRows.has(cardKey);
    
    // 단일 선택: 이미 선택된 카드 클릭 시 선택 해제, 아니면 새로 선택
    handleCardSelection(cardKey, data, !isCurrentlySelected);
    
    if (componentConfig.onCardClick) {
      componentConfig.onCardClick(data);
    }
  }, [getCardKey, selectedRows, handleCardSelection, componentConfig.onCardClick]);

  // DataProvidable 인터페이스 구현 (테이블 리스트와 동일)
  const dataProvider = useMemo(() => ({
    componentId: component.id,
    componentType: "card-display" as const,
    
    getSelectedData: () => {
      const selectedData = displayData.filter((item, index) => 
        selectedRows.has(getCardKey(item, index))
      );
      return selectedData;
    },
    
    getAllData: () => {
      return displayData;
    },
    
    clearSelection: () => {
      setSelectedRows(new Set());
    },
  }), [component.id, displayData, selectedRows, getCardKey]);

  // ScreenContext에 데이터 제공자로 등록
  useEffect(() => {
    if (screenContext && component.id) {
      screenContext.registerDataProvider(component.id, dataProvider);
      
      return () => {
        screenContext.unregisterDataProvider(component.id);
      };
    }
  }, [screenContext, component.id, dataProvider]);

  // TableOptionsContext에 테이블 등록 (검색 필터 위젯 연동용)
  const tableId = `card-display-${component.id}`;
  const tableNameToUse = tableName || component.componentConfig?.tableName || '';
  const tableLabel = component.componentConfig?.title || component.label || "카드 디스플레이";

  // ref로 최신 데이터 참조 (useCallback 의존성 문제 해결)
  const loadedTableDataRef = useRef(loadedTableData);
  const categoryMappingsRef = useRef(categoryMappings);
  
  useEffect(() => {
    loadedTableDataRef.current = loadedTableData;
  }, [loadedTableData]);
  
  useEffect(() => {
    categoryMappingsRef.current = categoryMappings;
  }, [categoryMappings]);

  // 필터가 변경되면 데이터 다시 로드 (테이블 리스트와 동일한 패턴)
  // 초기 로드 여부 추적 - 마운트 카운터 사용 (Strict Mode 대응)
  const mountCountRef = useRef(0);
  
  useEffect(() => {
    mountCountRef.current += 1;
    const currentMount = mountCountRef.current;
    
    if (!tableNameToUse || isDesignMode) return;
    
    // 우측 패널이고 linkedFilters가 설정되어 있지만 좌측 데이터가 선택되지 않은 경우 스킵
    const isRightPanel = splitPanelPosition === "right" || 
      (screenId && splitPanelContext?.getPositionByScreenId?.(screenId as number) === "right");
    const hasLinkedFiltersInConfig = splitPanelContext?.linkedFilters && splitPanelContext.linkedFilters.length > 0;
    const hasSelectedLeftData = splitPanelContext?.selectedLeftData && 
      Object.keys(splitPanelContext.selectedLeftData).length > 0;
    
    // 우측 패널이고 좌측 데이터가 선택되지 않은 경우 - 기존 데이터 유지 (깜빡임 방지)
    if (isRightPanel && !hasSelectedLeftData) {
      // 데이터를 지우지 않고 로딩만 false로 설정
      setLoading(false);
      return;
    }
    
    // 첫 2번의 마운트는 초기 로드 useEffect에서 처리 (Strict Mode에서 2번 호출됨)
    // 필터 변경이 아닌 경우 스킵
    if (currentMount <= 2 && filters.length === 0) {
      return;
    }
    
    const loadFilteredData = async () => {
      try {
        // 로딩 상태를 true로 설정하지 않음 - 기존 데이터 유지하면서 새 데이터 로드 (깜빡임 방지)
        
        // 필터 값을 검색 파라미터로 변환
        const searchParams: Record<string, any> = {};
        filters.forEach(filter => {
          if (filter.value !== undefined && filter.value !== null && filter.value !== '') {
            searchParams[filter.columnName] = filter.value;
          }
        });
        
        // search 파라미터로 검색 조건 전달 (API 스펙에 맞게)
        const dataResponse = await tableTypeApi.getTableData(tableNameToUse, {
          page: 1,
          size: 50,
          search: searchParams,
        });
        
        setLoadedTableData(dataResponse.data);
        
        // 데이터 건수 업데이트
        if (tableOptionsContext) {
          tableOptionsContext.updateTableDataCount(tableId, dataResponse.data?.length || 0);
        }
      } catch (error) {
        // 필터 적용 실패 시 무시
      }
    };
    
    // 필터 변경 시 항상 데이터 다시 로드 (빈 필터 = 전체 데이터)
    loadFilteredData();
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [filters, tableNameToUse, isDesignMode, tableId, splitPanelContext?.selectedLeftData, splitPanelPosition]);

  // 컬럼 고유 값 조회 함수 (select 타입 필터용)
  const getColumnUniqueValues = useCallback(async (columnName: string): Promise<Array<{ label: string; value: string }>> => {
    if (!tableNameToUse) return [];
    
    try {
      // 현재 로드된 데이터에서 고유 값 추출
      const uniqueValues = new Set<string>();
      loadedTableDataRef.current.forEach(row => {
        const value = row[columnName];
        if (value !== null && value !== undefined && value !== '') {
          uniqueValues.add(String(value));
        }
      });
      
      // 카테고리 매핑이 있으면 라벨 적용
      const mapping = categoryMappingsRef.current[columnName];
      return Array.from(uniqueValues).map(value => ({
        value,
        label: mapping?.[value]?.label || value,
      }));
    } catch (error) {
      return [];
    }
  }, [tableNameToUse]);

  // TableOptionsContext에 등록
  // registerTable과 unregisterTable 함수 참조 저장 (의존성 안정화)
  const registerTableRef = useRef(tableOptionsContext?.registerTable);
  const unregisterTableRef = useRef(tableOptionsContext?.unregisterTable);
  
  // setFiltersInternal을 ref로 저장 (등록 시 최신 함수 사용)
  const setFiltersRef = useRef(setFiltersInternal);
  const getColumnUniqueValuesRef = useRef(getColumnUniqueValues);
  
  useEffect(() => {
    registerTableRef.current = tableOptionsContext?.registerTable;
    unregisterTableRef.current = tableOptionsContext?.unregisterTable;
  }, [tableOptionsContext]);
  
  useEffect(() => {
    setFiltersRef.current = setFiltersInternal;
  }, [setFiltersInternal]);
  
  useEffect(() => {
    getColumnUniqueValuesRef.current = getColumnUniqueValues;
  }, [getColumnUniqueValues]);
  
  // 테이블 등록 (한 번만 실행, 컬럼 변경 시에만 재등록)
  const columnsKey = JSON.stringify(loadedTableColumns.map((col: any) => col.columnName || col.column_name));
  
  useEffect(() => {
    if (!registerTableRef.current || !unregisterTableRef.current) return;
    if (isDesignMode || !tableNameToUse || loadedTableColumns.length === 0) return;

    // 컬럼 정보를 TableColumn 형식으로 변환
    const columns: TableColumn[] = loadedTableColumns.map((col: any) => ({
      columnName: col.columnName || col.column_name,
      columnLabel: col.displayName || col.columnLabel || col.column_label || col.columnName || col.column_name,
      inputType: columnMeta[col.columnName || col.column_name]?.inputType || 'text',
      visible: true,
      width: 200,
      sortable: true,
      filterable: true,
    }));

    // onFilterChange는 ref를 통해 최신 함수를 호출하는 래퍼 사용
    const onFilterChangeWrapper = (newFilters: TableFilter[]) => {
      setFiltersRef.current(newFilters);
    };
    
    const getColumnUniqueValuesWrapper = async (columnName: string) => {
      return getColumnUniqueValuesRef.current(columnName);
    };

    const registration = {
      tableId,
      label: tableLabel,
      tableName: tableNameToUse,
      columns,
      dataCount: loadedTableData.length,
      onFilterChange: onFilterChangeWrapper,
      onGroupChange: () => {}, // 카드 디스플레이는 그룹핑 미지원
      onColumnVisibilityChange: () => {}, // 카드 디스플레이는 컬럼 가시성 미지원
      getColumnUniqueValues: getColumnUniqueValuesWrapper,
    };

    registerTableRef.current(registration);

    const unregister = unregisterTableRef.current;
    const currentTableId = tableId;
    
    return () => {
      unregister(currentTableId);
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [
    isDesignMode,
    tableId,
    tableNameToUse,
    tableLabel,
    columnsKey, // 컬럼 변경 시에만 재등록
  ]);

  // 우측 패널이고 좌측 데이터가 한 번도 선택된 적이 없는 경우에만 "선택해주세요" 표시
  // 한 번이라도 선택된 적이 있으면 로딩 중에도 기존 데이터 유지 (깜빡임 방지)
  if (shouldHideDataForRightPanel) {
    return (
      <div
        className={className}
        style={{
          ...componentStyle,
          ...style,
          display: "flex",
          alignItems: "center",
          justifyContent: "center",
          padding: "20px",
          background: "#f8fafc",
          borderRadius: "12px",
        }}
      >
        <div className="text-muted-foreground text-center">
          <div className="text-lg mb-2">좌측에서 항목을 선택해주세요</div>
          <div className="text-sm text-muted-foreground/70">선택한 항목의 관련 데이터가 여기에 표시됩니다</div>
        </div>
      </div>
    );
  }

  // 로딩 중이고 데이터가 없는 경우에만 로딩 표시
  // 데이터가 있으면 로딩 중에도 기존 데이터 유지 (깜빡임 방지)
  if (loading && displayData.length === 0 && !hasEverSelectedLeftData) {
    return (
      <div
        className={className}
        style={{
          ...componentStyle,
          ...style,
          display: "flex",
          alignItems: "center",
          justifyContent: "center",
          padding: "20px",
        }}
      >
        <div className="text-muted-foreground">테이블 데이터를 로드하는 중...</div>
      </div>
    );
  }

  // 컨테이너 스타일 - 통일된 디자인 시스템 적용
  const containerStyle: React.CSSProperties = {
    display: "grid",
    gridTemplateColumns: `repeat(${componentConfig.cardsPerRow || 3}, 1fr)`, // 기본값 3 (한 행당 카드 수)
    gridAutoRows: "min-content", // 자동 행 생성으로 모든 데이터 표시
    gap: `${componentConfig.cardSpacing || 16}px`, // 카드 간격
    padding: "16px", // 패딩
    width: "100%",
    height: "100%",
    background: "transparent", // 배경색 제거
    overflow: "auto",
    borderRadius: "0", // 라운드 제거
  };

  // 카드 스타일 - 컴팩트한 디자인
  const cardStyle: React.CSSProperties = {
    backgroundColor: "hsl(var(--card))",
    border: "1px solid hsl(var(--border))",
    borderRadius: "8px",
    padding: "16px",
    boxShadow: "0 1px 2px rgba(0, 0, 0, 0.05)",
    transition: "all 0.2s ease",
    overflow: "hidden",
    display: "flex",
    flexDirection: "column",
    position: "relative",
    cursor: isDesignMode ? "pointer" : "default",
    width: "100%", // 전체 너비 차지
  };

  // 텍스트 자르기 함수
  const truncateText = (text: string, maxLength: number) => {
    if (!text) return "";
    if (text.length <= maxLength) return text;
    return text.substring(0, maxLength) + "...";
  };

  // 컬럼 값을 문자열로 가져오기 (카테고리 타입인 경우 매핑된 라벨 반환)
  const getColumnValueAsString = (data: any, columnName?: string): string => {
    if (!columnName) return "";
    const value = data[columnName];
    if (value === null || value === undefined || value === "") return "";
    
    // 카테고리 타입인 경우 매핑된 라벨 반환
    const meta = columnMeta[columnName];
    if (meta?.inputType === "category") {
      const mapping = categoryMappings[columnName];
      const valueStr = String(value);
      const categoryData = mapping?.[valueStr];
      return categoryData?.label || valueStr;
    }
    
    return String(value);
  };
  
  // 컬럼 매핑에서 값 가져오기 (카테고리 타입인 경우 배지로 표시)
  const getColumnValue = (data: any, columnName?: string): React.ReactNode => {
    if (!columnName) return "";
    const value = data[columnName];
    if (value === null || value === undefined || value === "") return "";
    
    // 카테고리 타입인 경우 매핑된 라벨과 배지로 표시
    const meta = columnMeta[columnName];
    if (meta?.inputType === "category") {
      const mapping = categoryMappings[columnName];
      const valueStr = String(value);
      const categoryData = mapping?.[valueStr];
      const displayLabel = categoryData?.label || valueStr;
      const displayColor = categoryData?.color;
      
      // 색상이 없거나(null/undefined), 빈 문자열이거나, "none"이면 일반 텍스트로 표시 (배지 없음)
      if (!displayColor || displayColor === "none") {
        return displayLabel;
      }
      
      return (
        <Badge
          style={{
            backgroundColor: displayColor,
            borderColor: displayColor,
          }}
          className="text-white text-xs"
        >
          {displayLabel}
        </Badge>
      );
    }
    
    return String(value);
  };

  // 컬럼명을 라벨로 변환하는 헬퍼 함수
  const getColumnLabel = (columnName: string) => {
    if (!actualTableColumns || actualTableColumns.length === 0) {
      // 컬럼 정보가 없으면 컬럼명을 보기 좋게 변환
      return formatColumnName(columnName);
    }
    const column = actualTableColumns.find(
      (col) => col.columnName === columnName || col.column_name === columnName
    );
    // 다양한 라벨 필드명 지원 (displayName이 API에서 반환하는 라벨)
    const label = column?.displayName || column?.columnLabel || column?.column_label || column?.label;
    return label || formatColumnName(columnName);
  };

  // 컬럼명을 보기 좋은 형태로 변환 (snake_case -> 공백 구분)
  const formatColumnName = (columnName: string) => {
    // 언더스코어를 공백으로 변환하고 각 단어 첫 글자 대문자화
    return columnName
      .replace(/_/g, ' ')
      .replace(/\b\w/g, (char) => char.toUpperCase());
  };

  // 자동 폴백 로직 - 컬럼이 설정되지 않은 경우 적절한 기본값 찾기
  const getAutoFallbackValue = (data: any, type: "title" | "subtitle" | "description") => {
    const keys = Object.keys(data);
    switch (type) {
      case "title":
        // 이름 관련 필드 우선 검색
        return data.name || data.title || data.label || data[keys[0]] || "제목 없음";
      case "subtitle":
        // 직책, 부서, 카테고리 관련 필드 검색
        return data.position || data.role || data.department || data.category || data.type || "";
      case "description":
        // 설명, 내용 관련 필드 검색
        return data.description || data.content || data.summary || data.memo || "";
      default:
        return "";
    }
  };

  // 이벤트 핸들러
  const handleClick = (e: React.MouseEvent) => {
    e.stopPropagation();
    onClick?.();
  };

  // DOM 안전한 props만 필터링 (filterDOMProps 유틸리티 사용)
  const safeDomProps = filterDOMProps(props);

  return (
    <>
      <style jsx>{`
        .card-hover {
          transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
          position: relative;
          overflow: hidden;
        }
        .card-hover::before {
          content: '';
          position: absolute;
          top: 0;
          left: -100%;
          width: 100%;
          height: 100%;
          background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.4), transparent);
          transition: left 0.6s ease;
        }
        .card-hover:hover::before {
          left: 100%;
        }
        .card-hover:hover {
          transform: translateY(-2px);
          box-shadow:
            0 10px 15px -3px rgba(0, 0, 0, 0.1),
            0 4px 6px -2px rgba(0, 0, 0, 0.05);
          border-color: #3b82f6;
        }
        .card-container {
          position: relative;
        }
        .card-container::before {
          content: '';
          position: absolute;
          top: 0;
          left: 0;
          right: 0;
          bottom: 0;
          background: transparent;
          border-radius: 12px;
          pointer-events: none;
        }
      `}</style>
      <div
        className={className}
        style={{
          ...componentStyle,
          ...style,
        }}
        onClick={handleClick}
        onDragStart={onDragStart}
        onDragEnd={onDragEnd}
        {...safeDomProps}
      >
        <div style={containerStyle} className="card-container">
          {displayData.length === 0 ? (
            <div
              style={{
                gridColumn: "1 / -1",
                textAlign: "center",
                padding: "40px 20px",
                color: "hsl(var(--muted-foreground))",
                fontSize: "14px",
              }}
            >
              표시할 데이터가 없습니다.
            </div>
          ) : (
            displayData.map((data, index) => {
              // 타이틀, 서브타이틀, 설명 값 결정 (문자열로 가져와서 표시)
              const titleValue =
                getColumnValueAsString(data, componentConfig.columnMapping?.titleColumn) || getAutoFallbackValue(data, "title");

              const subtitleValue =
                getColumnValueAsString(data, componentConfig.columnMapping?.subtitleColumn) ||
                getAutoFallbackValue(data, "subtitle");

              const descriptionValue =
                getColumnValueAsString(data, componentConfig.columnMapping?.descriptionColumn) ||
                getAutoFallbackValue(data, "description");

              // 이미지 컬럼 자동 감지 (image_path, photo 등) - 대소문자 무시
              const imageColumn = componentConfig.columnMapping?.imageColumn || 
                Object.keys(data).find(key => {
                  const lowerKey = key.toLowerCase();
                  return lowerKey.includes('image') || lowerKey.includes('photo') || 
                         lowerKey.includes('avatar') || lowerKey.includes('thumbnail') ||
                         lowerKey.includes('picture') || lowerKey.includes('img');
                });
              
              // 이미지 값 가져오기 (직접 접근 + 폴백)
              const imageValue = imageColumn 
                ? data[imageColumn] 
                : (data.image_path || data.imagePath || data.avatar || data.image || data.photo || "");
              
              // 이미지 표시 여부 결정: 이미지 값이 있거나, 설정에서 활성화된 경우
              const shouldShowImage = componentConfig.cardStyle?.showImage !== false;
              
              // 이미지 URL 생성 (TableListComponent와 동일한 로직 사용)
              const imageUrl = imageValue ? getFullImageUrl(imageValue) : "";

              const cardKey = getCardKey(data, index);
              const isCardSelected = selectedRows.has(cardKey);

              return (
                <div
                  key={cardKey}
                  style={{
                    ...cardStyle,
                    borderColor: isCardSelected ? "#000" : "#e5e7eb",
                    borderWidth: isCardSelected ? "2px" : "1px",
                    boxShadow: isCardSelected 
                      ? "0 4px 6px -1px rgba(0, 0, 0, 0.15)" 
                      : "0 1px 3px rgba(0, 0, 0, 0.08)",
                    flexDirection: "row", // 가로 배치
                  }}
                  className="card-hover group cursor-pointer transition-all duration-150"
                  onClick={() => handleCardClick(data, index)}
                >
                  {/* 카드 이미지 - 좌측 전체 높이 (이미지 컬럼이 있으면 자동 표시) */}
                  {shouldShowImage && (
                    <div className="flex-shrink-0 flex items-center justify-center mr-4">
                      {imageUrl ? (
                        <img 
                          src={imageUrl} 
                          alt={titleValue || "이미지"}
                          className="h-16 w-16 rounded-lg object-cover border border-border"
                          onError={(e) => {
                            // 이미지 로드 실패 시 기본 아이콘으로 대체
                            e.currentTarget.src = "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='64' height='64'%3E%3Crect width='64' height='64' fill='%23e0e7ff' rx='8'/%3E%3Ctext x='32' y='40' text-anchor='middle' fill='%236366f1' font-size='24'%3E👤%3C/text%3E%3C/svg%3E";
                          }}
                        />
                      ) : (
                        <div className="flex h-16 w-16 items-center justify-center rounded-lg bg-primary/10">
                          <span className="text-2xl text-primary">👤</span>
                        </div>
                      )}
                    </div>
                  )}

                  {/* 우측 컨텐츠 영역 */}
                  <div className="flex flex-col flex-1 min-w-0">
                    {/* 타이틀 + 서브타이틀 */}
                    {(componentConfig.cardStyle?.showTitle || componentConfig.cardStyle?.showSubtitle) && (
                      <div className="mb-1 flex items-center gap-2 flex-wrap">
                        {componentConfig.cardStyle?.showTitle && (
                          <h3 className="text-base font-semibold text-foreground leading-tight">{titleValue}</h3>
                        )}
                        {componentConfig.cardStyle?.showSubtitle && subtitleValue && (
                          <span className="text-xs font-medium text-primary bg-primary/10 px-2 py-0.5 rounded-full">{subtitleValue}</span>
                        )}
                      </div>
                    )}

                    {/* 추가 표시 컬럼들 - 가로 배치 */}
                    {componentConfig.columnMapping?.displayColumns &&
                      componentConfig.columnMapping.displayColumns.length > 0 && (
                        <div className="flex flex-wrap items-center gap-x-3 gap-y-0.5 text-xs text-muted-foreground">
                          {componentConfig.columnMapping.displayColumns.map((columnName, idx) => {
                            const value = getColumnValue(data, columnName);
                            if (!value) return null;

                            return (
                              <div key={idx} className="flex items-center gap-1">
                                <span>{getColumnLabel(columnName)}:</span>
                                <span className="font-medium text-foreground">{value}</span>
                              </div>
                            );
                          })}
                        </div>
                      )}

                    {/* 카드 설명 */}
                    {componentConfig.cardStyle?.showDescription && descriptionValue && (
                      <div className="mt-1 flex-1">
                        <p className="text-xs text-muted-foreground leading-relaxed">
                          {truncateText(descriptionValue, componentConfig.cardStyle?.maxDescriptionLength || 100)}
                        </p>
                      </div>
                    )}

                    {/* 카드 액션 - 설정에 따라 표시 */}
                    {(componentConfig.cardStyle?.showActions ?? true) && (
                      <div className="mt-2 flex justify-end space-x-2">
                        {(componentConfig.cardStyle?.showViewButton ?? true) && (
                          <button 
                            className="text-xs text-primary hover:text-primary transition-colors"
                            onClick={(e) => {
                              e.stopPropagation();
                              handleCardView(data);
                            }}
                          >
                            상세보기
                          </button>
                        )}
                        {(componentConfig.cardStyle?.showEditButton ?? true) && (
                          <button 
                            className="text-xs text-muted-foreground hover:text-foreground transition-colors"
                            onClick={(e) => {
                              e.stopPropagation();
                              handleCardEdit(data);
                            }}
                          >
                            편집
                          </button>
                        )}
                        {(componentConfig.cardStyle?.showDeleteButton ?? false) && (
                          <button 
                            className="text-xs text-destructive hover:text-destructive transition-colors"
                            onClick={(e) => {
                              e.stopPropagation();
                              handleCardDelete(data, index);
                            }}
                          >
                            삭제
                          </button>
                        )}
                      </div>
                    )}
                  </div>
                </div>
              );
            })
          )}
        </div>
      </div>

      {/* 상세보기 모달 */}
      <Dialog open={viewModalOpen} onOpenChange={setViewModalOpen}>
        <DialogContent className="max-w-2xl max-h-[80vh] overflow-hidden">
          <DialogHeader>
            <DialogTitle className="flex items-center gap-2">
              <span className="text-lg">📋</span>
              상세 정보
            </DialogTitle>
          </DialogHeader>
          
          {selectedData && (
            <div className="space-y-4">
              <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
                {Object.entries(selectedData)
                  .filter(([key, value]) => value !== null && value !== undefined && value !== '')
                  .map(([key, value]) => {
                    // 카테고리 타입인 경우 배지로 표시
                    const meta = columnMeta[key];
                    let displayValue: React.ReactNode = String(value);
                    
                    if (meta?.inputType === "category") {
                      const mapping = categoryMappings[key];
                      const valueStr = String(value);
                      const categoryData = mapping?.[valueStr];
                      const displayLabel = categoryData?.label || valueStr;
                      const displayColor = categoryData?.color;
                      
                      // 색상이 있고 "none"이 아닌 경우에만 배지로 표시
                      if (displayColor && displayColor !== "none") {
                        displayValue = (
                          <Badge
                            style={{
                              backgroundColor: displayColor,
                              borderColor: displayColor,
                            }}
                            className="text-white"
                          >
                            {displayLabel}
                          </Badge>
                        );
                      } else {
                        // 배지 없음: 일반 텍스트로 표시
                        displayValue = displayLabel;
                      }
                    }
                    
                    return (
                      <div key={key} className="bg-muted rounded-lg p-3">
                        <div className="text-xs font-medium text-muted-foreground uppercase tracking-wide mb-1">
                          {getColumnLabel(key)}
                        </div>
                        <div className="text-sm font-medium text-foreground break-words">
                          {displayValue}
                        </div>
                      </div>
                    );
                  })
                }
              </div>
              
              <div className="flex justify-end pt-4 border-t">
                <button
                  onClick={() => setViewModalOpen(false)}
                  className="px-4 py-2 text-sm font-medium text-foreground bg-muted hover:bg-muted/80 rounded-md transition-colors"
                >
                  닫기
                </button>
              </div>
            </div>
          )}
        </DialogContent>
      </Dialog>

      {/* 편집 모달 */}
      <Dialog open={editModalOpen} onOpenChange={setEditModalOpen}>
        <DialogContent className="max-w-2xl max-h-[80vh] overflow-hidden">
          <DialogHeader>
            <DialogTitle className="flex items-center gap-2">
              <span className="text-lg">✏️</span>
              데이터 편집
            </DialogTitle>
          </DialogHeader>
          
          {editData && (
            <div className="space-y-4">
              <div className="grid grid-cols-1 gap-4">
                {Object.entries(editData)
                  .filter(([key, value]) => value !== null && value !== undefined)
                  .map(([key, value]) => (
                    <div key={key} className="space-y-2">
                      <label className="text-sm font-medium text-foreground block">
                        {key.replace(/_/g, ' ').toUpperCase()}
                      </label>
                      <Input
                        type="text"
                        value={String(value)}
                        onChange={(e) => handleEditFormChange(key, e.target.value)}
                        className="w-full"
                        placeholder={`${key} 입력`}
                      />
                    </div>
                  ))
                }
              </div>
              
              <div className="flex justify-end gap-2 pt-4 border-t">
                <Button
                  variant="outline"
                  onClick={() => {
                    setEditModalOpen(false);
                    setEditData(null);
                  }}
                >
                  취소
                </Button>
                <Button
                  onClick={handleEditSave}
                  className="bg-primary hover:bg-primary/90"
                >
                  저장
                </Button>
              </div>
            </div>
          )}
        </DialogContent>
      </Dialog>
    </>
  );
};
