"use client";

import React, { useState, useEffect, useCallback, useMemo } from "react";
import { DashboardElement, ChartDataSource, FieldGroup } from "@/components/admin/dashboard/types";
import { Button } from "@/components/ui/button";
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
import { Card } from "@/components/ui/card";
import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
} from "@/components/ui/dialog";
import { Loader2, RefreshCw, Truck, Clock, MapPin, Package, Info } from "lucide-react";
import { applyColumnMapping } from "@/lib/utils/columnMapping";
import { getApiUrl } from "@/lib/utils/apiUrl";

interface ListTestWidgetProps {
  element: DashboardElement;
}

interface QueryResult {
  columns: string[];
  rows: Record<string, any>[];
  totalRows: number;
  executionTime: number;
}

/**
 * 리스트 테스트 위젯 (다중 데이터 소스 지원)
 * - 여러 REST API 연결 가능
 * - 여러 Database 연결 가능
 * - REST API + Database 혼합 가능
 * - 데이터 자동 병합
 */
export function ListTestWidget({ element }: ListTestWidgetProps) {
  const [data, setData] = useState<QueryResult | null>(null);
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [currentPage, setCurrentPage] = useState(1);
  const [lastRefreshTime, setLastRefreshTime] = useState<Date | null>(null);

  // 행 상세 팝업 상태
  const [detailPopupOpen, setDetailPopupOpen] = useState(false);
  const [detailPopupData, setDetailPopupData] = useState<Record<string, any> | null>(null);
  const [detailPopupLoading, setDetailPopupLoading] = useState(false);
  const [additionalDetailData, setAdditionalDetailData] = useState<Record<string, any> | null>(null);

  // // console.log("🧪 ListTestWidget 렌더링!", element);

  const dataSources = useMemo(() => {
    // 다중 데이터 소스 우선
    const multiSources = element?.dataSources || element?.chartConfig?.dataSources;
    if (multiSources && multiSources.length > 0) {
      return multiSources;
    }
    
    // 단일 데이터 소스 fallback (배열로 변환)
    if (element?.dataSource) {
      return [element.dataSource];
    }
    
    return [];
  }, [element?.dataSources, element?.chartConfig?.dataSources, element?.dataSource]);

  // // console.log("📊 dataSources 확인:", {
  //   hasDataSources: !!dataSources,
  //   dataSourcesLength: dataSources?.length || 0,
  //   dataSources: dataSources,
  // });

  const config = element.listConfig || {
    columnMode: "auto",
    viewMode: "table",
    columns: [],
    pageSize: 10,
    enablePagination: true,
    showHeader: true,
    stripedRows: true,
    compactMode: false,
    cardColumns: 3,
  };

  // 행 클릭 핸들러 - 팝업 열기
  const handleRowClick = useCallback(
    async (row: Record<string, any>) => {
      // 팝업이 비활성화되어 있으면 무시
      if (!config.rowDetailPopup?.enabled) return;

      setDetailPopupData(row);
      setDetailPopupOpen(true);
      setAdditionalDetailData(null);
      setDetailPopupLoading(false);

      // 추가 데이터 조회 설정이 있으면 실행
      const additionalQuery = config.rowDetailPopup?.additionalQuery;
      if (additionalQuery?.enabled) {
        const queryMode = additionalQuery.queryMode || "table";
        
        // 커스텀 쿼리 모드
        if (queryMode === "custom" && additionalQuery.customQuery) {
          setDetailPopupLoading(true);
          try {
            // 쿼리에서 {컬럼명} 형태의 파라미터를 실제 값으로 치환
            let query = additionalQuery.customQuery;
            // console.log("🔍 [ListTestWidget] 커스텀 쿼리 파라미터 치환 시작");
            // console.log("🔍 [ListTestWidget] 클릭한 행 데이터:", row);
            // console.log("🔍 [ListTestWidget] 행 컬럼 목록:", Object.keys(row));
            
            Object.keys(row).forEach((key) => {
              const value = row[key];
              const placeholder = new RegExp(`\\{${key}\\}`, "g");
              // SQL 인젝션 방지를 위해 값 이스케이프
              const safeValue = typeof value === "string" 
                ? value.replace(/'/g, "''") 
                : value;
              query = query.replace(placeholder, String(safeValue ?? ""));
              // console.log(`🔍 [ListTestWidget] 치환: {${key}} → ${safeValue}`);
            });

            // console.log("🔍 [ListTestWidget] 최종 쿼리:", query);
            
            const { dashboardApi } = await import("@/lib/api/dashboard");
            const result = await dashboardApi.executeQuery(query);
            // console.log("🔍 [ListTestWidget] 쿼리 결과:", result);

            if (result.success && result.rows.length > 0) {
              setAdditionalDetailData(result.rows[0]);
            } else {
              setAdditionalDetailData({});
            }
          } catch (err) {
            console.error("커스텀 쿼리 실행 실패:", err);
            setAdditionalDetailData({});
          } finally {
            setDetailPopupLoading(false);
          }
        }
        // 테이블 조회 모드
        else if (queryMode === "table" && additionalQuery.tableName && additionalQuery.matchColumn) {
          const sourceColumn = additionalQuery.sourceColumn || additionalQuery.matchColumn;
          const matchValue = row[sourceColumn];

          if (matchValue !== undefined && matchValue !== null) {
            setDetailPopupLoading(true);
            try {
              const query = `
                SELECT *
                FROM ${additionalQuery.tableName}
                WHERE ${additionalQuery.matchColumn} = '${matchValue}'
                LIMIT 1;
              `;

              const { dashboardApi } = await import("@/lib/api/dashboard");
              const result = await dashboardApi.executeQuery(query);

              if (result.success && result.rows.length > 0) {
                setAdditionalDetailData(result.rows[0]);
              } else {
                setAdditionalDetailData({});
              }
            } catch (err) {
              console.error("추가 데이터 로드 실패:", err);
              setAdditionalDetailData({});
            } finally {
              setDetailPopupLoading(false);
            }
          }
        }
      }
    },
    [config.rowDetailPopup],
  );

  // 값 포맷팅 함수
  const formatValue = (value: any, format?: string): string => {
    if (value === null || value === undefined) return "-";

    switch (format) {
      case "date":
        try {
          const dateVal = new Date(value);
          return dateVal.toLocaleDateString("ko-KR", { timeZone: "Asia/Seoul" });
        } catch {
          return String(value);
        }
      case "datetime":
        try {
          const dateVal = new Date(value);
          return dateVal.toLocaleString("ko-KR", { timeZone: "Asia/Seoul" });
        } catch {
          return String(value);
        }
      case "number":
        return Number(value).toLocaleString("ko-KR");
      case "currency":
        return `${Number(value).toLocaleString("ko-KR")}원`;
      case "boolean":
        return value ? "예" : "아니오";
      case "distance":
        return typeof value === "number" ? `${value.toFixed(1)} km` : String(value);
      case "duration":
        return typeof value === "number" ? `${value}분` : String(value);
      default:
        return String(value);
    }
  };

  // 아이콘 렌더링
  const renderIcon = (icon?: string, color?: string) => {
    const colorClass =
      color === "blue"
        ? "text-primary"
        : color === "orange"
          ? "text-amber-600"
          : color === "green"
            ? "text-emerald-600"
            : color === "red"
              ? "text-destructive"
              : color === "purple"
                ? "text-purple-600"
                : "text-muted-foreground";

    switch (icon) {
      case "truck":
        return <Truck className={`h-4 w-4 ${colorClass}`} />;
      case "clock":
        return <Clock className={`h-4 w-4 ${colorClass}`} />;
      case "map":
        return <MapPin className={`h-4 w-4 ${colorClass}`} />;
      case "package":
        return <Package className={`h-4 w-4 ${colorClass}`} />;
      default:
        return <Info className={`h-4 w-4 ${colorClass}`} />;
    }
  };

  // 필드 그룹 렌더링
  const renderFieldGroup = (group: FieldGroup, groupData: Record<string, any>) => {
    const colorClass =
      group.color === "blue"
        ? "text-primary"
        : group.color === "orange"
          ? "text-amber-600"
          : group.color === "green"
            ? "text-emerald-600"
            : group.color === "red"
              ? "text-destructive"
              : group.color === "purple"
                ? "text-purple-600"
                : "text-muted-foreground";

    return (
      <div key={group.id} className="rounded-lg border p-4">
        <div className={`mb-3 flex items-center gap-2 text-sm font-semibold ${colorClass}`}>
          {renderIcon(group.icon, group.color)}
          {group.title}
        </div>
        <div className="grid grid-cols-1 gap-3 text-xs sm:grid-cols-2">
          {group.fields.map((field) => (
            <div key={field.column} className="flex flex-col gap-0.5">
              <span className="text-muted-foreground text-[10px] font-medium uppercase tracking-wide">
                {field.label}
              </span>
              <span className="font-medium break-words">{formatValue(groupData[field.column], field.format)}</span>
            </div>
          ))}
        </div>
      </div>
    );
  };

  // 기본 필드 그룹 생성 (설정이 없을 경우)
  const getDefaultFieldGroups = (row: Record<string, any>, additional: Record<string, any> | null): FieldGroup[] => {
    const groups: FieldGroup[] = [];
    const displayColumns = config.rowDetailPopup?.additionalQuery?.displayColumns;
    const queryMode = config.rowDetailPopup?.additionalQuery?.queryMode || "table";

    // 커스텀 쿼리 모드일 때는 additional 데이터를 우선 사용
    // row와 additional을 병합하되, 커스텀 쿼리 결과(additional)가 우선
    const mergedData = queryMode === "custom" && additional && Object.keys(additional).length > 0
      ? { ...row, ...additional }  // additional이 row를 덮어씀
      : row;

    // 기본 정보 그룹 - displayColumns가 있으면 해당 컬럼만, 없으면 전체
    const allKeys = Object.keys(mergedData).filter((key) => !key.startsWith("_")); // _source 등 내부 필드 제외
    let basicFields: { column: string; label: string }[] = [];
    
    if (displayColumns && displayColumns.length > 0) {
      // DisplayColumnConfig 형식 지원
      // 커스텀 쿼리 모드일 때는 mergedData에서 컬럼 확인
      basicFields = displayColumns
        .map((colConfig) => {
          const column = typeof colConfig === 'object' ? colConfig.column : colConfig;
          const label = typeof colConfig === 'object' ? colConfig.label : colConfig;
          return { column, label };
        })
        .filter((item) => allKeys.includes(item.column));
    } else {
      // 전체 컬럼 - 커스텀 쿼리 모드일 때는 additional 컬럼만 표시
      if (queryMode === "custom" && additional && Object.keys(additional).length > 0) {
        basicFields = Object.keys(additional)
          .filter((key) => !key.startsWith("_"))
          .map((key) => ({ column: key, label: key }));
      } else {
        basicFields = allKeys.map((key) => ({ column: key, label: key }));
      }
    }

    groups.push({
      id: "basic",
      title: "기본 정보",
      icon: "info",
      color: "gray",
      fields: basicFields.map((item) => ({
        column: item.column,
        label: item.label,
        format: "text" as const,
      })),
    });

    // 추가 데이터가 있고 vehicles 테이블인 경우 운행/공차 정보 추가 (테이블 모드일 때만)
    if (queryMode === "table" && additional && Object.keys(additional).length > 0) {
      // 운행 정보
      if (additional.last_trip_start || additional.last_trip_end) {
        groups.push({
          id: "trip",
          title: "운행 정보",
          icon: "truck",
          color: "blue",
          fields: [
            { column: "last_trip_start", label: "시작", format: "datetime" as const },
            { column: "last_trip_end", label: "종료", format: "datetime" as const },
            { column: "last_trip_distance", label: "거리", format: "distance" as const },
            { column: "last_trip_time", label: "시간", format: "duration" as const },
            { column: "departure", label: "출발지", format: "text" as const },
            { column: "arrival", label: "도착지", format: "text" as const },
          ],
        });
      }

      // 공차 정보
      if (additional.last_empty_start) {
        groups.push({
          id: "empty",
          title: "공차 정보",
          icon: "package",
          color: "orange",
          fields: [
            { column: "last_empty_start", label: "시작", format: "datetime" as const },
            { column: "last_empty_end", label: "종료", format: "datetime" as const },
            { column: "last_empty_distance", label: "거리", format: "distance" as const },
            { column: "last_empty_time", label: "시간", format: "duration" as const },
          ],
        });
      }
    }

    return groups;
  };

  // visible 컬럼 설정 객체 배열 (field + label)
  const visibleColumnConfigs = useMemo(() => {
    if (config.columns && config.columns.length > 0 && typeof config.columns[0] === "object") {
      return config.columns.filter((col: any) => col.visible !== false);
    }
    return [];
  }, [config.columns]);

  // 표시할 컬럼 필드명 (데이터 접근용)
  const displayColumns = useMemo(() => {
    if (!data?.columns) return [];
    
    // 컬럼 설정이 있으면 field 사용
    if (visibleColumnConfigs.length > 0) {
      return visibleColumnConfigs.map((col: any) => col.field);
    }
    
    // 자동 모드: 모든 컬럼 표시
    return data.columns;
  }, [data?.columns, visibleColumnConfigs]);

  // 다중 데이터 소스 로딩
  const loadMultipleDataSources = useCallback(async () => {
    console.log("[ListTestWidget] dataSources:", dataSources);
    
    if (!dataSources || dataSources.length === 0) {
      console.log("[ListTestWidget] 데이터 소스가 없습니다.");
      return;
    }

    console.log(`[ListTestWidget] ${dataSources.length}개의 데이터 소스 로딩 시작...`, dataSources[0]);
    setIsLoading(true);
    setError(null);

    try {
      // 모든 데이터 소스를 병렬로 로딩
      const results = await Promise.allSettled(
        dataSources.map(async (source) => {
          try {
            // console.log(`📡 데이터 소스 "${source.name || source.id}" 로딩 중...`);
            
            if (source.type === "api") {
              return await loadRestApiData(source);
            } else if (source.type === "database") {
              return await loadDatabaseData(source);
            }
            
            return { columns: [], rows: [] };
          } catch (err: any) {
            console.error(`❌ 데이터 소스 "${source.name || source.id}" 로딩 실패:`, err);
            return { columns: [], rows: [] };
          }
        })
      );

      // 성공한 데이터만 병합
      const allColumns = new Set<string>();
      const allRows: Record<string, any>[] = [];

      results.forEach((result, index) => {
        if (result.status === "fulfilled") {
          const { columns, rows } = result.value;
          
          // 컬럼 수집
          columns.forEach((col: string) => allColumns.add(col));
          
          // 행 병합 (소스 정보 추가)
          const sourceName = dataSources[index].name || dataSources[index].id || `소스 ${index + 1}`;
          rows.forEach((row: any) => {
            allRows.push({
              ...row,
              _source: sourceName,
            });
          });
        }
      });

      const finalColumns = Array.from(allColumns);
      
      // _source 컬럼을 맨 앞으로
      const sortedColumns = finalColumns.includes("_source")
        ? ["_source", ...finalColumns.filter((c) => c !== "_source")]
        : finalColumns;

      setData({
        columns: sortedColumns,
        rows: allRows,
        totalRows: allRows.length,
        executionTime: 0,
      });
      setLastRefreshTime(new Date());

      // console.log(`✅ 총 ${allRows.length}개의 행 로딩 완료`);
    } catch (err) {
      setError(err instanceof Error ? err.message : "데이터 로딩 실패");
    } finally {
      setIsLoading(false);
    }
  }, [dataSources]);

  // 수동 새로고침 핸들러
  const handleManualRefresh = useCallback(() => {
    // console.log("🔄 수동 새로고침 버튼 클릭");
    loadMultipleDataSources();
  }, [loadMultipleDataSources]);

  // REST API 데이터 로딩
  const loadRestApiData = async (source: ChartDataSource): Promise<{ columns: string[]; rows: any[] }> => {
    if (!source.endpoint) {
      throw new Error("API endpoint가 없습니다.");
    }

    const params = new URLSearchParams();
    if (source.queryParams) {
      Object.entries(source.queryParams).forEach(([key, value]) => {
        if (key && value) {
          params.append(key, String(value));
        }
      });
    }

    // 요청 메서드 (기본값: GET)
    const requestMethod = source.method || "GET";
    
    // 요청 body (POST, PUT, PATCH인 경우)
    let requestBody = undefined;
    if (["POST", "PUT", "PATCH"].includes(requestMethod) && source.body) {
      try {
        // body가 문자열이면 JSON 파싱 시도
        requestBody = typeof source.body === "string" ? JSON.parse(source.body) : source.body;
      } catch {
        // 파싱 실패하면 문자열 그대로 사용
        requestBody = source.body;
      }
    }

    // headers를 KeyValuePair[] 에서 객체로 변환
    const headersObj: Record<string, string> = {};
    if (source.headers && Array.isArray(source.headers)) {
      source.headers.forEach((h: any) => {
        if (h.key && h.value) {
          headersObj[h.key] = h.value;
        }
      });
    } else if (source.headers && typeof source.headers === "object") {
      // 이미 객체인 경우 그대로 사용
      Object.assign(headersObj, source.headers);
    }

    const requestPayload = {
      url: source.endpoint,
      method: requestMethod,
      headers: headersObj,
      queryParams: Object.fromEntries(params),
      body: requestBody,
      externalConnectionId: source.externalConnectionId,
    };
    
    console.log("[ListTestWidget] API 요청:", requestPayload);

    const response = await fetch(getApiUrl("/api/dashboards/fetch-external-api"), {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      credentials: "include",
      body: JSON.stringify(requestPayload),
    });

    if (!response.ok) {
      const errorText = await response.text();
      console.error("❌ API 호출 실패:", {
        status: response.status,
        statusText: response.statusText,
        body: errorText.substring(0, 500),
      });
      throw new Error(`HTTP ${response.status}: ${response.statusText}`);
    }

    const result = await response.json();
    // console.log("✅ API 응답:", result);

    if (!result.success) {
      console.error("❌ API 실패:", result);
      throw new Error(result.message || result.error || "외부 API 호출 실패");
    }

    let processedData = result.data;

    // JSON Path 처리
    if (source.jsonPath) {
      const paths = source.jsonPath.split(".");
      for (const path of paths) {
        if (processedData && typeof processedData === "object" && path in processedData) {
          processedData = processedData[path];
        } else {
          throw new Error(`JSON Path "${source.jsonPath}"에서 데이터를 찾을 수 없습니다`);
        }
      }
    }

    let rows = Array.isArray(processedData) ? processedData : [processedData];
    
    // 컬럼 매핑 적용
    rows = applyColumnMapping(rows, source.columnMapping);
    
    const columns = rows.length > 0 ? Object.keys(rows[0]) : [];

    return { columns, rows };
  };

  // Database 데이터 로딩
  const loadDatabaseData = async (source: ChartDataSource): Promise<{ columns: string[]; rows: any[] }> => {
    if (!source.query) {
      throw new Error("SQL 쿼리가 없습니다.");
    }

    if (source.connectionType === "external" && source.externalConnectionId) {
      // 외부 DB
      const { ExternalDbConnectionAPI } = await import("@/lib/api/externalDbConnection");
      const externalResult = await ExternalDbConnectionAPI.executeQuery(
        parseInt(source.externalConnectionId),
        source.query,
      );
      
      if (!externalResult.success || !externalResult.data) {
        throw new Error(externalResult.message || "외부 DB 쿼리 실행 실패");
      }

      const resultData = externalResult.data as unknown as {
        columns: string[];
        rows: Record<string, unknown>[];
      };
      
      // 컬럼 매핑 적용
      const mappedRows = applyColumnMapping(resultData.rows, source.columnMapping);
      const columns = mappedRows.length > 0 ? Object.keys(mappedRows[0]) : resultData.columns;
      
      return {
        columns,
        rows: mappedRows,
      };
    } else {
      // 현재 DB
      const { dashboardApi } = await import("@/lib/api/dashboard");
      const result = await dashboardApi.executeQuery(source.query);
      
      // // console.log("💾 내부 DB 쿼리 결과:", {
      //   hasRows: !!result.rows,
      //   rowCount: result.rows?.length || 0,
      //   hasColumns: !!result.columns,
      //   columnCount: result.columns?.length || 0,
      //   firstRow: result.rows?.[0],
      //   resultKeys: Object.keys(result),
      // });
      
      // 컬럼 매핑 적용
      const mappedRows = applyColumnMapping(result.rows, source.columnMapping);
      const columns = mappedRows.length > 0 ? Object.keys(mappedRows[0]) : result.columns;
      
      // // console.log("✅ 매핑 후:", {
      //   columns,
      //   rowCount: mappedRows.length,
      //   firstMappedRow: mappedRows[0],
      // });
      
      return {
        columns,
        rows: mappedRows,
      };
    }
  };

  // 초기 로드
  useEffect(() => {
    if (dataSources && dataSources.length > 0) {
      loadMultipleDataSources();
    }
  }, [dataSources, loadMultipleDataSources]);

  // 자동 새로고침
  useEffect(() => {
    if (!dataSources || dataSources.length === 0) return;

    const intervals = dataSources
      .map((ds) => ds.refreshInterval)
      .filter((interval): interval is number => typeof interval === "number" && interval > 0);

    if (intervals.length === 0) return;

    const minInterval = Math.min(...intervals);
    // console.log(`⏱️ 자동 새로고침 설정: ${minInterval}초마다`);

    const intervalId = setInterval(() => {
      // console.log("🔄 자동 새로고침 실행");
      loadMultipleDataSources();
    }, minInterval * 1000);

    return () => {
      // console.log("⏹️ 자동 새로고침 정리");
      clearInterval(intervalId);
    };
  }, [dataSources, loadMultipleDataSources]);

  // 페이지네이션
  const pageSize = config.pageSize || 10;
  const totalPages = data ? Math.ceil(data.totalRows / pageSize) : 0;
  const startIndex = (currentPage - 1) * pageSize;
  const endIndex = startIndex + pageSize;
  const paginatedRows = data?.rows.slice(startIndex, endIndex) || [];

  // 테이블 뷰
  const renderTable = () => {
    // 헤더명 가져오기 (label 우선, 없으면 field 그대로)
    const getHeaderLabel = (field: string) => {
      const colConfig = visibleColumnConfigs.find((col: any) => col.field === field);
      return colConfig?.label || field;
    };

    return (
      <div className="overflow-auto">
        <Table>
          {config.showHeader && (
            <TableHeader>
              <TableRow>
                {displayColumns.map((field) => (
                  <TableHead key={field} className="whitespace-nowrap">
                    {getHeaderLabel(field)}
                  </TableHead>
                ))}
              </TableRow>
            </TableHeader>
          )}
          <TableBody>
            {paginatedRows.map((row, idx) => (
              <TableRow
                key={idx}
                className={`${config.stripedRows && idx % 2 === 0 ? "bg-muted/50" : ""} ${config.rowDetailPopup?.enabled ? "cursor-pointer transition-colors hover:bg-accent" : ""}`}
                onClick={() => handleRowClick(row)}
              >
                {displayColumns.map((field) => (
                  <TableCell key={field} className="whitespace-nowrap">
                    {String(row[field] ?? "")}
                  </TableCell>
                ))}
              </TableRow>
            ))}
          </TableBody>
        </Table>
      </div>
    );
  };

  // 카드 뷰
  const renderCards = () => {
    // 헤더명 가져오기 (label 우선, 없으면 field 그대로)
    const getLabel = (field: string) => {
      const colConfig = visibleColumnConfigs.find((col: any) => col.field === field);
      return colConfig?.label || field;
    };

    return (
      <div className={`grid gap-4 grid-cols-1 md:grid-cols-${config.cardColumns || 3}`}>
        {paginatedRows.map((row, idx) => (
          <Card
            key={idx}
            className={`p-4 ${config.rowDetailPopup?.enabled ? "cursor-pointer transition-shadow hover:shadow-md" : ""}`}
            onClick={() => handleRowClick(row)}
          >
            {displayColumns.map((field) => (
              <div key={field} className="mb-2">
                <span className="font-semibold">{getLabel(field)}: </span>
                <span>{String(row[field] ?? "")}</span>
              </div>
            ))}
          </Card>
        ))}
      </div>
    );
  };

  return (
      <div className="flex h-full flex-col bg-card shadow-sm">
      {/* 헤더 */}
      <div className="flex items-center justify-between border-b p-4">
        <div>
          <h3 className="text-lg font-semibold">
            {element?.customTitle || "리스트"}
          </h3>
          <p className="text-xs text-muted-foreground">
            {dataSources?.length || 0}개 데이터 소스 • {data?.totalRows || 0}개 행
            {lastRefreshTime && (
              <span className="ml-2">
                • {lastRefreshTime.toLocaleTimeString("ko-KR")}
              </span>
            )}
          </p>
        </div>
        <div className="flex items-center gap-2">
          <Button
            variant="outline"
            size="sm"
            onClick={handleManualRefresh}
            disabled={isLoading}
            className="h-8 gap-2 text-xs"
          >
            <RefreshCw className={`h-3 w-3 ${isLoading ? "animate-spin" : ""}`} />
            새로고침
          </Button>
          {isLoading && <Loader2 className="h-4 w-4 animate-spin" />}
        </div>
      </div>

      {/* 컨텐츠 */}
      <div className="flex-1 overflow-auto p-4">
        {error ? (
          <div className="flex h-full items-center justify-center">
            <p className="text-sm text-destructive">{error}</p>
          </div>
        ) : !dataSources || dataSources.length === 0 ? (
          <div className="flex h-full items-center justify-center">
            <p className="text-sm text-muted-foreground">
              데이터 소스를 연결해주세요
            </p>
          </div>
        ) : !data || data.rows.length === 0 ? (
          <div className="flex h-full items-center justify-center">
            <p className="text-sm text-muted-foreground">
              데이터가 없습니다
            </p>
          </div>
        ) : config.viewMode === "card" ? (
          renderCards()
        ) : (
          renderTable()
        )}
      </div>

      {/* 페이지네이션 */}
      {config.enablePagination && data && data.rows.length > 0 && totalPages > 1 && (
        <div className="flex items-center justify-between border-t p-4">
          <div className="text-sm text-muted-foreground">
            총 {data.totalRows}개 항목 (페이지 {currentPage}/{totalPages})
          </div>
          <div className="flex gap-2">
            <Button
              variant="outline"
              size="sm"
              onClick={() => setCurrentPage((p) => Math.max(1, p - 1))}
              disabled={currentPage === 1}
            >
              이전
            </Button>
            <Button
              variant="outline"
              size="sm"
              onClick={() => setCurrentPage((p) => Math.min(totalPages, p + 1))}
              disabled={currentPage === totalPages}
            >
              다음
            </Button>
          </div>
        </div>
      )}

      {/* 행 상세 팝업 */}
      <Dialog open={detailPopupOpen} onOpenChange={setDetailPopupOpen}>
        <DialogContent className="max-h-[90vh] max-w-[600px] overflow-y-auto">
          <DialogHeader>
            <DialogTitle>{config.rowDetailPopup?.title || "상세 정보"}</DialogTitle>
            <DialogDescription>
              {detailPopupLoading 
                ? "추가 정보를 로딩 중입니다..." 
                : detailPopupData 
                  ? `${Object.values(detailPopupData).filter(v => v && typeof v === 'string').slice(0, 2).join(' - ')}` 
                  : "선택된 항목의 상세 정보입니다."}
            </DialogDescription>
          </DialogHeader>

          {detailPopupLoading ? (
            <div className="flex items-center justify-center py-8">
              <div className="border-primary mx-auto mb-2 h-6 w-6 animate-spin rounded-full border-2 border-t-transparent" />
            </div>
          ) : (
            <div className="space-y-4">
              {detailPopupData && (
                <>
                  {/* 설정된 필드 그룹이 있으면 사용, 없으면 기본 그룹 생성 */}
                  {config.rowDetailPopup?.fieldGroups && config.rowDetailPopup.fieldGroups.length > 0
                    ? // 설정된 필드 그룹 렌더링
                      config.rowDetailPopup.fieldGroups.map((group) =>
                        renderFieldGroup(group, { ...detailPopupData, ...additionalDetailData }),
                      )
                    : // 기본 필드 그룹 렌더링
                      getDefaultFieldGroups(detailPopupData, additionalDetailData).map((group) =>
                        renderFieldGroup(group, { ...detailPopupData, ...additionalDetailData }),
                      )}
                </>
              )}
            </div>
          )}

          <DialogFooter>
            <Button onClick={() => setDetailPopupOpen(false)}>닫기</Button>
          </DialogFooter>
        </DialogContent>
      </Dialog>
    </div>
  );
}

