"use client";

/**
 * pop-dashboard 설정 패널 (디자이너용)
 *
 * 3개 탭:
 * [기본 설정] - 표시 모드, 간격, 인디케이터
 * [아이템 관리] - 아이템 추가/삭제/순서변경, 데이터 소스 설정
 * [페이지] - 페이지(슬라이드) 추가/삭제, 각 페이지 독립 그리드 레이아웃
 */

import React, { useState, useEffect, useCallback } from "react";
import {
  Plus,
  Trash2,
  ChevronDown,
  ChevronUp,
  GripVertical,
  Check,
  ChevronsUpDown,
  Eye,
} from "lucide-react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "@/components/ui/select";
import { Switch } from "@/components/ui/switch";
import {
  Command,
  CommandEmpty,
  CommandGroup,
  CommandInput,
  CommandItem,
  CommandList,
} from "@/components/ui/command";
import {
  Popover,
  PopoverContent,
  PopoverTrigger,
} from "@/components/ui/popover";
import { cn } from "@/lib/utils";
import type {
  PopDashboardConfig,
  DashboardItem,
  DashboardSubType,
  DashboardDisplayMode,
  DataSourceConfig,
  DataSourceFilter,
  FilterOperator,
  FormulaConfig,
  ItemVisibility,
  DashboardCell,
  DashboardPage,
  JoinConfig,
  JoinType,
  ItemStyleConfig,
  AggregationType,
} from "../types";
import {
  TEXT_ALIGN_LABELS,
} from "../types";
import { migrateConfig } from "./PopDashboardComponent";
import {
  fetchTableColumns,
  fetchTableList,
  type ColumnInfo,
  type TableInfo,
} from "./utils/dataFetcher";
import { validateExpression } from "./utils/formula";

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

interface ConfigPanelProps {
  config: PopDashboardConfig | undefined;
  onUpdate: (config: PopDashboardConfig) => void;
  /** 페이지 미리보기 요청 (-1이면 해제) */
  onPreviewPage?: (pageIndex: number) => void;
  /** 현재 미리보기 중인 페이지 인덱스 */
  previewPageIndex?: number;
}

// ===== 기본값 =====

const DEFAULT_CONFIG: PopDashboardConfig = {
  items: [],
  pages: [],
  displayMode: "arrows",
  autoSlideInterval: 5,
  autoSlideResumeDelay: 3,
  showIndicator: true,
  gap: 8,
};

const DEFAULT_VISIBILITY: ItemVisibility = {
  showLabel: true,
  showValue: true,
  showUnit: true,
  showTrend: true,
  showSubLabel: false,
  showTarget: true,
};

const DEFAULT_DATASOURCE: DataSourceConfig = {
  tableName: "",
  filters: [],
  sort: [],
};

// ===== 라벨 상수 =====

const DISPLAY_MODE_LABELS: Record<DashboardDisplayMode, string> = {
  arrows: "좌우 버튼",
  "auto-slide": "자동 슬라이드",
  scroll: "스크롤",
};

const SUBTYPE_LABELS: Record<DashboardSubType, string> = {
  "kpi-card": "KPI 카드",
  chart: "차트",
  gauge: "게이지",
  "stat-card": "통계 카드",
};

const JOIN_TYPE_LABELS: Record<JoinType, string> = {
  inner: "INNER JOIN",
  left: "LEFT JOIN",
  right: "RIGHT JOIN",
};

// ===== 집계 함수 유효성 검증 유틸 =====

// 아이템 타입별 사용 가능한 집계 함수
const SUBTYPE_AGGREGATION_MAP: Record<DashboardSubType, AggregationType[]> = {
  "kpi-card": ["count", "sum", "avg", "min", "max"],
  chart: ["count", "sum", "avg", "min", "max"],
  gauge: ["count", "sum", "avg", "min", "max"],
  "stat-card": ["count"],
};

// 집계 함수 라벨
const AGGREGATION_LABELS: Record<AggregationType, string> = {
  count: "건수 (COUNT)",
  sum: "합계 (SUM)",
  avg: "평균 (AVG)",
  min: "최소 (MIN)",
  max: "최대 (MAX)",
};

// 숫자 전용 집계 함수 (숫자 컬럼에만 사용 가능)
const NUMERIC_ONLY_AGGREGATIONS: AggregationType[] = ["sum", "avg"];

// PostgreSQL 숫자 타입 판별용 패턴
const NUMERIC_TYPE_PATTERNS = [
  "int", "integer", "bigint", "smallint",
  "numeric", "decimal", "real", "double",
  "float", "serial", "bigserial", "smallserial",
  "money", "number",
];

/** 컬럼이 숫자 타입인지 판별 */
function isNumericColumn(col: ColumnInfo): boolean {
  const t = (col.type || "").toLowerCase();
  const u = (col.udtName || "").toLowerCase();
  return NUMERIC_TYPE_PATTERNS.some(
    (pattern) => t.includes(pattern) || u.includes(pattern)
  );
}

/** 현재 집계 함수가 숫자 전용(sum/avg)인지 판별 */
function isNumericOnlyAggregation(aggType: string | undefined): boolean {
  return !!aggType && NUMERIC_ONLY_AGGREGATIONS.includes(aggType as AggregationType);
}

const FILTER_OPERATOR_LABELS: Record<FilterOperator, string> = {
  "=": "같음 (=)",
  "!=": "다름 (!=)",
  ">": "초과 (>)",
  ">=": "이상 (>=)",
  "<": "미만 (<)",
  "<=": "이하 (<=)",
  like: "포함 (LIKE)",
  in: "목록 (IN)",
  between: "범위 (BETWEEN)",
};

// ===== 데이터 소스 편집기 =====

function DataSourceEditor({
  dataSource,
  onChange,
  subType,
}: {
  dataSource: DataSourceConfig;
  onChange: (ds: DataSourceConfig) => void;
  subType?: DashboardSubType;
}) {
  // 테이블 목록 (Combobox용)
  const [tables, setTables] = useState<TableInfo[]>([]);
  const [loadingTables, setLoadingTables] = useState(false);
  const [tableOpen, setTableOpen] = useState(false);

  // 컬럼 목록 (집계 대상 컬럼용)
  const [columns, setColumns] = useState<ColumnInfo[]>([]);
  const [loadingCols, setLoadingCols] = useState(false);
  const [columnOpen, setColumnOpen] = useState(false);

  // 그룹핑 컬럼 (차트 X축용)
  const [groupByOpen, setGroupByOpen] = useState(false);

  // 마운트 시 테이블 목록 로드
  useEffect(() => {
    setLoadingTables(true);
    fetchTableList()
      .then(setTables)
      .finally(() => setLoadingTables(false));
  }, []);

  // 테이블 변경 시 컬럼 목록 조회
  useEffect(() => {
    if (!dataSource.tableName) {
      setColumns([]);
      return;
    }
    setLoadingCols(true);
    fetchTableColumns(dataSource.tableName)
      .then(setColumns)
      .finally(() => setLoadingCols(false));
  }, [dataSource.tableName]);

  return (
    <div className="space-y-2">
      {/* 테이블 선택 (검색 가능한 Combobox) */}
      <div>
        <Label className="text-xs">테이블</Label>
        <Popover open={tableOpen} onOpenChange={setTableOpen}>
          <PopoverTrigger asChild>
            <Button
              variant="outline"
              role="combobox"
              aria-expanded={tableOpen}
              className="h-8 w-full justify-between text-xs"
            >
              {dataSource.tableName
                ? (tables.find((t) => t.tableName === dataSource.tableName)
                    ?.displayName ?? dataSource.tableName)
                : loadingTables
                  ? "로딩..."
                  : "테이블 선택"}
              <ChevronsUpDown className="ml-2 h-3 w-3 shrink-0 opacity-50" />
            </Button>
          </PopoverTrigger>
          <PopoverContent
            className="p-0"
            style={{ width: "var(--radix-popover-trigger-width)" }}
            align="start"
          >
            <Command>
              <CommandInput placeholder="테이블 검색..." className="text-xs" />
              <CommandList>
                <CommandEmpty className="py-2 text-center text-xs">
                  테이블을 찾을 수 없습니다
                </CommandEmpty>
                <CommandGroup>
                  {tables.map((table) => (
                    <CommandItem
                      key={table.tableName}
                      value={table.tableName}
                      onSelect={() => {
                        const newVal =
                          table.tableName === dataSource.tableName
                            ? ""
                            : table.tableName;
                        onChange({ ...dataSource, tableName: newVal });
                        setTableOpen(false);
                      }}
                      className="text-xs"
                    >
                      <Check
                        className={cn(
                          "mr-2 h-3 w-3",
                          dataSource.tableName === table.tableName
                            ? "opacity-100"
                            : "opacity-0"
                        )}
                      />
                      <div className="flex flex-col">
                        <span className="font-medium">
                          {table.displayName || table.tableName}
                        </span>
                        {table.displayName &&
                          table.displayName !== table.tableName && (
                            <span className="text-[10px] text-muted-foreground">
                              {table.tableName}
                            </span>
                          )}
                      </div>
                    </CommandItem>
                  ))}
                </CommandGroup>
              </CommandList>
            </Command>
          </PopoverContent>
        </Popover>
      </div>

      {/* 집계 함수 + 대상 컬럼 */}
      <div className="grid grid-cols-2 gap-2">
        <div>
          <Label className="text-xs">집계 함수</Label>
          <Select
            value={dataSource.aggregation?.type ?? ""}
            onValueChange={(val) => {
              // STEP 4: 숫자 전용 집계로 변경 시, 현재 컬럼이 숫자가 아니면 초기화
              let currentColumn = dataSource.aggregation?.column ?? "";
              if (val && isNumericOnlyAggregation(val) && currentColumn) {
                const selectedCol = columns.find((c) => c.name === currentColumn);
                if (selectedCol && !isNumericColumn(selectedCol)) {
                  currentColumn = "";
                }
              }
              onChange({
                ...dataSource,
                aggregation: val
                  ? {
                      type: val as NonNullable<
                        DataSourceConfig["aggregation"]
                      >["type"],
                      column: currentColumn,
                      groupBy: dataSource.aggregation?.groupBy,
                    }
                  : undefined,
              });
            }}
          >
            <SelectTrigger className="h-8 text-xs">
              <SelectValue placeholder="없음" />
            </SelectTrigger>
            <SelectContent>
              {(subType
                ? SUBTYPE_AGGREGATION_MAP[subType]
                : (Object.keys(AGGREGATION_LABELS) as AggregationType[])
              ).map((aggType) => (
                <SelectItem key={aggType} value={aggType}>
                  {AGGREGATION_LABELS[aggType]}
                </SelectItem>
              ))}
            </SelectContent>
          </Select>
        </div>

        {dataSource.aggregation && (
          <div>
            <Label className="text-xs">대상 컬럼</Label>
            <Popover open={columnOpen} onOpenChange={setColumnOpen}>
              <PopoverTrigger asChild>
                <Button
                  variant="outline"
                  role="combobox"
                  aria-expanded={columnOpen}
                  disabled={loadingCols}
                  className="h-8 w-full justify-between text-xs"
                >
                  {loadingCols
                    ? "로딩..."
                    : dataSource.aggregation.column
                      ? columns.find(
                          (c) => c.name === dataSource.aggregation!.column
                        )
                        ? `${dataSource.aggregation.column} (${columns.find((c) => c.name === dataSource.aggregation!.column)?.type})`
                        : dataSource.aggregation.column
                      : "선택"}
                  <ChevronsUpDown className="ml-2 h-3 w-3 shrink-0 opacity-50" />
                </Button>
              </PopoverTrigger>
              <PopoverContent
                className="p-0"
                style={{ width: "var(--radix-popover-trigger-width)" }}
                align="start"
              >
                <Command>
                  <CommandInput
                    placeholder="컬럼 검색..."
                    className="text-xs"
                  />
                  <CommandList>
                    <CommandEmpty className="py-2 text-center text-xs">
                      {isNumericOnlyAggregation(dataSource.aggregation?.type)
                        ? "숫자 타입 컬럼이 없습니다."
                        : "컬럼을 찾을 수 없습니다."}
                    </CommandEmpty>
                    <CommandGroup>
                      {(isNumericOnlyAggregation(dataSource.aggregation?.type)
                        ? columns.filter(isNumericColumn)
                        : columns
                      ).map((col) => (
                        <CommandItem
                          key={col.name}
                          value={`${col.name} ${col.type}`}
                          onSelect={() => {
                            onChange({
                              ...dataSource,
                              aggregation: {
                                ...dataSource.aggregation!,
                                column: col.name,
                              },
                            });
                            setColumnOpen(false);
                          }}
                          className="text-xs"
                        >
                          <Check
                            className={cn(
                              "mr-2 h-3 w-3",
                              dataSource.aggregation?.column === col.name
                                ? "opacity-100"
                                : "opacity-0"
                            )}
                          />
                          <span className="font-medium">{col.name}</span>
                          <span className="ml-1 text-muted-foreground">
                            ({col.type})
                          </span>
                        </CommandItem>
                      ))}
                    </CommandGroup>
                  </CommandList>
                </Command>
              </PopoverContent>
            </Popover>
          </div>
        )}
      </div>

      {/* 그룹핑 (차트 X축 분류) */}
      {dataSource.aggregation && (
        <div>
          <Label className="text-xs">그룹핑 (X축)</Label>
          <Popover open={groupByOpen} onOpenChange={setGroupByOpen}>
            <PopoverTrigger asChild>
              <Button
                variant="outline"
                role="combobox"
                aria-expanded={groupByOpen}
                disabled={loadingCols}
                className="h-8 w-full justify-between text-xs"
              >
                {dataSource.aggregation.groupBy?.length
                  ? dataSource.aggregation.groupBy.join(", ")
                  : "없음 (단일 값)"}
                <ChevronsUpDown className="ml-2 h-3 w-3 shrink-0 opacity-50" />
              </Button>
            </PopoverTrigger>
            <PopoverContent
              className="p-0"
              style={{ width: "var(--radix-popover-trigger-width)" }}
              align="start"
            >
              <Command>
                <CommandInput placeholder="컬럼 검색..." className="text-xs" />
                <CommandList>
                  <CommandEmpty className="py-2 text-center text-xs">
                    컬럼을 찾을 수 없습니다.
                  </CommandEmpty>
                  <CommandGroup>
                    {columns.map((col) => (
                      <CommandItem
                        key={col.name}
                        value={`groupby-${col.name}`}
                        onSelect={() => {
                          const current = dataSource.aggregation?.groupBy ?? [];
                          const isSelected = current.includes(col.name);
                          const newGroupBy = isSelected
                            ? current.filter((g) => g !== col.name)
                            : [...current, col.name];
                          onChange({
                            ...dataSource,
                            aggregation: {
                              ...dataSource.aggregation!,
                              groupBy: newGroupBy.length > 0 ? newGroupBy : undefined,
                            },
                          });
                          setGroupByOpen(false);
                        }}
                        className="text-xs"
                      >
                        <Check
                          className={cn(
                            "mr-2 h-3 w-3",
                            dataSource.aggregation?.groupBy?.includes(col.name)
                              ? "opacity-100"
                              : "opacity-0"
                          )}
                        />
                        <span className="font-medium">{col.name}</span>
                        <span className="ml-1 text-muted-foreground">({col.type})</span>
                      </CommandItem>
                    ))}
                  </CommandGroup>
                </CommandList>
              </Command>
            </PopoverContent>
          </Popover>
          <p className="mt-0.5 text-[10px] text-muted-foreground">
            차트에서 X축 카테고리로 사용됩니다
          </p>
          {subType === "chart" && !dataSource.aggregation?.groupBy?.length && (
            <p className="mt-0.5 text-[10px] text-destructive">
              차트 모드에서는 그룹핑(X축)을 설정해야 의미 있는 차트가 표시됩니다
            </p>
          )}
        </div>
      )}

      {/* 자동 새로고침 (Switch + 주기 입력) */}
      <div className="space-y-1">
        <div className="flex items-center justify-between">
          <Label className="text-xs">자동 새로고침</Label>
          <Switch
            checked={(dataSource.refreshInterval ?? 0) > 0}
            onCheckedChange={(checked) =>
              onChange({
                ...dataSource,
                refreshInterval: checked ? 30 : 0,
              })
            }
          />
        </div>
        {(dataSource.refreshInterval ?? 0) > 0 && (
          <div>
            <Label className="text-[10px] text-muted-foreground">
              주기 (초)
            </Label>
            <Input
              type="number"
              value={dataSource.refreshInterval ?? 30}
              onChange={(e) =>
                onChange({
                  ...dataSource,
                  refreshInterval: Math.max(
                    5,
                    parseInt(e.target.value) || 30
                  ),
                })
              }
              className="h-7 text-xs"
              min={5}
            />
          </div>
        )}
      </div>

      {/* 조인 설정 */}
      <JoinEditor
        joins={dataSource.joins ?? []}
        mainTable={dataSource.tableName}
        onChange={(joins) => onChange({ ...dataSource, joins })}
      />

      {/* 필터 조건 */}
      <FilterEditor
        filters={dataSource.filters ?? []}
        tableName={dataSource.tableName}
        onChange={(filters) => onChange({ ...dataSource, filters })}
      />
    </div>
  );
}

// ===== 조인 편집기 =====

function JoinEditor({
  joins,
  mainTable,
  onChange,
}: {
  joins: JoinConfig[];
  mainTable: string;
  onChange: (joins: JoinConfig[]) => void;
}) {
  const [tables, setTables] = useState<TableInfo[]>([]);

  // 테이블 목록 로드
  useEffect(() => {
    fetchTableList().then(setTables);
  }, []);

  const addJoin = () => {
    onChange([
      ...joins,
      {
        targetTable: "",
        joinType: "left",
        on: { sourceColumn: "", targetColumn: "" },
      },
    ]);
  };

  const updateJoin = (index: number, partial: Partial<JoinConfig>) => {
    const newJoins = [...joins];
    newJoins[index] = { ...newJoins[index], ...partial };
    onChange(newJoins);
  };

  const removeJoin = (index: number) => {
    onChange(joins.filter((_, i) => i !== index));
  };

  return (
    <div className="space-y-2">
      <div className="flex items-center justify-between">
        <Label className="text-xs font-medium">테이블 조인</Label>
        <Button
          variant="outline"
          size="sm"
          className="h-6 text-[10px]"
          onClick={addJoin}
          disabled={!mainTable}
        >
          <Plus className="mr-1 h-3 w-3" />
          조인 추가
        </Button>
      </div>

      {!mainTable && joins.length === 0 && (
        <p className="text-[10px] text-muted-foreground">
          먼저 메인 테이블을 선택하세요
        </p>
      )}

      {joins.map((join, index) => (
        <JoinRow
          key={index}
          join={join}
          mainTable={mainTable}
          tables={tables}
          onUpdate={(partial) => updateJoin(index, partial)}
          onRemove={() => removeJoin(index)}
        />
      ))}
    </div>
  );
}

function JoinRow({
  join,
  mainTable,
  tables,
  onUpdate,
  onRemove,
}: {
  join: JoinConfig;
  mainTable: string;
  tables: TableInfo[];
  onUpdate: (partial: Partial<JoinConfig>) => void;
  onRemove: () => void;
}) {
  const [targetColumns, setTargetColumns] = useState<ColumnInfo[]>([]);
  const [sourceColumns, setSourceColumns] = useState<ColumnInfo[]>([]);
  const [targetTableOpen, setTargetTableOpen] = useState(false);

  // 메인 테이블 컬럼 로드
  useEffect(() => {
    if (!mainTable) return;
    fetchTableColumns(mainTable).then(setSourceColumns);
  }, [mainTable]);

  // 조인 대상 테이블 컬럼 로드
  useEffect(() => {
    if (!join.targetTable) return;
    fetchTableColumns(join.targetTable).then(setTargetColumns);
  }, [join.targetTable]);

  return (
    <div className="space-y-1.5 rounded border p-2">
      <div className="flex items-center gap-1">
        {/* 조인 타입 */}
        <Select
          value={join.joinType}
          onValueChange={(val) => onUpdate({ joinType: val as JoinType })}
        >
          <SelectTrigger className="h-7 w-28 text-[10px]">
            <SelectValue />
          </SelectTrigger>
          <SelectContent>
            {Object.entries(JOIN_TYPE_LABELS).map(([val, label]) => (
              <SelectItem key={val} value={val} className="text-xs">
                {label}
              </SelectItem>
            ))}
          </SelectContent>
        </Select>

        {/* 조인 대상 테이블 (Combobox) */}
        <Popover open={targetTableOpen} onOpenChange={setTargetTableOpen}>
          <PopoverTrigger asChild>
            <Button
              variant="outline"
              role="combobox"
              className="h-7 flex-1 justify-between text-[10px]"
            >
              {join.targetTable
                ? (tables.find((t) => t.tableName === join.targetTable)
                    ?.displayName ?? join.targetTable)
                : "테이블 선택"}
              <ChevronsUpDown className="ml-1 h-3 w-3 opacity-50" />
            </Button>
          </PopoverTrigger>
          <PopoverContent
            className="p-0"
            style={{ width: "var(--radix-popover-trigger-width)" }}
          >
            <Command>
              <CommandInput placeholder="검색..." className="text-xs" />
              <CommandList>
                <CommandEmpty className="py-2 text-center text-xs">
                  없음
                </CommandEmpty>
                <CommandGroup>
                  {tables
                    .filter((t) => t.tableName !== mainTable)
                    .map((t) => (
                      <CommandItem
                        key={t.tableName}
                        value={t.tableName}
                        onSelect={() => {
                          onUpdate({ targetTable: t.tableName });
                          setTargetTableOpen(false);
                        }}
                        className="text-xs"
                      >
                        {t.displayName || t.tableName}
                      </CommandItem>
                    ))}
                </CommandGroup>
              </CommandList>
            </Command>
          </PopoverContent>
        </Popover>

        {/* 삭제 */}
        <Button
          variant="ghost"
          size="icon"
          className="h-6 w-6 text-destructive"
          onClick={onRemove}
        >
          <Trash2 className="h-3 w-3" />
        </Button>
      </div>

      {/* 조인 조건 (ON 절) */}
      {join.targetTable && (
        <div className="flex items-center gap-1 text-[10px]">
          <span className="shrink-0 text-muted-foreground">ON</span>
          <Select
            value={join.on.sourceColumn}
            onValueChange={(val) =>
              onUpdate({ on: { ...join.on, sourceColumn: val } })
            }
          >
            <SelectTrigger className="h-6 text-[10px]">
              <SelectValue placeholder={`${mainTable} 컬럼`} />
            </SelectTrigger>
            <SelectContent>
              {sourceColumns.map((c) => (
                <SelectItem key={c.name} value={c.name} className="text-xs">
                  {c.name}
                </SelectItem>
              ))}
            </SelectContent>
          </Select>
          <span className="shrink-0 text-muted-foreground">=</span>
          <Select
            value={join.on.targetColumn}
            onValueChange={(val) =>
              onUpdate({ on: { ...join.on, targetColumn: val } })
            }
          >
            <SelectTrigger className="h-6 text-[10px]">
              <SelectValue placeholder={`${join.targetTable} 컬럼`} />
            </SelectTrigger>
            <SelectContent>
              {targetColumns.map((c) => (
                <SelectItem key={c.name} value={c.name} className="text-xs">
                  {c.name}
                </SelectItem>
              ))}
            </SelectContent>
          </Select>
        </div>
      )}
    </div>
  );
}

// ===== 필터 편집기 =====

function FilterEditor({
  filters,
  tableName,
  onChange,
}: {
  filters: DataSourceFilter[];
  tableName: string;
  onChange: (filters: DataSourceFilter[]) => void;
}) {
  const [columns, setColumns] = useState<ColumnInfo[]>([]);

  useEffect(() => {
    if (!tableName) return;
    fetchTableColumns(tableName).then(setColumns);
  }, [tableName]);

  const addFilter = () => {
    onChange([...filters, { column: "", operator: "=", value: "" }]);
  };

  const updateFilter = (
    index: number,
    partial: Partial<DataSourceFilter>
  ) => {
    const newFilters = [...filters];
    newFilters[index] = { ...newFilters[index], ...partial };

    // operator 변경 시 value 초기화
    if (partial.operator) {
      if (partial.operator === "between") {
        newFilters[index].value = ["", ""];
      } else if (partial.operator === "in") {
        newFilters[index].value = [];
      } else if (
        typeof newFilters[index].value !== "string" &&
        typeof newFilters[index].value !== "number"
      ) {
        newFilters[index].value = "";
      }
    }

    onChange(newFilters);
  };

  const removeFilter = (index: number) => {
    onChange(filters.filter((_, i) => i !== index));
  };

  return (
    <div className="space-y-2">
      <div className="flex items-center justify-between">
        <Label className="text-xs font-medium">필터 조건</Label>
        <Button
          variant="outline"
          size="sm"
          className="h-6 text-[10px]"
          onClick={addFilter}
          disabled={!tableName}
        >
          <Plus className="mr-1 h-3 w-3" />
          필터 추가
        </Button>
      </div>

      {filters.map((filter, index) => (
        <div key={index} className="flex items-start gap-1">
          {/* 컬럼 선택 */}
          <Select
            value={filter.column}
            onValueChange={(val) => updateFilter(index, { column: val })}
          >
            <SelectTrigger className="h-7 w-24 text-[10px]">
              <SelectValue placeholder="컬럼" />
            </SelectTrigger>
            <SelectContent>
              {columns.map((c) => (
                <SelectItem key={c.name} value={c.name} className="text-xs">
                  {c.name}
                </SelectItem>
              ))}
            </SelectContent>
          </Select>

          {/* 연산자 */}
          <Select
            value={filter.operator}
            onValueChange={(val) =>
              updateFilter(index, { operator: val as FilterOperator })
            }
          >
            <SelectTrigger className="h-7 w-20 text-[10px]">
              <SelectValue />
            </SelectTrigger>
            <SelectContent>
              {Object.entries(FILTER_OPERATOR_LABELS).map(([op, label]) => (
                <SelectItem key={op} value={op} className="text-xs">
                  {label}
                </SelectItem>
              ))}
            </SelectContent>
          </Select>

          {/* 값 입력 (연산자에 따라 다른 UI) */}
          <div className="flex-1">
            {filter.operator === "between" ? (
              <div className="flex gap-1">
                <Input
                  value={
                    Array.isArray(filter.value)
                      ? (filter.value[0] ?? "")
                      : ""
                  }
                  onChange={(e) => {
                    const arr = Array.isArray(filter.value)
                      ? [...filter.value]
                      : ["", ""];
                    arr[0] = e.target.value;
                    updateFilter(index, { value: arr });
                  }}
                  placeholder="시작"
                  className="h-7 text-[10px]"
                />
                <Input
                  value={
                    Array.isArray(filter.value)
                      ? (filter.value[1] ?? "")
                      : ""
                  }
                  onChange={(e) => {
                    const arr = Array.isArray(filter.value)
                      ? [...filter.value]
                      : ["", ""];
                    arr[1] = e.target.value;
                    updateFilter(index, { value: arr });
                  }}
                  placeholder="끝"
                  className="h-7 text-[10px]"
                />
              </div>
            ) : filter.operator === "in" ? (
              <Input
                value={
                  Array.isArray(filter.value)
                    ? filter.value.join(", ")
                    : String(filter.value ?? "")
                }
                onChange={(e) => {
                  const vals = e.target.value
                    .split(",")
                    .map((v) => v.trim())
                    .filter(Boolean);
                  updateFilter(index, { value: vals });
                }}
                placeholder="값1, 값2, 값3"
                className="h-7 text-[10px]"
              />
            ) : (
              <Input
                value={String(filter.value ?? "")}
                onChange={(e) =>
                  updateFilter(index, { value: e.target.value })
                }
                placeholder="값"
                className="h-7 text-[10px]"
              />
            )}
          </div>

          {/* 삭제 */}
          <Button
            variant="ghost"
            size="icon"
            className="h-7 w-7 text-destructive"
            onClick={() => removeFilter(index)}
          >
            <Trash2 className="h-3 w-3" />
          </Button>
        </div>
      ))}
    </div>
  );
}

// ===== 수식 편집기 =====

function FormulaEditor({
  formula,
  onChange,
}: {
  formula: FormulaConfig;
  onChange: (f: FormulaConfig) => void;
}) {
  const availableIds = formula.values.map((v) => v.id);
  const isValid = formula.expression
    ? validateExpression(formula.expression, availableIds)
    : true;

  return (
    <div className="space-y-3 rounded-md border p-2">
      <p className="text-xs font-medium">계산식 설정</p>

      {/* 값 목록 */}
      {formula.values.map((fv, index) => (
        <div key={fv.id} className="space-y-1">
          <div className="flex items-center gap-2">
            <span className="rounded bg-primary/10 px-1.5 py-0.5 text-xs font-bold text-primary">
              {fv.id}
            </span>
            <Input
              value={fv.label}
              onChange={(e) => {
                const newValues = [...formula.values];
                newValues[index] = { ...fv, label: e.target.value };
                onChange({ ...formula, values: newValues });
              }}
              placeholder="라벨 (예: 생산량)"
              className="h-7 flex-1 text-xs"
            />
            {formula.values.length > 2 && (
              <Button
                variant="ghost"
                size="icon"
                className="h-7 w-7"
                onClick={() => {
                  const newValues = formula.values.filter(
                    (_, i) => i !== index
                  );
                  onChange({ ...formula, values: newValues });
                }}
              >
                <Trash2 className="h-3 w-3" />
              </Button>
            )}
          </div>
          <DataSourceEditor
            dataSource={fv.dataSource}
            onChange={(ds) => {
              const newValues = [...formula.values];
              newValues[index] = { ...fv, dataSource: ds };
              onChange({ ...formula, values: newValues });
            }}
          />
        </div>
      ))}

      {/* 값 추가 */}
      <Button
        variant="outline"
        size="sm"
        className="h-7 w-full text-xs"
        onClick={() => {
          const nextId = String.fromCharCode(65 + formula.values.length);
          onChange({
            ...formula,
            values: [
              ...formula.values,
              {
                id: nextId,
                label: "",
                dataSource: { ...DEFAULT_DATASOURCE },
              },
            ],
          });
        }}
      >
        <Plus className="mr-1 h-3 w-3" />
        값 추가
      </Button>

      {/* 수식 입력 */}
      <div>
        <Label className="text-xs">수식</Label>
        <Input
          value={formula.expression}
          onChange={(e) =>
            onChange({ ...formula, expression: e.target.value })
          }
          placeholder="예: A / B * 100"
          className={`h-8 text-xs ${!isValid ? "border-destructive" : ""}`}
        />
        {!isValid && (
          <p className="mt-0.5 text-[10px] text-destructive">
            수식에 정의되지 않은 변수가 있습니다
          </p>
        )}
      </div>

      {/* 표시 형태 */}
      <div>
        <Label className="text-xs">표시 형태</Label>
        <Select
          value={formula.displayFormat}
          onValueChange={(val) =>
            onChange({
              ...formula,
              displayFormat: val as FormulaConfig["displayFormat"],
            })
          }
        >
          <SelectTrigger className="h-8 text-xs">
            <SelectValue />
          </SelectTrigger>
          <SelectContent>
            <SelectItem value="value">계산 결과 숫자</SelectItem>
            <SelectItem value="fraction">분수 (1,234 / 5,678)</SelectItem>
            <SelectItem value="percent">퍼센트 (21.7%)</SelectItem>
            <SelectItem value="ratio">비율 (1,234 : 5,678)</SelectItem>
          </SelectContent>
        </Select>
      </div>
    </div>
  );
}

// ===== 아이템 편집기 =====

function ItemEditor({
  item,
  index,
  onUpdate,
  onDelete,
  onMoveUp,
  onMoveDown,
  isFirst,
  isLast,
}: {
  item: DashboardItem;
  index: number;
  onUpdate: (item: DashboardItem) => void;
  onDelete: () => void;
  onMoveUp: () => void;
  onMoveDown: () => void;
  isFirst: boolean;
  isLast: boolean;
}) {
  const [expanded, setExpanded] = useState(false);
  const [dataMode, setDataMode] = useState<"single" | "formula">(
    item.formula?.enabled ? "formula" : "single"
  );

  return (
    <div className="rounded-md border p-2">
      {/* 헤더 */}
      <div className="flex items-center gap-1">
        <GripVertical className="h-4 w-4 shrink-0 text-muted-foreground" />
        <Input
          value={item.label}
          onChange={(e) => onUpdate({ ...item, label: e.target.value })}
          placeholder={`아이템 ${index + 1}`}
          className="h-6 min-w-0 flex-1 border-0 bg-transparent px-1 text-xs font-medium shadow-none focus-visible:ring-1"
        />
        <span className="shrink-0 rounded bg-muted px-1 py-0.5 text-[10px]">
          {SUBTYPE_LABELS[item.subType]}
        </span>

        <Button
          variant="ghost"
          size="icon"
          className="h-6 w-6"
          onClick={onMoveUp}
          disabled={isFirst}
        >
          <ChevronUp className="h-3 w-3" />
        </Button>
        <Button
          variant="ghost"
          size="icon"
          className="h-6 w-6"
          onClick={onMoveDown}
          disabled={isLast}
        >
          <ChevronDown className="h-3 w-3" />
        </Button>

        <Switch
          checked={item.visible}
          onCheckedChange={(checked) =>
            onUpdate({ ...item, visible: checked })
          }
          className="scale-75"
        />

        <Button
          variant="ghost"
          size="icon"
          className="h-6 w-6"
          onClick={() => setExpanded(!expanded)}
        >
          {expanded ? (
            <ChevronUp className="h-3 w-3" />
          ) : (
            <ChevronDown className="h-3 w-3" />
          )}
        </Button>

        <Button
          variant="ghost"
          size="icon"
          className="h-6 w-6 text-destructive"
          onClick={onDelete}
        >
          <Trash2 className="h-3 w-3" />
        </Button>
      </div>

      {/* 상세 설정 */}
      {expanded && (
        <div className="mt-2 space-y-3">
          <div>
            <Label className="text-xs">타입</Label>
            <Select
              value={item.subType}
              onValueChange={(val) => {
                const newSubType = val as DashboardSubType;
                const allowedAggs = SUBTYPE_AGGREGATION_MAP[newSubType];
                const currentAggType = item.dataSource.aggregation?.type;

                // STEP 7.5: subType 변경 시, 현재 집계 함수가 새 타입에서 허용되지 않으면 자동 전환
                let newDataSource = item.dataSource;
                if (currentAggType && !allowedAggs.includes(currentAggType)) {
                  const fallbackAgg = allowedAggs[0]; // 허용되는 첫 번째 집계 함수로 전환
                  newDataSource = {
                    ...item.dataSource,
                    aggregation: item.dataSource.aggregation
                      ? {
                          ...item.dataSource.aggregation,
                          type: fallbackAgg,
                          // count로 전환되면 컬럼 의미 없으므로 초기화
                          column: fallbackAgg === "count" ? "" : item.dataSource.aggregation.column,
                        }
                      : undefined,
                  };
                }

                onUpdate({ ...item, subType: newSubType, dataSource: newDataSource });
              }}
            >
              <SelectTrigger className="h-8 text-xs">
                <SelectValue />
              </SelectTrigger>
              <SelectContent>
                <SelectItem value="kpi-card">KPI 카드</SelectItem>
                <SelectItem value="chart">차트</SelectItem>
                <SelectItem value="gauge">게이지</SelectItem>
                <SelectItem value="stat-card">통계 카드</SelectItem>
              </SelectContent>
            </Select>
          </div>

          <div>
            <Label className="text-xs">데이터 모드</Label>
            <Select
              value={dataMode}
              onValueChange={(val) => {
                const mode = val as "single" | "formula";
                setDataMode(mode);
                if (mode === "formula" && !item.formula) {
                  onUpdate({
                    ...item,
                    formula: {
                      enabled: true,
                      values: [
                        {
                          id: "A",
                          label: "",
                          dataSource: { ...DEFAULT_DATASOURCE },
                        },
                        {
                          id: "B",
                          label: "",
                          dataSource: { ...DEFAULT_DATASOURCE },
                        },
                      ],
                      expression: "A / B",
                      displayFormat: "value",
                    },
                  });
                } else if (mode === "single") {
                  onUpdate({ ...item, formula: undefined });
                }
              }}
            >
              <SelectTrigger className="h-8 text-xs">
                <SelectValue />
              </SelectTrigger>
              <SelectContent>
                <SelectItem value="single">단일 집계</SelectItem>
                <SelectItem value="formula">계산식</SelectItem>
              </SelectContent>
            </Select>
          </div>

          {dataMode === "formula" && item.formula ? (
            <FormulaEditor
              formula={item.formula}
              onChange={(f) => onUpdate({ ...item, formula: f })}
            />
          ) : (
            <DataSourceEditor
              dataSource={item.dataSource}
              onChange={(ds) => onUpdate({ ...item, dataSource: ds })}
              subType={item.subType}
            />
          )}

          {/* 요소별 보이기/숨기기 */}
          <div>
            <Label className="text-xs">표시 요소</Label>
            <div className="mt-1 grid grid-cols-2 gap-1">
              {(
                [
                  ["showLabel", "라벨"],
                  ["showValue", "값"],
                  ["showUnit", "단위"],
                  ["showTrend", "증감율"],
                  ["showSubLabel", "보조라벨"],
                  ["showTarget", "목표값"],
                ] as const
              ).map(([key, label]) => (
                <label
                  key={key}
                  className="flex items-center gap-1.5 text-xs"
                >
                  <input
                    type="checkbox"
                    checked={item.visibility[key]}
                    onChange={(e) =>
                      onUpdate({
                        ...item,
                        visibility: {
                          ...item.visibility,
                          [key]: e.target.checked,
                        },
                      })
                    }
                    className="h-3 w-3 rounded border-input"
                  />
                  {label}
                </label>
              ))}
            </div>
          </div>

          {/* 서브타입별 추가 설정 */}
          {item.subType === "kpi-card" && (
            <div>
              <Label className="text-xs">단위</Label>
              <Input
                value={item.kpiConfig?.unit ?? ""}
                onChange={(e) =>
                  onUpdate({
                    ...item,
                    kpiConfig: { ...item.kpiConfig, unit: e.target.value },
                  })
                }
                placeholder="EA, 톤, 원"
                className="h-8 text-xs"
              />
            </div>
          )}

          {item.subType === "chart" && (
            <div className="space-y-2">
              <div>
                <Label className="text-xs">차트 유형</Label>
                <Select
                  value={item.chartConfig?.chartType ?? "bar"}
                  onValueChange={(val) =>
                    onUpdate({
                      ...item,
                      chartConfig: {
                        ...item.chartConfig,
                        chartType: val as "bar" | "pie" | "line",
                      },
                    })
                  }
                >
                  <SelectTrigger className="h-8 text-xs">
                    <SelectValue />
                  </SelectTrigger>
                  <SelectContent>
                    <SelectItem value="bar">막대 차트</SelectItem>
                    <SelectItem value="pie">원형 차트</SelectItem>
                    <SelectItem value="line">라인 차트</SelectItem>
                  </SelectContent>
                </Select>
              </div>

              {/* X축/Y축 자동 안내 */}
              <p className="text-[10px] text-muted-foreground">
                X축: 그룹핑(X축)에서 선택한 컬럼 자동 적용 / Y축: 집계 결과(value) 자동 적용
              </p>
            </div>
          )}

          {item.subType === "gauge" && (
            <div className="grid grid-cols-3 gap-2">
              <div>
                <Label className="text-xs">최소</Label>
                <Input
                  type="number"
                  value={item.gaugeConfig?.min ?? 0}
                  onChange={(e) =>
                    onUpdate({
                      ...item,
                      gaugeConfig: {
                        ...item.gaugeConfig,
                        min: parseInt(e.target.value) || 0,
                        max: item.gaugeConfig?.max ?? 100,
                      },
                    })
                  }
                  className="h-8 text-xs"
                />
              </div>
              <div>
                <Label className="text-xs">최대</Label>
                <Input
                  type="number"
                  value={item.gaugeConfig?.max ?? 100}
                  onChange={(e) =>
                    onUpdate({
                      ...item,
                      gaugeConfig: {
                        ...item.gaugeConfig,
                        min: item.gaugeConfig?.min ?? 0,
                        max: parseInt(e.target.value) || 100,
                      },
                    })
                  }
                  className="h-8 text-xs"
                />
              </div>
              <div>
                <Label className="text-xs">목표</Label>
                <Input
                  type="number"
                  value={item.gaugeConfig?.target ?? ""}
                  onChange={(e) =>
                    onUpdate({
                      ...item,
                      gaugeConfig: {
                        ...item.gaugeConfig,
                        min: item.gaugeConfig?.min ?? 0,
                        max: item.gaugeConfig?.max ?? 100,
                        target: parseInt(e.target.value) || undefined,
                      },
                    })
                  }
                  className="h-8 text-xs"
                />
              </div>
            </div>
          )}

          {/* 통계 카드 카테고리 설정 */}
          {item.subType === "stat-card" && (
            <div className="space-y-2">
              <div className="flex items-center justify-between">
                <Label className="text-xs font-medium">카테고리 설정</Label>
                <Button
                  variant="outline"
                  size="sm"
                  className="h-6 text-[10px]"
                  onClick={() => {
                    const currentCats = item.statConfig?.categories ?? [];
                    onUpdate({
                      ...item,
                      statConfig: {
                        ...item.statConfig,
                        categories: [
                          ...currentCats,
                          {
                            label: `카테고리 ${currentCats.length + 1}`,
                            filter: { column: "", operator: "=", value: "" },
                          },
                        ],
                      },
                    });
                  }}
                >
                  <Plus className="mr-1 h-3 w-3" />
                  카테고리 추가
                </Button>
              </div>

              {(item.statConfig?.categories ?? []).map((cat, catIdx) => (
                <div key={catIdx} className="space-y-1 rounded border p-2">
                  <div className="flex items-center gap-1">
                    <Input
                      value={cat.label}
                      onChange={(e) => {
                        const newCats = [...(item.statConfig?.categories ?? [])];
                        newCats[catIdx] = { ...cat, label: e.target.value };
                        onUpdate({
                          ...item,
                          statConfig: { ...item.statConfig, categories: newCats },
                        });
                      }}
                      placeholder="라벨 (예: 수주)"
                      className="h-6 flex-1 text-xs"
                    />
                    <Input
                      value={cat.color ?? ""}
                      onChange={(e) => {
                        const newCats = [...(item.statConfig?.categories ?? [])];
                        newCats[catIdx] = { ...cat, color: e.target.value || undefined };
                        onUpdate({
                          ...item,
                          statConfig: { ...item.statConfig, categories: newCats },
                        });
                      }}
                      placeholder="#색상코드"
                      className="h-6 w-20 text-xs"
                    />
                    <Button
                      variant="ghost"
                      size="icon"
                      className="h-6 w-6 text-destructive"
                      onClick={() => {
                        const newCats = (item.statConfig?.categories ?? []).filter(
                          (_, i) => i !== catIdx
                        );
                        onUpdate({
                          ...item,
                          statConfig: { ...item.statConfig, categories: newCats },
                        });
                      }}
                    >
                      <Trash2 className="h-3 w-3" />
                    </Button>
                  </div>
                  {/* 필터 조건: 컬럼 / 연산자 / 값 */}
                  <div className="flex items-center gap-1 text-[10px]">
                    <Input
                      value={cat.filter.column}
                      onChange={(e) => {
                        const newCats = [...(item.statConfig?.categories ?? [])];
                        newCats[catIdx] = {
                          ...cat,
                          filter: { ...cat.filter, column: e.target.value },
                        };
                        onUpdate({
                          ...item,
                          statConfig: { ...item.statConfig, categories: newCats },
                        });
                      }}
                      placeholder="컬럼"
                      className="h-6 w-20 text-[10px]"
                    />
                    <Select
                      value={cat.filter.operator}
                      onValueChange={(val) => {
                        const newCats = [...(item.statConfig?.categories ?? [])];
                        newCats[catIdx] = {
                          ...cat,
                          filter: { ...cat.filter, operator: val as FilterOperator },
                        };
                        onUpdate({
                          ...item,
                          statConfig: { ...item.statConfig, categories: newCats },
                        });
                      }}
                    >
                      <SelectTrigger className="h-6 w-16 text-[10px]">
                        <SelectValue />
                      </SelectTrigger>
                      <SelectContent>
                        <SelectItem value="=" className="text-xs">= 같음</SelectItem>
                        <SelectItem value="!=" className="text-xs">!= 다름</SelectItem>
                        <SelectItem value="like" className="text-xs">LIKE</SelectItem>
                      </SelectContent>
                    </Select>
                    <Input
                      value={String(cat.filter.value ?? "")}
                      onChange={(e) => {
                        const newCats = [...(item.statConfig?.categories ?? [])];
                        newCats[catIdx] = {
                          ...cat,
                          filter: { ...cat.filter, value: e.target.value },
                        };
                        onUpdate({
                          ...item,
                          statConfig: { ...item.statConfig, categories: newCats },
                        });
                      }}
                      placeholder="값"
                      className="h-6 flex-1 text-[10px]"
                    />
                  </div>
                </div>
              ))}

              {(item.statConfig?.categories ?? []).length === 0 && (
                <p className="text-[10px] text-muted-foreground">
                  카테고리를 추가하면 각 조건에 맞는 건수가 표시됩니다
                </p>
              )}
            </div>
          )}
        </div>
      )}
    </div>
  );
}

// ===== 그리드 레이아웃 편집기 =====

/** 기본 셀 그리드 생성 헬퍼 */
function generateDefaultCells(
  cols: number,
  rows: number
): DashboardCell[] {
  const cells: DashboardCell[] = [];
  for (let r = 0; r < rows; r++) {
    for (let c = 0; c < cols; c++) {
      cells.push({
        id: `cell-${r}-${c}`,
        gridColumn: `${c + 1} / ${c + 2}`,
        gridRow: `${r + 1} / ${r + 2}`,
        itemId: null,
      });
    }
  }
  return cells;
}

// =====================================================
// 아이템 스타일 에디터 (접기/펼치기 지원)
// =====================================================
function ItemStyleEditor({
  item,
  onUpdate,
}: {
  item: DashboardItem;
  onUpdate: (updatedItem: DashboardItem) => void;
}) {
  const [expanded, setExpanded] = useState(false);

  const updateStyle = (partial: Partial<ItemStyleConfig>) => {
    const updatedItem = {
      ...item,
      itemStyle: { ...item.itemStyle, ...partial },
    };
    onUpdate(updatedItem);
  };

  return (
    <div className="rounded border">
      {/* 헤더 - 클릭으로 접기/펼치기 */}
      <button
        type="button"
        className="flex w-full items-center justify-between px-2 py-1.5 text-left hover:bg-muted/50 transition-colors"
        onClick={() => setExpanded(!expanded)}
      >
        <span className="text-[10px] font-medium truncate">
          {item.label || item.id}
        </span>
        {expanded ? (
          <ChevronUp className="h-3 w-3 shrink-0 text-muted-foreground" />
        ) : (
          <ChevronDown className="h-3 w-3 shrink-0 text-muted-foreground" />
        )}
      </button>

      {/* 내용 - 접기/펼치기 */}
      {expanded && (
        <div className="space-y-2 border-t px-2 pb-2 pt-1.5">
          {/* 라벨 정렬 */}
          <div>
            <span className="text-[9px] text-muted-foreground">
              라벨 정렬
            </span>
            <div className="mt-0.5 flex gap-1">
              {(["left", "center", "right"] as const).map((align) => (
                <Button
                  key={align}
                  variant={
                    item.itemStyle?.labelAlign === align ||
                    (!item.itemStyle?.labelAlign && align === "center")
                      ? "default"
                      : "outline"
                  }
                  size="sm"
                  className="h-6 flex-1 text-[10px] px-1"
                  onClick={() => updateStyle({ labelAlign: align })}
                >
                  {TEXT_ALIGN_LABELS[align]}
                </Button>
              ))}
            </div>
          </div>
        </div>
      )}
    </div>
  );
}

function GridLayoutEditor({
  cells,
  gridColumns,
  gridRows,
  items,
  onChange,
  onUpdateItem,
}: {
  cells: DashboardCell[];
  gridColumns: number;
  gridRows: number;
  items: DashboardItem[];
  onChange: (cells: DashboardCell[], cols: number, rows: number) => void;
  /** 아이템 스타일 업데이트 콜백 */
  onUpdateItem?: (updatedItem: DashboardItem) => void;
}) {
  const ensuredCells =
    cells.length > 0 ? cells : generateDefaultCells(gridColumns, gridRows);

  return (
    <div className="space-y-3">
      {/* 행/열 조절 버튼 */}
      <div className="flex items-center gap-3">
        <div className="flex items-center gap-1">
          <Label className="text-xs">열</Label>
          <Button
            variant="outline"
            size="icon"
            className="h-6 w-6"
            onClick={() => {
              if (gridColumns > 1) {
                const newCells = ensuredCells.filter((c) => {
                  const col = parseInt(c.gridColumn.split(" / ")[0]);
                  return col <= gridColumns - 1;
                });
                onChange(newCells, gridColumns - 1, gridRows);
              }
            }}
            disabled={gridColumns <= 1}
          >
            <span className="text-xs">-</span>
          </Button>
          <span className="w-6 text-center text-xs font-medium">
            {gridColumns}
          </span>
          <Button
            variant="outline"
            size="icon"
            className="h-6 w-6"
            onClick={() => {
              if (gridColumns < 6) {
                const newCells = [...ensuredCells];
                for (let r = 0; r < gridRows; r++) {
                  newCells.push({
                    id: `cell-${r}-${gridColumns}`,
                    gridColumn: `${gridColumns + 1} / ${gridColumns + 2}`,
                    gridRow: `${r + 1} / ${r + 2}`,
                    itemId: null,
                  });
                }
                onChange(newCells, gridColumns + 1, gridRows);
              }
            }}
            disabled={gridColumns >= 6}
          >
            <span className="text-xs">+</span>
          </Button>
        </div>

        <div className="flex items-center gap-1">
          <Label className="text-xs">행</Label>
          <Button
            variant="outline"
            size="icon"
            className="h-6 w-6"
            onClick={() => {
              if (gridRows > 1) {
                const newCells = ensuredCells.filter((c) => {
                  const row = parseInt(c.gridRow.split(" / ")[0]);
                  return row <= gridRows - 1;
                });
                onChange(newCells, gridColumns, gridRows - 1);
              }
            }}
            disabled={gridRows <= 1}
          >
            <span className="text-xs">-</span>
          </Button>
          <span className="w-6 text-center text-xs font-medium">
            {gridRows}
          </span>
          <Button
            variant="outline"
            size="icon"
            className="h-6 w-6"
            onClick={() => {
              if (gridRows < 6) {
                const newCells = [...ensuredCells];
                for (let c = 0; c < gridColumns; c++) {
                  newCells.push({
                    id: `cell-${gridRows}-${c}`,
                    gridColumn: `${c + 1} / ${c + 2}`,
                    gridRow: `${gridRows + 1} / ${gridRows + 2}`,
                    itemId: null,
                  });
                }
                onChange(newCells, gridColumns, gridRows + 1);
              }
            }}
            disabled={gridRows >= 6}
          >
            <span className="text-xs">+</span>
          </Button>
        </div>

        <Button
          variant="ghost"
          size="sm"
          className="ml-auto h-6 text-[10px]"
          onClick={() =>
            onChange(
              generateDefaultCells(gridColumns, gridRows),
              gridColumns,
              gridRows
            )
          }
        >
          초기화
        </Button>
      </div>

      {/* 시각적 그리드 프리뷰 + 아이템 배정 */}
      <div
        className="gap-1 rounded border p-2"
        style={{
          display: "grid",
          gridTemplateColumns: `repeat(${gridColumns}, 1fr)`,
          gridTemplateRows: `repeat(${gridRows}, 48px)`,
        }}
      >
        {ensuredCells.map((cell) => (
          <div
            key={cell.id}
            className={cn(
              "flex items-center justify-center rounded border border-dashed p-0.5 transition-colors",
              cell.itemId
                ? "border-primary/50 bg-primary/5"
                : "border-muted-foreground/30 hover:border-muted-foreground/50"
            )}
            style={{
              gridColumn: cell.gridColumn,
              gridRow: cell.gridRow,
            }}
          >
            <Select
              value={cell.itemId ?? "empty"}
              onValueChange={(val) => {
                const newCells = ensuredCells.map((c) =>
                  c.id === cell.id
                    ? { ...c, itemId: val === "empty" ? null : val }
                    : c
                );
                onChange(newCells, gridColumns, gridRows);
              }}
            >
              <SelectTrigger className="h-full w-full border-0 bg-transparent p-0 text-[10px] shadow-none">
                <SelectValue placeholder="빈 셀" />
              </SelectTrigger>
              <SelectContent>
                <SelectItem value="empty">빈 셀</SelectItem>
                {items.map((item) => (
                  <SelectItem key={item.id} value={item.id}>
                    {item.label || item.id}
                  </SelectItem>
                ))}
              </SelectContent>
            </Select>
          </div>
        ))}
      </div>

      <p className="text-[10px] text-muted-foreground">
        각 셀을 클릭하여 아이템을 배정하세요. +/- 버튼으로 행/열을
        추가/삭제할 수 있습니다.
      </p>

      {/* 배정된 아이템별 스타일 설정 */}
      {onUpdateItem && (() => {
        const assignedItemIds = ensuredCells
          .map((c) => c.itemId)
          .filter((id): id is string => !!id);
        const uniqueIds = [...new Set(assignedItemIds)];
        const assignedItems = uniqueIds
          .map((id) => items.find((i) => i.id === id))
          .filter((i): i is DashboardItem => !!i);

        if (assignedItems.length === 0) return null;

        return (
          <div className="space-y-1 border-t pt-2">
            <span className="text-[10px] font-medium text-muted-foreground">
              아이템 스타일
            </span>
            {assignedItems.map((item) => (
              <ItemStyleEditor
                key={item.id}
                item={item}
                onUpdate={onUpdateItem}
              />
            ))}
          </div>
        );
      })()}
    </div>
  );
}

// ===== 페이지 편집기 =====

function PageEditor({
  page,
  pageIndex,
  items,
  onChange,
  onDelete,
  onPreview,
  isPreviewing,
  onUpdateItem,
}: {
  page: DashboardPage;
  pageIndex: number;
  items: DashboardItem[];
  onChange: (updatedPage: DashboardPage) => void;
  onDelete: () => void;
  onPreview?: () => void;
  isPreviewing?: boolean;
  onUpdateItem?: (updatedItem: DashboardItem) => void;
}) {
  const [expanded, setExpanded] = useState(false);

  return (
    <div className="rounded-md border p-2">
      {/* 헤더 */}
      <div className="flex items-center gap-1">
        <span className="flex-1 truncate text-xs font-medium">
          {page.label || `페이지 ${pageIndex + 1}`}
        </span>
        <span className="rounded bg-muted px-1 py-0.5 text-[10px] text-muted-foreground">
          {page.gridColumns}x{page.gridRows}
        </span>
        <Button
          variant={isPreviewing ? "default" : "ghost"}
          size="icon"
          className="h-6 w-6"
          onClick={() => onPreview?.()}
          title="이 페이지 미리보기"
        >
          <Eye className="h-3 w-3" />
        </Button>
        <Button
          variant="ghost"
          size="icon"
          className="h-6 w-6"
          onClick={() => setExpanded(!expanded)}
        >
          {expanded ? (
            <ChevronUp className="h-3 w-3" />
          ) : (
            <ChevronDown className="h-3 w-3" />
          )}
        </Button>
        <Button
          variant="ghost"
          size="icon"
          className="h-6 w-6 text-destructive"
          onClick={onDelete}
        >
          <Trash2 className="h-3 w-3" />
        </Button>
      </div>

      {/* 상세 */}
      {expanded && (
        <div className="mt-2 space-y-2">
          {/* 라벨 */}
          <div>
            <Label className="text-xs">라벨</Label>
            <Input
              value={page.label ?? ""}
              onChange={(e) =>
                onChange({ ...page, label: e.target.value })
              }
              placeholder={`페이지 ${pageIndex + 1}`}
              className="h-7 text-xs"
            />
          </div>

          {/* GridLayoutEditor 재사용 */}
          <GridLayoutEditor
            cells={page.gridCells}
            gridColumns={page.gridColumns}
            gridRows={page.gridRows}
            items={items}
            onChange={(cells, cols, rows) =>
              onChange({
                ...page,
                gridCells: cells,
                gridColumns: cols,
                gridRows: rows,
              })
            }
            onUpdateItem={onUpdateItem}
          />
        </div>
      )}
    </div>
  );
}

// ===== 메인 설정 패널 =====

export function PopDashboardConfigPanel(props: ConfigPanelProps) {
  const { config, onUpdate: onChange } = props;
  // config가 빈 객체 {}로 전달될 수 있으므로 spread로 기본값 보장
  const merged = { ...DEFAULT_CONFIG, ...(config || {}) };

  // 마이그레이션: 기존 useGridLayout/grid* -> pages 기반으로 변환
  const cfg = migrateConfig(
    merged as unknown as Record<string, unknown>
  ) as PopDashboardConfig;

  const [activeTab, setActiveTab] = useState<"basic" | "items" | "pages">(
    "basic"
  );

  // 설정 변경 헬퍼
  const updateConfig = useCallback(
    (partial: Partial<PopDashboardConfig>) => {
      onChange({ ...cfg, ...partial });
    },
    [cfg, onChange]
  );

  // 아이템 추가
  const addItem = useCallback(
    (subType: DashboardSubType) => {
      const newItem: DashboardItem = {
        id: `item-${Date.now()}`,
        label: `${SUBTYPE_LABELS[subType]} ${cfg.items.length + 1}`,
        visible: true,
        subType,
        dataSource: { ...DEFAULT_DATASOURCE },
        visibility: { ...DEFAULT_VISIBILITY },
      };
      updateConfig({ items: [...cfg.items, newItem] });
    },
    [cfg.items, updateConfig]
  );

  // 아이템 업데이트
  const updateItem = useCallback(
    (index: number, item: DashboardItem) => {
      const newItems = [...cfg.items];
      newItems[index] = item;
      updateConfig({ items: newItems });
    },
    [cfg.items, updateConfig]
  );

  // 아이템 삭제 (모든 페이지의 셀 배정도 해제)
  const deleteItem = useCallback(
    (index: number) => {
      const deletedId = cfg.items[index].id;
      const newItems = cfg.items.filter((_, i) => i !== index);

      const newPages = cfg.pages?.map((page) => ({
        ...page,
        gridCells: page.gridCells.map((cell) =>
          cell.itemId === deletedId ? { ...cell, itemId: null } : cell
        ),
      }));

      updateConfig({ items: newItems, pages: newPages });
    },
    [cfg.items, cfg.pages, updateConfig]
  );

  // 아이템 순서 변경
  const moveItem = useCallback(
    (from: number, to: number) => {
      if (to < 0 || to >= cfg.items.length) return;
      const newItems = [...cfg.items];
      const [moved] = newItems.splice(from, 1);
      newItems.splice(to, 0, moved);
      updateConfig({ items: newItems });
    },
    [cfg.items, updateConfig]
  );

  return (
    <div className="space-y-3">
      {/* 탭 헤더 */}
      <div className="flex gap-1 border-b pb-1">
        {(
          [
            ["basic", "기본 설정"],
            ["items", "아이템"],
            ["pages", "페이지"],
          ] as const
        ).map(([key, label]) => (
          <button
            key={key}
            type="button"
            onClick={() => setActiveTab(key)}
            className={`rounded-t px-2 py-1 text-xs font-medium transition-colors ${
              activeTab === key
                ? "bg-primary text-primary-foreground"
                : "text-muted-foreground hover:bg-muted"
            }`}
          >
            {label}
          </button>
        ))}
      </div>

      {/* ===== 기본 설정 탭 ===== */}
      {activeTab === "basic" && (
        <div className="space-y-3">
          {/* 표시 모드 */}
          <div>
            <Label className="text-xs">표시 모드</Label>
            <Select
              value={cfg.displayMode}
              onValueChange={(val) =>
                updateConfig({
                  displayMode: val as DashboardDisplayMode,
                })
              }
            >
              <SelectTrigger className="h-8 text-xs">
                <SelectValue />
              </SelectTrigger>
              <SelectContent>
                {Object.entries(DISPLAY_MODE_LABELS).map(([val, label]) => (
                  <SelectItem key={val} value={val}>
                    {label}
                  </SelectItem>
                ))}
              </SelectContent>
            </Select>
          </div>

          {/* 자동 슬라이드 설정 */}
          {cfg.displayMode === "auto-slide" && (
            <div className="grid grid-cols-2 gap-2">
              <div>
                <Label className="text-xs">전환 간격 (초)</Label>
                <Input
                  type="number"
                  value={cfg.autoSlideInterval ?? 5}
                  onChange={(e) =>
                    updateConfig({
                      autoSlideInterval: parseInt(e.target.value) || 5,
                    })
                  }
                  className="h-8 text-xs"
                  min={1}
                />
              </div>
              <div>
                <Label className="text-xs">재개 대기 (초)</Label>
                <Input
                  type="number"
                  value={cfg.autoSlideResumeDelay ?? 3}
                  onChange={(e) =>
                    updateConfig({
                      autoSlideResumeDelay: parseInt(e.target.value) || 3,
                    })
                  }
                  className="h-8 text-xs"
                  min={1}
                />
              </div>
            </div>
          )}

          {/* 인디케이터 */}
          <div className="flex items-center justify-between">
            <Label className="text-xs">페이지 인디케이터</Label>
            <Switch
              checked={cfg.showIndicator ?? true}
              onCheckedChange={(checked) =>
                updateConfig({ showIndicator: checked })
              }
            />
          </div>

          {/* 간격 */}
          <div>
            <Label className="text-xs">아이템 간격 (px)</Label>
            <Input
              type="number"
              value={cfg.gap ?? 8}
              onChange={(e) =>
                updateConfig({ gap: parseInt(e.target.value) || 8 })
              }
              className="h-8 text-xs"
              min={0}
            />
          </div>

          {/* 배경색 */}
          <div>
            <Label className="text-xs">배경색</Label>
            <Input
              value={cfg.backgroundColor ?? ""}
              onChange={(e) =>
                updateConfig({
                  backgroundColor: e.target.value || undefined,
                })
              }
              placeholder="예: #f0f0f0"
              className="h-8 text-xs"
            />
          </div>
        </div>
      )}

      {/* ===== 아이템 관리 탭 ===== */}
      {activeTab === "items" && (
        <div className="space-y-2">
          {cfg.items.map((item, index) => (
            <ItemEditor
              key={item.id}
              item={item}
              index={index}
              onUpdate={(updated) => updateItem(index, updated)}
              onDelete={() => deleteItem(index)}
              onMoveUp={() => moveItem(index, index - 1)}
              onMoveDown={() => moveItem(index, index + 1)}
              isFirst={index === 0}
              isLast={index === cfg.items.length - 1}
            />
          ))}

          <div className="grid grid-cols-2 gap-1">
            {(Object.keys(SUBTYPE_LABELS) as DashboardSubType[]).map(
              (subType) => (
                <Button
                  key={subType}
                  variant="outline"
                  size="sm"
                  className="h-8 text-xs"
                  onClick={() => addItem(subType)}
                >
                  <Plus className="mr-1 h-3 w-3" />
                  {SUBTYPE_LABELS[subType]}
                </Button>
              )
            )}
          </div>
        </div>
      )}

      {/* ===== 페이지 탭 ===== */}
      {activeTab === "pages" && (
        <div className="space-y-3">
          {/* 페이지 목록 */}
          {(cfg.pages ?? []).map((page, pageIdx) => (
            <PageEditor
              key={page.id}
              page={page}
              pageIndex={pageIdx}
              items={cfg.items}
              onChange={(updatedPage) => {
                const newPages = [...(cfg.pages ?? [])];
                newPages[pageIdx] = updatedPage;
                updateConfig({ pages: newPages });
              }}
              onDelete={() => {
                const newPages = (cfg.pages ?? []).filter(
                  (_, i) => i !== pageIdx
                );
                updateConfig({ pages: newPages });
              }}
              onPreview={() => {
                if (props.onPreviewPage) {
                  // 같은 페이지를 다시 누르면 미리보기 해제
                  props.onPreviewPage(props.previewPageIndex === pageIdx ? -1 : pageIdx);
                }
              }}
              isPreviewing={props.previewPageIndex === pageIdx}
              onUpdateItem={(updatedItem) => {
                const newItems = cfg.items.map((i) =>
                  i.id === updatedItem.id ? updatedItem : i
                );
                updateConfig({ items: newItems });
                // 스타일 변경 시 자동으로 해당 페이지 미리보기 활성화
                if (props.onPreviewPage && props.previewPageIndex !== pageIdx) {
                  props.onPreviewPage(pageIdx);
                }
              }}
            />
          ))}

          {/* 페이지 추가 버튼 */}
          <Button
            variant="outline"
            className="h-9 w-full text-xs"
            onClick={() => {
              const newPage: DashboardPage = {
                id: `page-${Date.now()}`,
                label: `페이지 ${(cfg.pages?.length ?? 0) + 1}`,
                gridColumns: 2,
                gridRows: 2,
                gridCells: generateDefaultCells(2, 2),
              };
              updateConfig({ pages: [...(cfg.pages ?? []), newPage] });
            }}
          >
            <Plus className="mr-1 h-4 w-4" />
            페이지 추가
          </Button>

          {(cfg.pages?.length ?? 0) === 0 && (
            <p className="text-center text-[10px] text-muted-foreground">
              페이지를 추가하면 각 페이지에 독립적인 그리드 레이아웃을
              설정할 수 있습니다.
              <br />
              페이지가 없으면 아이템이 하나씩 슬라이드됩니다.
            </p>
          )}
        </div>
      )}
    </div>
  );
}
