"use client";

/**
 * GridEditor — 복잡한 테이블 양식 편집기
 *
 * 셀 병합(rowspan/colspan), 고정 텍스트/데이터 바인딩 혼합,
 * 셀별 스타일 설정이 가능한 그리드 에디터.
 */

import React, { useState, useCallback, useMemo, useRef } from "react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "@/components/ui/select";
import {
  Plus,
  Minus,
  Merge,
  SplitSquareHorizontal,
  Type,
  Database,
  Paintbrush,
  Bold,
  AlignLeft,
  AlignCenter,
  AlignRight,
  Rows3,
  Columns3,
  Trash2,
} from "lucide-react";
import type { GridCell, ComponentConfig } from "@/types/report";

// ─── 상수 ────────────────────────────────────────────────────────────────────

const DEFAULT_COL_WIDTH = 100;
const DEFAULT_ROW_HEIGHT = 32;
const MIN_ROWS = 1;
const MIN_COLS = 1;
const MAX_ROWS = 100;
const MAX_COLS = 30;
const INITIAL_ROWS = 4;
const INITIAL_COLS = 6;

// ─── 유틸 함수 ──────────────────────────────────────────────────────────────

function cellId(row: number, col: number): string {
  return `r${row}c${col}`;
}

function createEmptyCell(row: number, col: number): GridCell {
  return {
    id: cellId(row, col),
    row,
    col,
    rowSpan: 1,
    colSpan: 1,
    cellType: "static",
    value: "",
    align: "center",
    verticalAlign: "middle",
    fontWeight: "normal",
    fontSize: 12,
    borderStyle: "thin",
  };
}

function initGrid(rows: number, cols: number): GridCell[] {
  const cells: GridCell[] = [];
  for (let r = 0; r < rows; r++) {
    for (let c = 0; c < cols; c++) {
      cells.push(createEmptyCell(r, c));
    }
  }
  return cells;
}

function getCell(cells: GridCell[], row: number, col: number): GridCell | undefined {
  return cells.find((c) => c.row === row && c.col === col);
}

interface SelectionRange {
  startRow: number;
  startCol: number;
  endRow: number;
  endCol: number;
}

function normalizeRange(range: SelectionRange): SelectionRange {
  return {
    startRow: Math.min(range.startRow, range.endRow),
    startCol: Math.min(range.startCol, range.endCol),
    endRow: Math.max(range.startRow, range.endRow),
    endCol: Math.max(range.startCol, range.endCol),
  };
}

function isCellInRange(row: number, col: number, range: SelectionRange | null): boolean {
  if (!range) return false;
  const n = normalizeRange(range);
  return row >= n.startRow && row <= n.endRow && col >= n.startCol && col <= n.endCol;
}

// ─── Props ───────────────────────────────────────────────────────────────────

interface GridEditorProps {
  component: ComponentConfig;
  onUpdate: (updates: Partial<ComponentConfig>) => void;
  schemaColumns?: Array<{ column_name: string; data_type: string }>;
}

// ─── 메인 컴포넌트 ──────────────────────────────────────────────────────────

export function GridEditor({ component, onUpdate, schemaColumns = [] }: GridEditorProps) {
  const rowCount = component.gridRowCount ?? INITIAL_ROWS;
  const colCount = component.gridColCount ?? INITIAL_COLS;
  const cells = component.gridCells ?? initGrid(rowCount, colCount);
  const colWidths = component.gridColWidths ?? Array(colCount).fill(DEFAULT_COL_WIDTH);
  const rowHeights = component.gridRowHeights ?? Array(rowCount).fill(DEFAULT_ROW_HEIGHT);

  const [selection, setSelection] = useState<SelectionRange | null>(null);
  const [isDragging, setIsDragging] = useState(false);
  const dragStartRef = useRef<{ row: number; col: number } | null>(null);

  // 열/행 리사이즈
  const [resizingColIdx, setResizingColIdx] = useState<number | null>(null);
  const [resizingRowIdx, setResizingRowIdx] = useState<number | null>(null);
  const resizeStartRef = useRef(0);
  const resizeStartSizeRef = useRef(0);

  // 선택된 단일 셀 (설정 패널용)
  const selectedCell = useMemo(() => {
    if (!selection) return null;
    const n = normalizeRange(selection);
    return getCell(cells, n.startRow, n.startCol);
  }, [selection, cells]);

  // 선택 범위 내 셀 개수
  const selectedCellCount = useMemo(() => {
    if (!selection) return 0;
    const n = normalizeRange(selection);
    let count = 0;
    for (let r = n.startRow; r <= n.endRow; r++) {
      for (let c = n.startCol; c <= n.endCol; c++) {
        const cell = getCell(cells, r, c);
        if (cell && !cell.merged) count++;
      }
    }
    return count;
  }, [selection, cells]);

  // ─── 그리드 업데이트 헬퍼 ─────────────────────────────────────────────────

  const updateGrid = useCallback(
    (newCells: GridCell[], newRowCount?: number, newColCount?: number, newColWidths?: number[], newRowHeights?: number[]) => {
      onUpdate({
        gridCells: newCells,
        gridRowCount: newRowCount ?? rowCount,
        gridColCount: newColCount ?? colCount,
        gridColWidths: newColWidths ?? colWidths,
        gridRowHeights: newRowHeights ?? rowHeights,
      });
    },
    [onUpdate, rowCount, colCount, colWidths, rowHeights],
  );

  const updateCellProps = useCallback(
    (row: number, col: number, updates: Partial<GridCell>) => {
      const newCells = cells.map((c) =>
        c.row === row && c.col === col ? { ...c, ...updates } : c,
      );
      updateGrid(newCells);
    },
    [cells, updateGrid],
  );

  // ─── 셀 선택 핸들러 ──────────────────────────────────────────────────────

  const handleCellMouseDown = useCallback(
    (row: number, col: number, e: React.MouseEvent) => {
      e.preventDefault();
      // 병합된 셀 클릭 시 주체 셀로 리다이렉트
      const cell = getCell(cells, row, col);
      if (cell?.merged && cell.mergedBy) {
        const master = cells.find((c) => c.id === cell.mergedBy);
        if (master) {
          row = master.row;
          col = master.col;
        }
      }

      dragStartRef.current = { row, col };
      setIsDragging(true);
      setSelection({ startRow: row, startCol: col, endRow: row, endCol: col });
    },
    [cells],
  );

  const handleCellMouseEnter = useCallback(
    (row: number, col: number) => {
      if (!isDragging || !dragStartRef.current) return;
      setSelection({
        startRow: dragStartRef.current.row,
        startCol: dragStartRef.current.col,
        endRow: row,
        endCol: col,
      });
    },
    [isDragging],
  );

  const handleMouseUp = useCallback(() => {
    setIsDragging(false);
    dragStartRef.current = null;
  }, []);

  // ─── 행/열 추가/삭제 ─────────────────────────────────────────────────────

  const handleAddRow = useCallback(() => {
    if (rowCount >= MAX_ROWS) return;
    const newRow = rowCount;
    const newCells = [
      ...cells,
      ...Array.from({ length: colCount }, (_, c) => createEmptyCell(newRow, c)),
    ];
    updateGrid(newCells, newRow + 1, colCount, colWidths, [...rowHeights, DEFAULT_ROW_HEIGHT]);
  }, [cells, rowCount, colCount, colWidths, rowHeights, updateGrid]);

  const handleAddCol = useCallback(() => {
    if (colCount >= MAX_COLS) return;
    const newCol = colCount;
    const newCells = [
      ...cells,
      ...Array.from({ length: rowCount }, (_, r) => createEmptyCell(r, newCol)),
    ];
    updateGrid(newCells, rowCount, newCol + 1, [...colWidths, DEFAULT_COL_WIDTH], rowHeights);
  }, [cells, rowCount, colCount, colWidths, rowHeights, updateGrid]);

  const handleRemoveRow = useCallback(() => {
    if (rowCount <= MIN_ROWS) return;
    const lastRow = rowCount - 1;
    // 병합이 마지막 행을 포함하면 제거 불가
    const hasMergeConflict = cells.some(
      (c) => !c.merged && c.row < lastRow && c.row + (c.rowSpan ?? 1) - 1 >= lastRow,
    );
    if (hasMergeConflict) return;

    const newCells = cells.filter((c) => c.row < lastRow);
    updateGrid(newCells, lastRow, colCount, colWidths, rowHeights.slice(0, lastRow));
  }, [cells, rowCount, colCount, colWidths, rowHeights, updateGrid]);

  const handleRemoveCol = useCallback(() => {
    if (colCount <= MIN_COLS) return;
    const lastCol = colCount - 1;
    const hasMergeConflict = cells.some(
      (c) => !c.merged && c.col < lastCol && c.col + (c.colSpan ?? 1) - 1 >= lastCol,
    );
    if (hasMergeConflict) return;

    const newCells = cells.filter((c) => c.col < lastCol);
    updateGrid(newCells, rowCount, lastCol, colWidths.slice(0, lastCol), rowHeights);
  }, [cells, rowCount, colCount, colWidths, rowHeights, updateGrid]);

  const handleRemoveColAt = useCallback(
    (targetCol: number) => {
      if (colCount <= MIN_COLS) return;

      const hasMergeConflict = cells.some((c) => {
        if (c.merged) return false;
        const cStart = c.col;
        const cEnd = c.col + (c.colSpan ?? 1) - 1;
        return cStart !== targetCol && cEnd >= targetCol && cStart < targetCol;
      });
      if (hasMergeConflict) return;

      const newCells = cells
        .filter((c) => c.col !== targetCol)
        .map((c) => {
          const newCol = c.col > targetCol ? c.col - 1 : c.col;
          return {
            ...c,
            col: newCol,
            id: cellId(c.row, newCol),
            ...(c.mergedBy
              ? {
                  mergedBy: cells.find((m) => m.id === c.mergedBy)
                    ? cellId(
                        cells.find((m) => m.id === c.mergedBy)!.row,
                        cells.find((m) => m.id === c.mergedBy)!.col > targetCol
                          ? cells.find((m) => m.id === c.mergedBy)!.col - 1
                          : cells.find((m) => m.id === c.mergedBy)!.col,
                      )
                    : undefined,
                }
              : {}),
          };
        });

      const newColWidths = colWidths.filter((_, i) => i !== targetCol);
      updateGrid(newCells, rowCount, colCount - 1, newColWidths, rowHeights);
      setSelection(null);
    },
    [cells, rowCount, colCount, colWidths, rowHeights, updateGrid],
  );

  const handleRemoveRowAt = useCallback(
    (targetRow: number) => {
      if (rowCount <= MIN_ROWS) return;

      const hasMergeConflict = cells.some((c) => {
        if (c.merged) return false;
        const rStart = c.row;
        const rEnd = c.row + (c.rowSpan ?? 1) - 1;
        return rStart !== targetRow && rEnd >= targetRow && rStart < targetRow;
      });
      if (hasMergeConflict) return;

      const newCells = cells
        .filter((c) => c.row !== targetRow)
        .map((c) => {
          const newRow = c.row > targetRow ? c.row - 1 : c.row;
          return {
            ...c,
            row: newRow,
            id: cellId(newRow, c.col),
            ...(c.mergedBy
              ? {
                  mergedBy: cells.find((m) => m.id === c.mergedBy)
                    ? cellId(
                        cells.find((m) => m.id === c.mergedBy)!.row > targetRow
                          ? cells.find((m) => m.id === c.mergedBy)!.row - 1
                          : cells.find((m) => m.id === c.mergedBy)!.row,
                        cells.find((m) => m.id === c.mergedBy)!.col,
                      )
                    : undefined,
                }
              : {}),
          };
        });

      const newRowHeights = rowHeights.filter((_, i) => i !== targetRow);
      updateGrid(newCells, rowCount - 1, colCount, colWidths, newRowHeights);
      setSelection(null);
    },
    [cells, rowCount, colCount, colWidths, rowHeights, updateGrid],
  );

  // ─── 셀 병합 / 해제 ──────────────────────────────────────────────────────

  const canMerge = useMemo(() => {
    if (!selection) return false;
    const n = normalizeRange(selection);
    if (n.startRow === n.endRow && n.startCol === n.endCol) return false;
    // 선택 범위 안에 이미 병합된 셀이 있으면 불가
    for (let r = n.startRow; r <= n.endRow; r++) {
      for (let c = n.startCol; c <= n.endCol; c++) {
        const cell = getCell(cells, r, c);
        if (!cell) return false;
        if (cell.merged) return false;
        if ((cell.rowSpan ?? 1) > 1 || (cell.colSpan ?? 1) > 1) return false;
      }
    }
    return true;
  }, [selection, cells]);

  const canUnmerge = useMemo(() => {
    if (!selectedCell) return false;
    return (selectedCell.rowSpan ?? 1) > 1 || (selectedCell.colSpan ?? 1) > 1;
  }, [selectedCell]);

  const handleMerge = useCallback(() => {
    if (!selection || !canMerge) return;
    const n = normalizeRange(selection);
    const rSpan = n.endRow - n.startRow + 1;
    const cSpan = n.endCol - n.startCol + 1;
    const masterId = cellId(n.startRow, n.startCol);

    const newCells = cells.map((cell) => {
      if (cell.row === n.startRow && cell.col === n.startCol) {
        return { ...cell, rowSpan: rSpan, colSpan: cSpan };
      }
      if (isCellInRange(cell.row, cell.col, n) && !(cell.row === n.startRow && cell.col === n.startCol)) {
        return { ...cell, merged: true, mergedBy: masterId, value: "", field: "", formula: "" };
      }
      return cell;
    });

    updateGrid(newCells);
    setSelection({ startRow: n.startRow, startCol: n.startCol, endRow: n.startRow, endCol: n.startCol });
  }, [selection, canMerge, cells, updateGrid]);

  const handleUnmerge = useCallback(() => {
    if (!selectedCell || !canUnmerge) return;
    const masterId = selectedCell.id;

    const newCells = cells.map((cell) => {
      if (cell.id === masterId) {
        return { ...cell, rowSpan: 1, colSpan: 1 };
      }
      if (cell.mergedBy === masterId) {
        return { ...cell, merged: false, mergedBy: undefined };
      }
      return cell;
    });

    updateGrid(newCells);
  }, [selectedCell, canUnmerge, cells, updateGrid]);

  // ─── 열/행 리사이즈 ────────────────────────────────────────────────────────

  const MIN_COL_WIDTH = 40;
  const MIN_ROW_HEIGHT = 20;

  const handleColResizeStart = useCallback(
    (colIdx: number, e: React.MouseEvent) => {
      e.preventDefault();
      e.stopPropagation();
      setResizingColIdx(colIdx);
      resizeStartRef.current = e.clientX;
      resizeStartSizeRef.current = colWidths[colIdx] ?? DEFAULT_COL_WIDTH;

      const onMove = (moveEvent: MouseEvent) => {
        const delta = moveEvent.clientX - resizeStartRef.current;
        const newWidth = Math.max(MIN_COL_WIDTH, resizeStartSizeRef.current + delta);
        const newColWidths = colWidths.map((w, i) => (i === colIdx ? newWidth : w));
        updateGrid(cells, rowCount, colCount, newColWidths, rowHeights);
      };

      const onUp = () => {
        setResizingColIdx(null);
        document.removeEventListener("mousemove", onMove);
        document.removeEventListener("mouseup", onUp);
      };

      document.addEventListener("mousemove", onMove);
      document.addEventListener("mouseup", onUp);
    },
    [colWidths, cells, rowCount, colCount, rowHeights, updateGrid],
  );

  const handleRowResizeStart = useCallback(
    (rowIdx: number, e: React.MouseEvent) => {
      e.preventDefault();
      e.stopPropagation();
      setResizingRowIdx(rowIdx);
      resizeStartRef.current = e.clientY;
      resizeStartSizeRef.current = rowHeights[rowIdx] ?? DEFAULT_ROW_HEIGHT;

      const onMove = (moveEvent: MouseEvent) => {
        const delta = moveEvent.clientY - resizeStartRef.current;
        const newHeight = Math.max(MIN_ROW_HEIGHT, resizeStartSizeRef.current + delta);
        const newRowHeights = rowHeights.map((h, i) => (i === rowIdx ? newHeight : h));
        updateGrid(cells, rowCount, colCount, colWidths, newRowHeights);
      };

      const onUp = () => {
        setResizingRowIdx(null);
        document.removeEventListener("mousemove", onMove);
        document.removeEventListener("mouseup", onUp);
      };

      document.addEventListener("mousemove", onMove);
      document.addEventListener("mouseup", onUp);
    },
    [rowHeights, cells, rowCount, colCount, colWidths, updateGrid],
  );

  // ─── 셀 직접 편집 (더블클릭) ──────────────────────────────────────────────

  const [editingCell, setEditingCell] = useState<string | null>(null);
  const editInputRef = useRef<HTMLInputElement>(null);

  const handleCellDoubleClick = useCallback((cell: GridCell) => {
    if (cell.merged) return;
    setEditingCell(cell.id);
    setTimeout(() => editInputRef.current?.focus(), 0);
  }, []);

  const handleEditBlur = useCallback(
    (cell: GridCell, newValue: string) => {
      setEditingCell(null);
      if (cell.cellType === "static") {
        updateCellProps(cell.row, cell.col, { value: newValue });
      } else if (cell.cellType === "field") {
        updateCellProps(cell.row, cell.col, { field: newValue });
      }
    },
    [updateCellProps],
  );

  const handleEditKeyDown = useCallback(
    (e: React.KeyboardEvent, cell: GridCell, value: string) => {
      if (e.key === "Enter") {
        handleEditBlur(cell, value);
      } else if (e.key === "Escape") {
        setEditingCell(null);
      }
    },
    [handleEditBlur],
  );

  // ─── 그리드 렌더링 ────────────────────────────────────────────────────────

  const totalWidth = colWidths.reduce((a, b) => a + b, 0);

  const renderGridRow = (r: number): React.ReactNode[] => {
    const tds: React.ReactNode[] = [];

    for (let c = 0; c < colCount; c++) {
      const cell = getCell(cells, r, c);
      if (!cell || cell.merged) continue;

      const rSpan = cell.rowSpan ?? 1;
      const cSpan = cell.colSpan ?? 1;
      const isSelected = isCellInRange(r, c, selection);
      const isEditing = editingCell === cell.id;

      const w = colWidths.slice(c, c + cSpan).reduce((a, b) => a + b, 0);
      const h = rowHeights.slice(r, r + rSpan).reduce((a, b) => a + b, 0);

      const cellBg = cell.backgroundColor || (isSelected ? "#dbeafe" : "white");
      const borderW = cell.borderStyle === "medium" ? 2 : cell.borderStyle === "thick" ? 3 : cell.borderStyle === "none" ? 0 : 1;

      const displayValue =
        cell.cellType === "field" && cell.field
          ? `{${cell.field}}`
          : cell.cellType === "formula" && cell.formula
            ? `=${cell.formula}`
            : cell.value ?? "";

      tds.push(
        <td
          key={cell.id}
          rowSpan={rSpan > 1 ? rSpan : undefined}
          colSpan={cSpan > 1 ? cSpan : undefined}
          className="relative cursor-pointer select-none"
          style={{
            width: w,
            height: h,
            minWidth: w,
            minHeight: h,
            backgroundColor: cellBg,
            border: `${borderW}px solid ${isSelected ? "#3b82f6" : "#e5e7eb"}`,
            padding: "2px 4px",
            fontSize: cell.fontSize ?? 12,
            fontWeight: cell.fontWeight === "bold" ? 700 : 400,
            color: cell.textColor || "#111827",
            textAlign: cell.align || "center",
            verticalAlign: cell.verticalAlign || "middle",
            overflow: "hidden",
            whiteSpace: "nowrap",
            textOverflow: "ellipsis",
            outline: isSelected ? "2px solid #3b82f6" : "none",
            outlineOffset: "-2px",
          }}
          onMouseDown={(e) => handleCellMouseDown(r, c, e)}
          onMouseEnter={() => handleCellMouseEnter(r, c)}
          onDoubleClick={() => handleCellDoubleClick(cell)}
        >
          {isEditing ? (
            <input
              ref={editInputRef}
              className="h-full w-full border-none bg-transparent text-center text-xs outline-none"
              defaultValue={cell.cellType === "field" ? cell.field ?? "" : cell.value ?? ""}
              onBlur={(e) => handleEditBlur(cell, e.target.value)}
              onKeyDown={(e) => handleEditKeyDown(e, cell, (e.target as HTMLInputElement).value)}
              style={{ fontSize: cell.fontSize ?? 12 }}
            />
          ) : (
            <span className="pointer-events-none block truncate text-xs">
              {displayValue || <span className="text-gray-300">&mdash;</span>}
            </span>
          )}
          {cell.cellType === "field" && !isEditing && (
            <span className="absolute right-0.5 top-0.5 h-1.5 w-1.5 rounded-full bg-blue-500" />
          )}
        </td>,
      );
    }

    return tds;
  };

  // ─── 메인 렌더링 ──────────────────────────────────────────────────────────

  return (
    <div
      className="rounded-xl border border-border bg-white shadow-sm"
      onMouseUp={handleMouseUp}
      onMouseLeave={handleMouseUp}
    >
      {/* 컨트롤 바 */}
      <div className="flex items-center justify-between border-b border-gray-200 bg-gray-50 px-4 py-2.5">
        <span className="text-sm font-bold text-gray-800">격자 양식</span>
        <div className="flex items-center gap-1">
          {/* 행 조절 */}
          <Button
            variant="outline"
            size="sm"
            className="h-7 w-7 border-blue-300 p-0 text-blue-600 hover:bg-blue-50 disabled:opacity-30"
            onClick={handleRemoveRow}
            disabled={rowCount <= MIN_ROWS}
          >
            <Minus className="h-3 w-3" />
          </Button>
          <span className="flex w-12 items-center justify-center gap-0.5 text-xs font-medium text-gray-700">
            <Rows3 className="h-3 w-3 text-gray-400" />{rowCount}행
          </span>
          <Button
            variant="outline"
            size="sm"
            className="h-7 w-7 border-blue-300 p-0 text-blue-600 hover:bg-blue-50 disabled:opacity-30"
            onClick={handleAddRow}
            disabled={rowCount >= MAX_ROWS}
          >
            <Plus className="h-3 w-3" />
          </Button>

          <div className="mx-1.5 h-4 w-px bg-gray-300" />

          {/* 열 조절 */}
          <Button
            variant="outline"
            size="sm"
            className="h-7 w-7 border-blue-300 p-0 text-blue-600 hover:bg-blue-50 disabled:opacity-30"
            onClick={handleRemoveCol}
            disabled={colCount <= MIN_COLS}
          >
            <Minus className="h-3 w-3" />
          </Button>
          <span className="flex w-12 items-center justify-center gap-0.5 text-xs font-medium text-gray-700">
            <Columns3 className="h-3 w-3 text-gray-400" />{colCount}열
          </span>
          <Button
            variant="outline"
            size="sm"
            className="h-7 w-7 border-blue-300 p-0 text-blue-600 hover:bg-blue-50 disabled:opacity-30"
            onClick={handleAddCol}
            disabled={colCount >= MAX_COLS}
          >
            <Plus className="h-3 w-3" />
          </Button>

          <div className="mx-1.5 h-4 w-px bg-gray-300" />

          {/* 병합 / 해제 */}
          <Button
            variant="outline"
            size="sm"
            className="h-7 gap-1 border-gray-300 px-2 text-xs text-blue-600 hover:bg-blue-50 disabled:opacity-30 disabled:text-gray-400"
            onClick={handleMerge}
            disabled={!canMerge}
          >
            <Merge className="h-3 w-3" />
            병합
          </Button>
          <Button
            variant="outline"
            size="sm"
            className="h-7 gap-1 border-gray-300 px-2 text-xs text-orange-600 hover:bg-orange-50 disabled:opacity-30 disabled:text-gray-400"
            onClick={handleUnmerge}
            disabled={!canUnmerge}
          >
            <SplitSquareHorizontal className="h-3 w-3" />
            해제
          </Button>
        </div>
      </div>

      {/* 그리드 캔버스 */}
      <div className="overflow-auto p-3" style={{ maxHeight: 360 }}>
        <table
          className="select-none border-collapse"
          style={{ width: totalWidth + 28, tableLayout: "fixed" }}
        >
          <colgroup>
            <col style={{ width: 28, minWidth: 28 }} />
            {colWidths.map((w, i) => (
              <col key={i} style={{ width: w, minWidth: 40 }} />
            ))}
          </colgroup>
          <thead>
            <tr>
              <th className="border border-gray-200 bg-gray-100" />
              {colWidths.map((_, i) => (
                <th
                  key={i}
                  className="group/colhdr relative border border-gray-200 bg-gray-100 px-1 py-1 text-[10px] font-medium text-gray-400"
                >
                  <span>{i + 1}</span>
                  {colCount > MIN_COLS && (
                    <button
                      onClick={() => handleRemoveColAt(i)}
                      className="absolute -top-0.5 right-0.5 hidden h-3.5 w-3.5 items-center justify-center rounded-full bg-red-400 text-white shadow-sm transition-colors hover:bg-red-500 group-hover/colhdr:flex"
                      title={`${i + 1}열 삭제`}
                    >
                      <Trash2 className="h-2 w-2" />
                    </button>
                  )}
                  {i < colCount - 1 && (
                    <div
                      className={`absolute -right-[3px] top-0 z-10 h-full w-[5px] cursor-col-resize transition-colors ${
                        resizingColIdx === i ? "bg-blue-400" : "hover:bg-blue-300"
                      }`}
                      onMouseDown={(e) => handleColResizeStart(i, e)}
                    />
                  )}
                </th>
              ))}
            </tr>
          </thead>
          <tbody>
            {Array.from({ length: rowCount }).map((_, r) => {
              const gridRow = renderGridRow(r);
              return (
                <tr key={`row-${r}`}>
                  <td
                    className="group/rowhdr relative border border-gray-200 bg-gray-100 text-center text-[10px] font-medium text-gray-400"
                    style={{ width: 28, minWidth: 28, height: rowHeights[r] ?? DEFAULT_ROW_HEIGHT, minHeight: rowHeights[r] ?? DEFAULT_ROW_HEIGHT }}
                  >
                    <span>{r + 1}</span>
                    {rowCount > MIN_ROWS && (
                      <button
                        onClick={() => handleRemoveRowAt(r)}
                        className="absolute -left-0.5 top-0.5 hidden h-3.5 w-3.5 items-center justify-center rounded-full bg-red-400 text-white shadow-sm transition-colors hover:bg-red-500 group-hover/rowhdr:flex"
                        title={`${r + 1}행 삭제`}
                      >
                        <Trash2 className="h-2 w-2" />
                      </button>
                    )}
                    {r < rowCount - 1 && (
                      <div
                        className={`absolute -bottom-[3px] left-0 z-10 h-[5px] w-full cursor-row-resize transition-colors ${
                          resizingRowIdx === r ? "bg-blue-400" : "hover:bg-blue-300"
                        }`}
                        onMouseDown={(e) => handleRowResizeStart(r, e)}
                      />
                    )}
                  </td>
                  {gridRow}
                </tr>
              );
            })}
          </tbody>
        </table>
      </div>

      {/* 셀 설정 패널 */}
      <div className="border-t border-gray-200 bg-gray-50/50 px-4 py-3">
        {!selectedCell ? (
          <div className="flex items-center justify-center py-3 text-xs text-gray-400">
            셀을 클릭하여 선택하세요
          </div>
        ) : (
          <div className="space-y-3">
            {/* 셀 위치 */}
            <span className="inline-block rounded bg-gray-200 px-1.5 py-0.5 text-[10px] font-medium text-gray-500">
              {selectedCell.row + 1}행 {selectedCell.col + 1}열
              {((selectedCell.rowSpan ?? 1) > 1 || (selectedCell.colSpan ?? 1) > 1)
                ? ` (${selectedCell.rowSpan ?? 1}x${selectedCell.colSpan ?? 1})`
                : ""}
            </span>

            {/* 셀 타입 */}
            <div className="space-y-1">
              <label className="text-[10px] font-medium text-gray-500">셀 타입</label>
              <Select
                value={selectedCell.cellType}
                onValueChange={(v) => updateCellProps(selectedCell.row, selectedCell.col, { cellType: v as GridCell["cellType"] })}
              >
                <SelectTrigger className="h-8 w-full text-xs">
                  <SelectValue />
                </SelectTrigger>
                <SelectContent>
                  <SelectItem value="static">
                    <span className="flex items-center gap-1"><Type className="h-3 w-3" /> 고정 텍스트</span>
                  </SelectItem>
                  <SelectItem value="field">
                    <span className="flex items-center gap-1"><Database className="h-3 w-3" /> 데이터 연결</span>
                  </SelectItem>
                </SelectContent>
              </Select>
            </div>

            {/* 셀 값 */}
            {selectedCell.cellType === "static" && (
              <div className="space-y-1">
                <label className="text-[10px] font-medium text-gray-500">텍스트</label>
                <Input
                  className="h-8 w-full text-xs"
                  value={selectedCell.value ?? ""}
                  onChange={(e) => updateCellProps(selectedCell.row, selectedCell.col, { value: e.target.value })}
                  placeholder="텍스트 입력"
                />
              </div>
            )}

            {selectedCell.cellType === "field" && (
              <div className="space-y-1">
                <label className="text-[10px] font-medium text-gray-500">연결 필드</label>
                {selectedCell.field ? (
                  <span className="inline-flex items-center gap-1 rounded bg-blue-50 px-2 py-1.5 text-xs font-medium text-blue-700">
                    <Database className="h-3 w-3" />{selectedCell.field}
                  </span>
                ) : (
                  <p className="text-[10px] text-gray-400">데이터 연결 탭에서 배치</p>
                )}
              </div>
            )}

            {/* 구분선 */}
            <div className="h-px bg-gray-200" />

            {/* 스타일 설정 — 1행 5열 */}
            <div className="flex items-end gap-2">
              {/* 정렬 */}
              <div className="space-y-1">
                <label className="text-[10px] font-medium text-gray-500">정렬</label>
                <div className="flex items-center gap-0.5 rounded border border-gray-200 bg-white p-0.5">
                  {(["left", "center", "right"] as const).map((a) => (
                    <button
                      key={a}
                      className={`flex h-6 w-6 items-center justify-center rounded ${
                        selectedCell.align === a ? "bg-blue-100 text-blue-700" : "text-gray-400 hover:bg-gray-100"
                      }`}
                      onClick={() => updateCellProps(selectedCell.row, selectedCell.col, { align: a })}
                    >
                      {a === "left" ? <AlignLeft className="h-3 w-3" /> : a === "center" ? <AlignCenter className="h-3 w-3" /> : <AlignRight className="h-3 w-3" />}
                    </button>
                  ))}
                </div>
              </div>

              {/* 굵기 */}
              <div className="space-y-1">
                <label className="text-[10px] font-medium text-gray-500">굵기</label>
                <button
                  className={`flex h-7 w-7 items-center justify-center rounded border ${
                    selectedCell.fontWeight === "bold" ? "border-blue-300 bg-blue-100 text-blue-700" : "border-gray-200 text-gray-400 hover:bg-gray-100"
                  }`}
                  onClick={() => updateCellProps(selectedCell.row, selectedCell.col, { fontWeight: selectedCell.fontWeight === "bold" ? "normal" : "bold" })}
                >
                  <Bold className="h-3 w-3" />
                </button>
              </div>

              {/* 글자 크기 */}
              <div className="space-y-1">
                <label className="text-[10px] font-medium text-gray-500">크기</label>
                <Input
                  type="number"
                  className="h-7 w-12 text-center text-xs"
                  value={selectedCell.fontSize ?? 12}
                  min={8}
                  max={36}
                  onChange={(e) => updateCellProps(selectedCell.row, selectedCell.col, { fontSize: parseInt(e.target.value) || 12 })}
                />
              </div>

              {/* 배경색 */}
              <div className="space-y-1">
                <label className="text-[10px] font-medium text-gray-500">배경색</label>
                <div className="flex h-7 items-center gap-1 rounded border border-gray-200 bg-white px-1.5">
                  <Paintbrush className="h-3 w-3 shrink-0 text-gray-400" />
                  <input
                    type="color"
                    className="h-5 w-6 cursor-pointer rounded border-none"
                    value={selectedCell.backgroundColor || "#ffffff"}
                    onChange={(e) => updateCellProps(selectedCell.row, selectedCell.col, { backgroundColor: e.target.value })}
                  />
                </div>
              </div>

              {/* 글자색 */}
              <div className="space-y-1">
                <label className="text-[10px] font-medium text-gray-500">글자색</label>
                <div className="flex h-7 items-center gap-1 rounded border border-gray-200 bg-white px-1.5">
                  <Type className="h-3 w-3 shrink-0 text-gray-400" />
                  <input
                    type="color"
                    className="h-5 w-6 cursor-pointer rounded border-none"
                    value={selectedCell.textColor || "#111827"}
                    onChange={(e) => updateCellProps(selectedCell.row, selectedCell.col, { textColor: e.target.value })}
                  />
                </div>
              </div>
            </div>
          </div>
        )}
      </div>
    </div>
  );
}
