"use client";

/**
 * pop-string-list 런타임 컴포넌트
 *
 * 리스트 모드: 엑셀형 행/열 (CSS Grid)
 * 카드 모드: 셀 병합 가능한 카드 (CSS Grid + colSpan/rowSpan)
 * 오버플로우: visibleRows 제한 + "더보기" 점진 확장
 */

import { useState, useEffect, useCallback, useMemo } from "react";
import { ChevronDown, ChevronUp, ChevronLeft, ChevronRight, Loader2, AlertCircle, ChevronsUpDown } from "lucide-react";
import { Button } from "@/components/ui/button";
import {
  Popover,
  PopoverContent,
  PopoverTrigger,
} from "@/components/ui/popover";
import { cn } from "@/lib/utils";
import { dataApi } from "@/lib/api/data";
import { executePopAction } from "@/hooks/pop/executePopAction";
import { usePopEvent } from "@/hooks/pop/usePopEvent";
import { toast } from "sonner";
import { showErrorToast } from "@/lib/utils/toastUtils";
import type {
  PopStringListConfig,
  CardGridConfig,
  ListColumnConfig,
  CardCellDefinition,
} from "./types";

// ===== 유틸리티 =====

/**
 * 컬럼명에서 실제 데이터 키를 추출
 * 조인 컬럼은 "테이블명.컬럼명" 형식으로 저장됨 -> "컬럼명"만 추출
 * 일반 컬럼은 그대로 반환
 */
function resolveColumnName(name: string): string {
  if (!name) return name;
  const dotIdx = name.lastIndexOf(".");
  return dotIdx >= 0 ? name.substring(dotIdx + 1) : name;
}

// ===== Props =====

interface PopStringListComponentProps {
  config?: PopStringListConfig;
  className?: string;
  screenId?: string;
  componentId?: string;
}

// 테이블 행 데이터 타입
type RowData = Record<string, unknown>;

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

export function PopStringListComponent({
  config,
  className,
  screenId,
  componentId,
}: PopStringListComponentProps) {
  const displayMode = config?.displayMode || "list";
  const header = config?.header;
  const overflow = config?.overflow;
  const dataSource = config?.dataSource;
  const listColumns = config?.listColumns || [];
  const cardGrid = config?.cardGrid;
  const rowClickAction = config?.rowClickAction || "none";

  // 데이터 상태
  const [rows, setRows] = useState<RowData[]>([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);
  // 더보기 모드: 현재 표시 중인 행 수
  const [displayCount, setDisplayCount] = useState<number>(0);
  // 페이지네이션 모드: 현재 페이지 (1부터 시작)
  const [currentPage, setCurrentPage] = useState(1);

  // 카드 버튼 행 단위 로딩 인덱스 (-1 = 로딩 없음)
  const [loadingRowIdx, setLoadingRowIdx] = useState<number>(-1);

  // 이벤트 버스
  const { publish, subscribe } = usePopEvent(screenId || "");

  // 외부 필터 조건 (연결 시스템에서 수신, connectionId별 Map으로 복수 필터 AND 결합)
  const [externalFilters, setExternalFilters] = useState<
    Map<string, {
      fieldName: string;
      value: unknown;
      filterConfig?: { targetColumn: string; targetColumns?: string[]; filterMode: string };
    }>
  >(new Map());

  // 표준 입력 이벤트 구독
  useEffect(() => {
    if (!componentId) return;
    const unsub = subscribe(
      `__comp_input__${componentId}__filter_condition`,
      (payload: unknown) => {
        const data = payload as {
          value?: { fieldName?: string; value?: unknown };
          filterConfig?: { targetColumn: string; targetColumns?: string[]; filterMode: string };
          _connectionId?: string;
        };
        const connId = data?._connectionId || "default";
        setExternalFilters(prev => {
          const next = new Map(prev);
          if (data?.value?.value) {
            next.set(connId, {
              fieldName: data.value.fieldName || "",
              value: data.value.value,
              filterConfig: data.filterConfig,
            });
          } else {
            next.delete(connId);
          }
          return next;
        });
      }
    );
    return unsub;
  }, [componentId, subscribe]);

  // 카드 버튼 클릭 핸들러
  const handleCardButtonClick = useCallback(
    async (cell: CardCellDefinition, row: RowData) => {
      if (!cell.buttonAction) return;

      // 확인 다이얼로그 (간단 구현: window.confirm)
      if (cell.buttonConfirm?.enabled) {
        const msg = cell.buttonConfirm.message || "이 작업을 실행하시겠습니까?";
        if (!window.confirm(msg)) return;
      }

      const rowIndex = rows.indexOf(row);
      setLoadingRowIdx(rowIndex);

      try {
        const result = await executePopAction(cell.buttonAction, row as Record<string, unknown>, {
          publish,
          screenId,
        });

        if (result.success) {
          toast.success("작업이 완료되었습니다.");
        } else {
          showErrorToast("작업에 실패했습니다", result.error, { guidance: "잠시 후 다시 시도해 주세요." });
        }
      } catch {
        showErrorToast("예기치 않은 오류가 발생했습니다", null, { guidance: "잠시 후 다시 시도해 주세요." });
      } finally {
        setLoadingRowIdx(-1);
      }
    },
    [rows, publish, screenId]
  );

  // 행 클릭 핸들러 (selected_row 발행 + 모달 닫기 옵션)
  const handleRowClick = useCallback(
    (row: RowData) => {
      if (rowClickAction === "none") return;

      // selected_row 이벤트 발행
      if (componentId) {
        publish(`__comp_output__${componentId}__selected_row`, row);
      }

      // 모달 내부에서 사용 시: 선택 후 모달 닫기 + 데이터 반환
      if (rowClickAction === "select-and-close-modal") {
        publish("__pop_modal_close__", { selectedRow: row });
      }
    },
    [rowClickAction, componentId, publish]
  );

  // 오버플로우 설정 (JSON 복원 시 string 유입 방어)
  const overflowMode = overflow?.mode || "loadMore";
  const visibleRows = Number(overflow?.visibleRows) || 5;
  const loadMoreCount = Number(overflow?.loadMoreCount) || 5;
  const maxExpandRows = Number(overflow?.maxExpandRows) || 50;
  const showExpandButton = overflow?.showExpandButton ?? true;
  const pageSize = Number(overflow?.pageSize) || visibleRows;
  const paginationStyle = overflow?.paginationStyle || "bottom";

  // --- 외부 필터 적용 (복수 필터 AND 결합) ---
  const filteredRows = useMemo(() => {
    if (externalFilters.size === 0) return rows;

    const matchSingleFilter = (
      row: RowData,
      filter: { fieldName: string; value: unknown; filterConfig?: { targetColumn: string; targetColumns?: string[]; filterMode: string } }
    ): boolean => {
      const searchValue = String(filter.value).toLowerCase();
      if (!searchValue) return true;

      const fc = filter.filterConfig;
      const columns: string[] =
        fc?.targetColumns?.length
          ? fc.targetColumns
          : fc?.targetColumn
            ? [fc.targetColumn]
            : filter.fieldName
              ? [filter.fieldName]
              : [];

      if (columns.length === 0) return true;

      const mode = fc?.filterMode || "contains";

      const matchCell = (cellValue: string) => {
        switch (mode) {
          case "equals":
            return cellValue === searchValue;
          case "starts_with":
            return cellValue.startsWith(searchValue);
          case "contains":
          default:
            return cellValue.includes(searchValue);
        }
      };

      return columns.some((col) => matchCell(String(row[col] ?? "").toLowerCase()));
    };

    const allFilters = [...externalFilters.values()];
    return rows.filter((row) => allFilters.every((f) => matchSingleFilter(row, f)));
  }, [rows, externalFilters]);

  // --- 더보기 모드 ---
  useEffect(() => {
    setDisplayCount(visibleRows);
  }, [visibleRows]);

  const effectiveLimit = Math.min(displayCount || visibleRows, maxExpandRows, filteredRows.length);
  const hasMore = showExpandButton && filteredRows.length > effectiveLimit && effectiveLimit < maxExpandRows;
  const isExpanded = effectiveLimit > visibleRows;

  const handleLoadMore = useCallback(() => {
    setDisplayCount((prev) => {
      const current = prev || visibleRows;
      return Math.min(current + loadMoreCount, maxExpandRows, filteredRows.length);
    });
  }, [visibleRows, loadMoreCount, maxExpandRows, filteredRows.length]);

  const handleCollapse = useCallback(() => {
    setDisplayCount(visibleRows);
  }, [visibleRows]);

  // --- 페이지네이션 모드 ---
  const totalPages = Math.max(1, Math.ceil(filteredRows.length / pageSize));

  useEffect(() => {
    setCurrentPage(1);
  }, [pageSize, filteredRows.length]);

  const handlePageChange = useCallback((page: number) => {
    setCurrentPage(Math.max(1, Math.min(page, totalPages)));
  }, [totalPages]);

  // --- 모드별 visibleData 결정 ---
  const visibleData = useMemo(() => {
    if (overflowMode === "pagination") {
      const start = (currentPage - 1) * pageSize;
      return filteredRows.slice(start, start + pageSize);
    }
    return filteredRows.slice(0, effectiveLimit);
  }, [overflowMode, filteredRows, currentPage, pageSize, effectiveLimit]);

  // dataSource 원시값 추출 (객체 참조 대신 안정적인 의존성 사용)
  const dsTableName = dataSource?.tableName;
  const dsSortColumn = dataSource?.sort?.column;
  const dsSortDirection = dataSource?.sort?.direction;
  const dsLimitMode = dataSource?.limit?.mode;
  const dsLimitCount = dataSource?.limit?.count;
  const dsFiltersKey = useMemo(
    () => JSON.stringify(dataSource?.filters || []),
    [dataSource?.filters]
  );

  // 데이터 조회
  useEffect(() => {
    if (!dsTableName) {
      setLoading(false);
      setRows([]);
      return;
    }

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

      try {
        // 필터 조건 구성 (설정 패널 고정 필터 + 외부 검색 필터)
        const filters: Record<string, unknown> = {};
        const parsedFilters = JSON.parse(dsFiltersKey) as Array<{ column?: string; value?: string }>;
        if (parsedFilters.length > 0) {
          parsedFilters.forEach((f) => {
            if (f.column && f.value) {
              filters[f.column] = f.value;
            }
          });
        }

        // 정렬 조건
        const sortBy = dsSortColumn;
        const sortOrder = dsSortDirection;

        // 개수 제한 (string 유입 방어: Number 캐스팅)
        const size =
          dsLimitMode === "limited" && dsLimitCount
            ? Number(dsLimitCount)
            : maxExpandRows;

        const result = await dataApi.getTableData(dsTableName, {
          page: 1,
          size,
          sortBy: sortOrder ? sortBy : undefined,
          sortOrder,
          filters: Object.keys(filters).length > 0 ? filters : undefined,
        });

        setRows(result.data || []);
      } catch (err) {
        const message =
          err instanceof Error ? err.message : "데이터 조회 실패";
        setError(message);
        setRows([]);
      } finally {
        setLoading(false);
      }
    };

    fetchData();
  }, [dsTableName, dsSortColumn, dsSortDirection, dsLimitMode, dsLimitCount, dsFiltersKey, maxExpandRows]);

  // 로딩 상태
  if (loading) {
    return (
      <div className="flex h-full w-full items-center justify-center">
        <Loader2 className="h-5 w-5 animate-spin text-muted-foreground" />
      </div>
    );
  }

  // 에러 상태
  if (error) {
    return (
      <div className="flex h-full w-full flex-col items-center justify-center gap-1 p-2">
        <AlertCircle className="h-4 w-4 text-destructive" />
        <span className="text-xs text-destructive">{error}</span>
      </div>
    );
  }

  // 테이블 미선택
  if (!dataSource?.tableName) {
    return (
      <div className="flex h-full w-full items-center justify-center p-2">
        <span className="text-xs text-muted-foreground">
          테이블을 선택하세요
        </span>
      </div>
    );
  }

  // 데이터 없음
  if (rows.length === 0) {
    return (
      <div className="flex h-full w-full items-center justify-center p-2">
        <span className="text-xs text-muted-foreground">
          데이터가 없습니다
        </span>
      </div>
    );
  }

  const isPaginationSide = overflowMode === "pagination" && paginationStyle === "side" && totalPages > 1;

  return (
    <div className={`flex w-full flex-col ${className || ""}`}>
      {/* 헤더 */}
      {header?.enabled && header.label && (
        <div className="shrink-0 border-b px-3 py-2">
          <span className="text-sm font-medium">{header.label}</span>
        </div>
      )}

      {/* 컨텐츠 */}
      <div className={`flex-1 ${isPaginationSide ? "relative" : ""}`}>
        {displayMode === "list" ? (
          <ListModeView columns={listColumns} data={visibleData} onRowClick={rowClickAction !== "none" ? handleRowClick : undefined} />
        ) : (
          <CardModeView
            cardGrid={cardGrid}
            data={visibleData}
            handleCardButtonClick={handleCardButtonClick}
            loadingRowId={loadingRowIdx}
          />
        )}
        {isPaginationSide && (
          <>
            <Button
              variant="ghost"
              size="icon"
              onClick={() => handlePageChange(currentPage - 1)}
              disabled={currentPage <= 1}
              className="absolute left-1 top-1/2 z-10 h-7 w-7 -translate-y-1/2 rounded-full bg-background/60 opacity-70 shadow-sm backdrop-blur-sm transition-opacity hover:opacity-100 disabled:opacity-20"
            >
              <ChevronLeft className="h-4 w-4" />
            </Button>
            <Button
              variant="ghost"
              size="icon"
              onClick={() => handlePageChange(currentPage + 1)}
              disabled={currentPage >= totalPages}
              className="absolute right-1 top-1/2 z-10 h-7 w-7 -translate-y-1/2 rounded-full bg-background/60 opacity-70 shadow-sm backdrop-blur-sm transition-opacity hover:opacity-100 disabled:opacity-20"
            >
              <ChevronRight className="h-4 w-4" />
            </Button>
          </>
        )}
      </div>

      {/* side 모드 페이지 인디케이터 (컨텐츠 아래 별도 영역) */}
      {isPaginationSide && (
        <div className="shrink-0 flex justify-center py-1">
          <span className="rounded-full bg-muted/50 px-2.5 py-0.5 text-[10px] tabular-nums text-muted-foreground">
            {currentPage} / {totalPages}
          </span>
        </div>
      )}

      {/* 더보기 모드 컨트롤 */}
      {overflowMode === "loadMore" && showExpandButton && (hasMore || isExpanded) && (
        <div className="shrink-0 border-t px-3 py-1.5 flex gap-2">
          {hasMore && (
            <Button
              variant="ghost"
              size="sm"
              onClick={handleLoadMore}
              className="h-7 flex-1 text-xs text-muted-foreground"
            >
              <ChevronDown className="mr-1 h-3 w-3" />
              더보기 ({rows.length - effectiveLimit}건 남음)
            </Button>
          )}
          {isExpanded && (
            <Button
              variant="ghost"
              size="sm"
              onClick={handleCollapse}
              className="h-7 text-xs text-muted-foreground"
            >
              <ChevronUp className="mr-1 h-3 w-3" />
              접기
            </Button>
          )}
        </div>
      )}

      {/* 페이지네이션 bottom 모드 컨트롤 */}
      {overflowMode === "pagination" && paginationStyle === "bottom" && totalPages > 1 && (
        <div className="shrink-0 border-t px-3 py-1.5 flex items-center justify-between">
          <span className="text-[10px] text-muted-foreground">
            {rows.length}건 중 {(currentPage - 1) * pageSize + 1}-{Math.min(currentPage * pageSize, rows.length)}
          </span>
          <div className="flex items-center gap-1">
            <Button
              variant="ghost"
              size="icon"
              onClick={() => handlePageChange(currentPage - 1)}
              disabled={currentPage <= 1}
              className="h-6 w-6"
            >
              <ChevronLeft className="h-3 w-3" />
            </Button>
            <span className="text-xs tabular-nums px-1">
              {currentPage} / {totalPages}
            </span>
            <Button
              variant="ghost"
              size="icon"
              onClick={() => handlePageChange(currentPage + 1)}
              disabled={currentPage >= totalPages}
              className="h-6 w-6"
            >
              <ChevronRight className="h-3 w-3" />
            </Button>
          </div>
        </div>
      )}
    </div>
  );
}

// ===== 리스트 모드 =====

interface ListModeViewProps {
  columns: ListColumnConfig[];
  data: RowData[];
  onRowClick?: (row: RowData) => void;
}

function ListModeView({ columns, data, onRowClick }: ListModeViewProps) {
  // 런타임 컬럼 전환 상태
  // key: 컬럼 인덱스, value: 현재 활성 컬럼명 (alternateColumns 중 하나 또는 원래 columnName)
  const [activeColumns, setActiveColumns] = useState<Record<number, string>>({});

  if (columns.length === 0) {
    return (
      <div className="flex h-full items-center justify-center p-2">
        <span className="text-xs text-muted-foreground">
          컬럼을 설정하세요
        </span>
      </div>
    );
  }

  const gridCols = columns.map((c) => c.width || "1fr").join(" ");

  return (
    <div className="w-full">
      {/* 헤더 행 */}
      <div
        className="border-b bg-muted/50"
        style={{ display: "grid", gridTemplateColumns: gridCols }}
      >
        {columns.map((col, colIdx) => {
          const hasAlternates = (col.alternateColumns || []).length > 0;
          const currentColName = activeColumns[colIdx] || col.columnName;
          // 원래 컬럼이면 기존 라벨, 전환된 컬럼이면 컬럼명 부분만 표시
          const currentLabel =
            currentColName === col.columnName
              ? col.label
              : resolveColumnName(currentColName);

          if (hasAlternates) {
            // 전환 가능한 헤더: Popover 드롭다운
            return (
              <Popover key={col.columnName}>
                <PopoverTrigger asChild>
                  <button
                    className="flex w-full items-center justify-between px-2 py-1.5 text-xs font-medium text-muted-foreground hover:bg-muted/80 transition-colors"
                    style={{ textAlign: col.align || "left" }}
                  >
                    <span className="truncate">{currentLabel}</span>
                    <ChevronsUpDown className="ml-1 h-3 w-3 shrink-0 opacity-50" />
                  </button>
                </PopoverTrigger>
                <PopoverContent className="w-auto min-w-[120px] p-1" align="start">
                  <div className="flex flex-col">
                    {/* 원래 컬럼 */}
                    <button
                      className={cn(
                        "rounded px-2 py-1 text-left text-xs transition-colors",
                        currentColName === col.columnName
                          ? "bg-primary/10 text-primary font-medium"
                          : "hover:bg-muted"
                      )}
                      onClick={() => {
                        setActiveColumns((prev) => {
                          const next = { ...prev };
                          delete next[colIdx];
                          return next;
                        });
                      }}
                    >
                      {col.label} (기본)
                    </button>
                    {/* 대체 컬럼들 */}
                    {(col.alternateColumns || []).map((altCol) => {
                      const altLabel = resolveColumnName(altCol);
                      return (
                        <button
                          key={altCol}
                          className={cn(
                            "rounded px-2 py-1 text-left text-xs transition-colors",
                            currentColName === altCol
                              ? "bg-primary/10 text-primary font-medium"
                              : "hover:bg-muted"
                          )}
                          onClick={() => {
                            setActiveColumns((prev) => ({
                              ...prev,
                              [colIdx]: altCol,
                            }));
                          }}
                        >
                          {altLabel}
                        </button>
                      );
                    })}
                  </div>
                </PopoverContent>
              </Popover>
            );
          }

          // 전환 없는 일반 헤더
          return (
            <div
              key={col.columnName}
              className="px-2 py-1.5 text-xs font-medium text-muted-foreground"
              style={{ textAlign: col.align || "left" }}
            >
              {col.label}
            </div>
          );
        })}
      </div>

      {/* 데이터 행 */}
      {data.map((row, i) => (
        <div
          key={i}
          className={cn(
            "border-b last:border-b-0 hover:bg-muted/30 transition-colors",
            onRowClick && "cursor-pointer"
          )}
          style={{ display: "grid", gridTemplateColumns: gridCols }}
          onClick={() => onRowClick?.(row)}
        >
          {columns.map((col, colIdx) => {
            const currentColName = activeColumns[colIdx] || col.columnName;
            const resolvedKey = resolveColumnName(currentColName);
            return (
              <div
                key={`${col.columnName}-${colIdx}`}
                className="px-2 py-1.5 text-xs truncate"
                style={{ textAlign: col.align || "left" }}
              >
                {String(row[resolvedKey] ?? "")}
              </div>
            );
          })}
        </div>
      ))}
    </div>
  );
}

// ===== 카드 모드 =====

interface CardModeViewProps {
  cardGrid?: CardGridConfig;
  data: RowData[];
  handleCardButtonClick?: (cell: CardCellDefinition, row: RowData) => void;
  loadingRowId?: number;
}

function CardModeView({ cardGrid, data, handleCardButtonClick, loadingRowId }: CardModeViewProps) {
  if (!cardGrid || (cardGrid.cells || []).length === 0) {
    return (
      <div className="flex h-full items-center justify-center p-2">
        <span className="text-xs text-muted-foreground">
          카드 레이아웃을 설정하세요
        </span>
      </div>
    );
  }

  return (
    <div className="space-y-2 p-2">
      {data.map((row, i) => (
        <div
          key={i}
          className="rounded-md border"
          style={{
            display: "grid",
            gridTemplateColumns:
              cardGrid.colWidths && cardGrid.colWidths.length > 0
                ? cardGrid.colWidths.map((w) => `minmax(30px, ${w || "1fr"})`).join(" ")
                : "1fr",
            gridTemplateRows:
              cardGrid.rowHeights && cardGrid.rowHeights.length > 0
                ? cardGrid.rowHeights
                    .map((h) => {
                      if (!h) return "minmax(32px, auto)";
                      // px 값 -> minmax(Npx, auto): 최소 높이 보장 + 컨텐츠에 맞게 확장
                      if (h.endsWith("px")) {
                        return `minmax(${h}, auto)`;
                      }
                      // fr 값 -> 마이그레이션 호환: px 변환 후 minmax 적용
                      const px = Math.round(parseFloat(h) * 32) || 32;
                      return `minmax(${px}px, auto)`;
                    })
                    .join(" ")
                : `repeat(${Number(cardGrid.rows) || 1}, minmax(32px, auto))`,
            gap: `${Number(cardGrid.gap) || 0}px`,
          }}
        >
          {(cardGrid.cells || []).map((cell) => {
            // 가로 정렬 매핑
            const justifyMap = { left: "flex-start", center: "center", right: "flex-end" } as const;
            const alignItemsMap = { top: "flex-start", middle: "center", bottom: "flex-end" } as const;
            return (
              <div
                key={cell.id}
                className="overflow-hidden p-1.5"
                style={{
                  display: "flex",
                  flexDirection: "column",
                  justifyContent: alignItemsMap[cell.verticalAlign || "top"],
                  alignItems: justifyMap[cell.align || "left"],
                  gridColumn: `${Number(cell.col) || 1} / span ${Number(cell.colSpan) || 1}`,
                  gridRow: `${Number(cell.row) || 1} / span ${Number(cell.rowSpan) || 1}`,
                  border: cardGrid.showBorder
                    ? "1px solid hsl(var(--border))"
                    : "none",
                }}
              >
                {renderCellContent(cell, row, handleCardButtonClick, loadingRowId === i)}
              </div>
            );
          })}
        </div>
      ))}
    </div>
  );
}

// ===== 셀 컨텐츠 렌더링 =====

function renderCellContent(
  cell: CardCellDefinition,
  row: RowData,
  onButtonClick?: (cell: CardCellDefinition, row: RowData) => void,
  isButtonLoading?: boolean,
): React.ReactNode {
  const value = row[cell.columnName];
  const displayValue = value != null ? String(value) : "";

  switch (cell.type) {
    case "image":
      return displayValue ? (
        <img
          src={displayValue}
          alt={cell.label || cell.columnName}
          className="h-full max-h-[200px] w-full object-cover rounded"
        />
      ) : (
        <div className="flex h-full items-center justify-center bg-muted rounded">
          <span className="text-[10px] text-muted-foreground">No Image</span>
        </div>
      );

    case "badge":
      return (
        <span className="inline-flex items-center rounded-full bg-primary/10 px-2 py-0.5 text-[10px] font-medium text-primary">
          {displayValue}
        </span>
      );

    case "button":
      return (
        <Button
          variant={cell.buttonVariant || "outline"}
          size="sm"
          className="h-6 text-[10px]"
          disabled={isButtonLoading}
          onClick={(e) => {
            e.stopPropagation();
            onButtonClick?.(cell, row);
          }}
        >
          {cell.label || displayValue}
        </Button>
      );

    case "text":
    default: {
      // 글자 크기 매핑
      const fontSizeClass =
        cell.fontSize === "sm"
          ? "text-[10px]"
          : cell.fontSize === "lg"
            ? "text-sm"
            : "text-xs"; // md (기본)
      const isLabelLeft = cell.labelPosition === "left";

      return (
        <div className={isLabelLeft ? "flex items-baseline gap-1" : "flex flex-col"}>
          {cell.label && (
            <span className="text-[10px] text-muted-foreground shrink-0">
              {cell.label}{isLabelLeft ? ":" : ""}
            </span>
          )}
          <span className={`${fontSizeClass} truncate`}>{displayValue}</span>
        </div>
      );
    }
  }
}
