"use client";

/**
 * InvStatsConfigPanel — 통합 "통계 카드" (id: stats) cp 톤 설정 패널
 *
 * Phase G.4.2 — **DB-first KPI editor**.
 *
 * stats 는 KPI / 집계 컴포넌트다. 본 역할:
 *   - 테이블 선택
 *   - 집계 선택
 *   - (필요 시) 집계 컬럼 선택
 *   - 필터 적용
 *   - 결과 숫자 표시
 *
 * 그래서 항목 default 자체가 `{ label, dataSource: { aggregation: "count" } }` 이며,
 * fallback value / 변화량 (delta) 는 모두 "고급" 섹션 안의 보조 옵션이다. 메인 흐름에
 * 직접 노출되지 않는다.
 *
 * 흐름:
 *   ① 기본 — 제목
 *   ② 배치 + 스타일
 *   ③ 색상 프리셋
 *   ④ KPI 항목 list — collapsed row 가 핵심 KPI 메타 (라벨 / 테이블 / 집계 / 필터수)
 *     · 펼치면: 데이터 → 필터 → 외형 → 고급 (flat CP rows, 카드-속-카드 X)
 *
 * Reference: notes/gbpark/2026-04-28-cp-panel-standard.md
 */

import React, { useState } from "react";
import {
  Plus,
  Filter as FilterIcon,
  ChevronRight,
} from "lucide-react";
import {
  CPSection,
  CPRow,
  CPText,
  CPSelect,
  CPSegment,
  CPNumber,
  CPColor,
  CPVisualGrid,
  Hint,
  SectionLabel,
} from "@/components/v2/config-panels/_shared/cp";
import { IconPicker } from "../common/IconPicker";
import { useDbTables } from "../common/useDbTables";
import { RowNumberBadge, RowDeleteBtn } from "../common/row-helpers";
import { OptionFilterRow } from "../_shared/FilterRow";
import { ColumnPicker } from "../_shared/ColumnPicker";
import type { OptionFilter } from "../input/use-option-loader";
import type {
  StatsConfig,
  StatsItem,
  StatItemDataSource,
  StatsAggregation,
} from "./types";

const AGGREGATION_OPTS: Array<{ value: StatsAggregation; label: string }> = [
  { value: "count", label: "건수" },
  { value: "distinctCount", label: "고유" },
  { value: "sum", label: "합계" },
  { value: "avg", label: "평균" },
  { value: "min", label: "최소" },
  { value: "max", label: "최대" },
];

const COLOR_PRESETS = [
  { id: "blue", name: "블루", colors: ["#3b82f6", "#60a5fa", "#2563eb", "#1d4ed8"] },
  { id: "rainbow", name: "다채로운", colors: ["#3b82f6", "#10b981", "#f59e0b", "#ef4444"] },
  { id: "pastel", name: "파스텔", colors: ["#93c5fd", "#86efac", "#fcd34d", "#fca5a5"] },
  { id: "dark", name: "다크", colors: ["#1e3a5f", "#1e3f28", "#5c3c0a", "#5c1a1a"] },
];

const blankItem = (n: number): StatsItem => ({
  label: `항목 ${n}`,
  dataSource: { aggregation: "count" },
});

export interface InvStatsConfigPanelProps {
  config?: StatsConfig;
  onChange?: (config: StatsConfig) => void;
  selectedComponent?: { id: string; config?: StatsConfig; [k: string]: any };
  tables?: any[];
  tableColumns?: any[];
  screenTableName?: string;
  onTableChange?: (tableName: string) => void;
}

// Phase G.4.2 — KPI 패널 keyframes (한 번만 inject). cp 패널 표준 톤 .18s ease-out.
const KEYFRAMES = `
@keyframes stats-kpi-slide-down {
  from { opacity: 0; transform: translateY(-2px); max-height: 0; }
  to   { opacity: 1; transform: translateY(0);    max-height: 1200px; }
}
@keyframes stats-kpi-slide-up {
  from { opacity: 1; transform: translateY(0);    max-height: 1200px; }
  to   { opacity: 0; transform: translateY(-2px); max-height: 0; }
}
@keyframes stats-kpi-fade-in {
  from { opacity: 0; transform: translateY(-1px); }
  to   { opacity: 1; transform: translateY(0); }
}
@keyframes stats-kpi-pop-in {
  from { opacity: 0; transform: scale(0.96); }
  to   { opacity: 1; transform: scale(1); }
}`;

const SLIDE_DURATION_MS = 180;

function useKeyframesOnce() {
  React.useEffect(() => {
    const id = "stats-kpi-keyframes";
    if (typeof document === "undefined" || document.getElementById(id)) return;
    const style = document.createElement("style");
    style.id = id;
    style.textContent = KEYFRAMES;
    document.head.appendChild(style);
  }, []);
}

/**
 * useSlideToggle — open 변화 시 닫힘 애니메이션이 끝날 때까지 dom 유지.
 * open true → render true (즉시)
 * open false → closing true → SLIDE_DURATION 후 render false
 */
function useSlideToggle(open: boolean) {
  const [render, setRender] = React.useState(open);
  const [closing, setClosing] = React.useState(false);
  React.useEffect(() => {
    if (open) {
      setRender(true);
      setClosing(false);
      return;
    }
    if (!render) return;
    setClosing(true);
    const t = setTimeout(() => {
      setRender(false);
      setClosing(false);
    }, SLIDE_DURATION_MS);
    return () => clearTimeout(t);
  }, [open, render]);
  return { render, closing };
}

export const InvStatsConfigPanel: React.FC<InvStatsConfigPanelProps> = ({
  config,
  onChange,
  selectedComponent,
}) => {
  useKeyframesOnce();
  const current: StatsConfig =
    (config as StatsConfig) || (selectedComponent?.config as StatsConfig) || {};

  const patch = (p: Partial<StatsConfig>) => onChange?.({ ...current, ...p });

  const items: StatsItem[] = current.items ?? [];

  const updateItem = (idx: number, item: Partial<StatsItem>) => {
    const next = items.map((it, i) => (i === idx ? { ...it, ...item } : it));
    patch({ items: next });
  };

  const addItem = () => {
    patch({ items: [...items, blankItem(items.length + 1)] });
  };

  const removeItem = (idx: number) => {
    patch({ items: items.filter((_, i) => i !== idx) });
  };

  const { options: tableOptions } = useDbTables();

  const orientation = current.orientation || "horizontal";
  const styleMode = current.style || "card";

  return (
    <div style={{ fontFamily: "var(--v5-font-sans)", color: "var(--cp-text)", padding: "0 12px" }}>
      {/* ── ① 기본 ─────────────────────────── */}
      <CPSection title="① 기본">
        <CPRow label="제목" help="비우면 제목 영역 숨김">
          <CPText
            value={current.title || ""}
            onChange={(v) => patch({ title: v || undefined })}
            placeholder="이번 달 KPI"
          />
        </CPRow>
      </CPSection>

      {/* ── ② 배치 + 스타일 ─────────────────────────── */}
      <CPSection title="② 배치 + 스타일" desc="항목을 어떻게 보여줄지">
        <CPRow label="배치">
          <CPSegment
            value={orientation}
            onChange={(v) => patch({ orientation: v as StatsConfig["orientation"] })}
            options={[
              { value: "horizontal", label: "가로" },
              { value: "vertical", label: "세로" },
              { value: "grid", label: "그리드" },
            ]}
          />
        </CPRow>
        {orientation === "grid" && (
          <CPRow label="그리드 열" help="1 ~ 8">
            <CPNumber
              value={current.columns ?? 4}
              onChange={(v) => patch({ columns: v ?? 4 })}
              min={1}
              max={8}
            />
          </CPRow>
        )}

        <div style={{ marginTop: 8 }}>
          <CPRow label="표시 스타일" />
          <CPVisualGrid
            cols={3}
            cardHeight={52}
            value={styleMode}
            onChange={(v) => patch({ style: v as StatsConfig["style"] })}
            options={[
              {
                value: "card",
                label: "카드",
                preview: <CardPreview />,
                desc: "박스 + 보더 + 라벨/값",
              },
              {
                value: "chip",
                label: "칩",
                preview: <ChipPreview />,
                desc: "둥근 pill 형태",
              },
              {
                value: "bigNumber",
                label: "큰 숫자",
                preview: <BigNumberPreview />,
                desc: "값을 크게 강조",
              },
            ]}
          />
        </div>
      </CPSection>

      {/* ── ③ 색상 프리셋 ─────────────────────────── */}
      <CPSection title="③ 색상 프리셋" desc="모든 항목에 일괄 적용">
        <div style={{ display: "grid", gridTemplateColumns: "repeat(2, 1fr)", gap: 6 }}>
          {COLOR_PRESETS.map((preset) => (
            <button
              key={preset.id}
              type="button"
              onClick={() => {
                if (items.length === 0) {
                  patch({
                    items: preset.colors.map((c, i) => ({
                      ...blankItem(i + 1),
                      color: c,
                    })),
                  });
                } else {
                  const updated = items.map((it, i) => ({
                    ...it,
                    color: preset.colors[i % preset.colors.length],
                  }));
                  patch({ items: updated });
                }
              }}
              style={{
                display: "flex",
                alignItems: "center",
                gap: 7,
                padding: "5px 8px",
                background: "var(--cp-bg-subtle)",
                border: "1px solid var(--cp-border-subtle)",
                borderRadius: 5,
                cursor: "pointer",
                fontFamily: "var(--v5-font-sans)",
                transition: "background .12s ease, border-color .12s ease",
              }}
              onMouseEnter={(e) => {
                (e.currentTarget as HTMLButtonElement).style.borderColor =
                  "hsl(var(--primary) / 0.4)";
                (e.currentTarget as HTMLButtonElement).style.background =
                  "var(--cp-surface-hover, var(--cp-surface))";
              }}
              onMouseLeave={(e) => {
                (e.currentTarget as HTMLButtonElement).style.borderColor =
                  "var(--cp-border-subtle)";
                (e.currentTarget as HTMLButtonElement).style.background =
                  "var(--cp-bg-subtle)";
              }}
            >
              <span style={{ display: "inline-flex", gap: 2 }}>
                {preset.colors.map((c) => (
                  <span
                    key={c}
                    style={{
                      width: 14,
                      height: 14,
                      borderRadius: 3,
                      background: c,
                      boxShadow: "inset 0 0 0 1px rgba(0,0,0,0.06)",
                    }}
                  />
                ))}
              </span>
              <span style={{ fontSize: 10.5, fontWeight: 600, color: "var(--cp-text-sec)", letterSpacing: "-0.005em" }}>
                {preset.name}
              </span>
            </button>
          ))}
        </div>
        <Hint>
          항목이 없으면 KPI 4개 자동 생성 + 색 적용. 있으면 기존 항목 색만 일괄 변경.
        </Hint>
      </CPSection>

      {/* ── ④ KPI 항목 ─────────────────────────── */}
      <CPSection title="④ KPI 항목" desc={`${items.length}개`}>
        <div style={{ display: "flex", justifyContent: "flex-end", marginBottom: 5 }}>
          <button
            type="button"
            onClick={addItem}
            style={{
              padding: "4px 10px",
              fontSize: 10.5,
              background: "var(--cp-bg-subtle)",
              border: "1px solid var(--cp-border)",
              borderRadius: 4,
              cursor: "pointer",
              color: "var(--cp-text)",
              fontFamily: "var(--v5-font-sans)",
              display: "inline-flex",
              alignItems: "center",
              gap: 4,
              transition: "background-color .15s ease, border-color .15s ease, color .15s ease",
            }}
            onMouseEnter={(e) => {
              const b = e.currentTarget as HTMLButtonElement;
              b.style.borderColor = "hsl(var(--primary) / 0.4)";
              b.style.color = "hsl(var(--primary))";
              b.style.background = "hsl(var(--primary) / 0.06)";
            }}
            onMouseLeave={(e) => {
              const b = e.currentTarget as HTMLButtonElement;
              b.style.borderColor = "var(--cp-border)";
              b.style.color = "var(--cp-text)";
              b.style.background = "var(--cp-bg-subtle)";
            }}
          >
            <Plus size={10} /> 추가
          </button>
        </div>

        {items.length === 0 ? (
          <Hint>
            KPI 항목이 없습니다. [+ 추가] 또는 위 [색상 프리셋] 클릭으로 4개 자동 생성.
          </Hint>
        ) : (
          <div
            style={{
              border: "1px solid var(--cp-border-subtle)",
              borderRadius: 5,
              overflow: "hidden",
              background: "var(--cp-bg-subtle)",
            }}
          >
            {items.map((item, idx) => (
              <ItemEditRow
                key={idx}
                index={idx}
                item={item}
                isLast={idx === items.length - 1}
                tableOptions={tableOptions}
                onChange={(p) => updateItem(idx, p)}
                onRemove={() => removeItem(idx)}
              />
            ))}
          </div>
        )}
      </CPSection>
    </div>
  );
};

InvStatsConfigPanel.displayName = "InvStatsConfigPanel";

// ───────────────────────────────────────────────────────
// preview cards (시각 스타일 미리보기)
// ───────────────────────────────────────────────────────
function CardPreview() {
  return (
    <div
      style={{
        display: "flex",
        flexDirection: "column",
        alignItems: "flex-start",
        gap: 1,
        padding: "3px 6px",
        background: "var(--cp-surface)",
        border: "1px solid var(--cp-border)",
        borderRadius: 3,
        minWidth: 36,
      }}
    >
      <span style={{ fontSize: 7, color: "var(--cp-text-muted)" }}>매출</span>
      <span style={{ fontSize: 11, fontWeight: 700, color: "var(--cp-text)" }}>1.2M</span>
    </div>
  );
}
function ChipPreview() {
  return (
    <div
      style={{
        display: "inline-flex",
        alignItems: "center",
        gap: 4,
        padding: "2px 8px",
        background: "hsl(var(--primary) / 0.15)",
        color: "hsl(var(--primary))",
        borderRadius: 999,
        fontSize: 9,
        fontWeight: 700,
      }}
    >
      <span>매출</span>
      <span>1.2M</span>
    </div>
  );
}
function BigNumberPreview() {
  return (
    <div style={{ display: "flex", flexDirection: "column", alignItems: "center", lineHeight: 1 }}>
      <span style={{ fontSize: 17, fontWeight: 800, color: "var(--cp-text)" }}>1.2M</span>
      <span style={{ fontSize: 7, color: "var(--cp-text-muted)", marginTop: 1 }}>매출</span>
    </div>
  );
}

// ───────────────────────────────────────────────────────
// ItemEditRow — Phase G.4.2 DB-first KPI row
// ───────────────────────────────────────────────────────
function ItemEditRow({
  index,
  item,
  isLast,
  tableOptions,
  onChange,
  onRemove,
}: {
  index: number;
  item: StatsItem;
  isLast: boolean;
  tableOptions: { value: string; label: string }[];
  onChange: (p: Partial<StatsItem>) => void;
  onRemove: () => void;
}) {
  const [hover, setHover] = useState(false);
  const [expanded, setExpanded] = useState(false);
  const expandAnim = useSlideToggle(expanded);

  const ds = item.dataSource ?? { aggregation: "count" };
  const aggregation: StatsAggregation = ds.aggregation ?? "count";
  const needsColumn = aggregation !== "count";
  const filters = ds.filters ?? [];
  const filterCount = filters.length;

  // 항상 dataSource 가 있도록 보장 (G.4.2 — DB-first). 새 항목은 default 가 이미 채워져
  // 있지만, 옛 데이터 (value 만 있는 legacy item) 도 inline 편집 가능하게.
  const patchDs = (p: Partial<StatItemDataSource>) => {
    const next = { ...ds, ...p };
    if (!next.tableName && (!next.filters || next.filters.length === 0) && !next.columnName && !next.aggregation) {
      // 모두 비면 dataSource 제거 (사용자가 명시적으로 비웠다는 의도)
      onChange({ dataSource: undefined });
    } else {
      onChange({ dataSource: next });
    }
  };

  const updateFilter = (i: number, f: Partial<OptionFilter>) => {
    const next = filters.map((it, k) => (k === i ? { ...it, ...f } : it));
    patchDs({ filters: next });
  };
  const addFilter = () =>
    patchDs({
      filters: [
        ...filters,
        { column: "", operator: "=", value_type: "static", value: "" } as OptionFilter,
      ],
    });
  const removeFilter = (i: number) => {
    const next = filters.filter((_, k) => k !== i);
    patchDs({ filters: next.length === 0 ? undefined : next });
  };

  return (
    <div
      onMouseEnter={() => setHover(true)}
      onMouseLeave={() => setHover(false)}
      style={{
        borderBottom: isLast ? "none" : "1px solid var(--cp-border-subtle)",
        // expanded 강조는 close animation 동안 유지하기 위해 render 기준 (slide-up
        // 끝나야 비활성). 배경은 옅은 hover tint 만.
        background:
          hover && !expandAnim.render
            ? "hsl(var(--primary) / 0.025)"
            : "transparent",
        borderLeft: `2px solid ${
          expandAnim.render ? "hsl(var(--primary))" : "transparent"
        }`,
        transition:
          "background-color .15s ease, border-left-color .15s ease",
        fontFamily: "var(--v5-font-sans)",
        animation: "stats-kpi-fade-in .18s ease-out",
      }}
    >
      {/* collapsed KPI — 단일 padded 컨테이너 + flex column gap. 일관된 간격. */}
      <div
        style={{
          padding: "5px 8px",
          display: "flex",
          flexDirection: "column",
          gap: 4,
        }}
      >
        {/* 1줄 — 라벨 input + 삭제 (펼침 chevron 제거; 메타 chip 클릭으로 토글) */}
        <div
          style={{
            display: "grid",
            gridTemplateColumns: "16px minmax(0, 1fr) 22px",
            alignItems: "center",
            columnGap: 6,
          }}
        >
          <RowNumberBadge n={index + 1} />
          <input
            type="text"
            value={item.label}
            onChange={(e) => onChange({ label: e.target.value })}
            placeholder="라벨"
            style={inputStyle()}
            title="KPI 라벨"
          />
          <RowDeleteBtn onClick={onRemove} visible={hover} />
        </div>
        {/* 2줄 — 메타 chip 클릭 = expand 토글. 라벨 column 과 동일 grid 로 정렬 */}
        <div
          style={{
            display: "grid",
            gridTemplateColumns: "16px minmax(0, 1fr) 22px",
            alignItems: "center",
            columnGap: 6,
          }}
        >
          <span />
          <KpiMetaSegment
            tableValue={ds.tableName}
            tableLabel={
              ds.tableName
                ? tableOptions.find((t) => t.value === ds.tableName)?.label ?? ds.tableName
                : undefined
            }
            aggregationLabel={
              AGGREGATION_OPTS.find((o) => o.value === aggregation)?.label ?? aggregation
            }
            columnName={ds.columnName}
            filterCount={filterCount}
            expanded={expandAnim.render}
            onClick={() => setExpanded((x) => !x)}
          />
          <span />
        </div>
      </div>

      {expandAnim.render && (
        <div
          className="stats-kpi-expand"
          style={{
            padding: "4px 10px 8px 26px",
            display: "flex",
            flexDirection: "column",
            gap: 4,
            overflow: "hidden",
            animation: expandAnim.closing
              ? "stats-kpi-slide-up .18s ease-out forwards"
              : "stats-kpi-slide-down .18s ease-out",
          }}
        >
          {/* ── 데이터 ───────────────────────────── */}
          <SectionLabel text="데이터" />
          <CPRow label="테이블">
            <CPSelect
              value={ds.tableName || ""}
              onChange={(v) => patchDs({ tableName: v || undefined })}
            >
              <option value="">선택...</option>
              {tableOptions.map((t) => (
                <option key={t.value} value={t.value}>
                  {t.label}
                </option>
              ))}
            </CPSelect>
          </CPRow>
          <CPRow label="집계">
            <CPSelect
              value={aggregation}
              onChange={(v) => patchDs({ aggregation: (v as StatsAggregation) || "count" })}
            >
              {AGGREGATION_OPTS.map((o) => (
                <option key={o.value} value={o.value}>
                  {o.label}
                </option>
              ))}
            </CPSelect>
          </CPRow>
          {(needsColumn || ds.columnName) && (
            <CPRow label="컬럼" help={needsColumn ? "필수" : "(count 는 비워도 됨)"}>
              <ColumnPicker
                tableName={ds.tableName}
                value={ds.columnName}
                onChange={(v) => patchDs({ columnName: v || undefined })}
              />
            </CPRow>
          )}
          {!ds.tableName && (
            <Hint>
              테이블을 선택하면 운영 모드에서 DB 집계 값이 자동 표시됩니다. 디자인 모드는 항상 "—".
            </Hint>
          )}

          {/* ── 필터 ───────────────────────────── */}
          <div
            style={{
              display: "flex",
              alignItems: "center",
              justifyContent: "space-between",
              marginTop: 8,
            }}
          >
            <SectionLabel text={`필터${filterCount > 0 ? ` (${filterCount})` : ""}`} />
            <button
              type="button"
              onClick={addFilter}
              style={{
                padding: "2px 6px",
                fontSize: 10,
                background: "var(--cp-surface)",
                border: "1px solid var(--cp-border)",
                borderRadius: 3,
                cursor: "pointer",
                color: "var(--cp-text)",
                display: "inline-flex",
                alignItems: "center",
                gap: 3,
                transition:
                  "background-color .15s ease, border-color .15s ease, color .15s ease",
              }}
              onMouseEnter={(e) => {
                const b = e.currentTarget as HTMLButtonElement;
                b.style.borderColor = "hsl(var(--primary) / 0.4)";
                b.style.color = "hsl(var(--primary))";
              }}
              onMouseLeave={(e) => {
                const b = e.currentTarget as HTMLButtonElement;
                b.style.borderColor = "var(--cp-border)";
                b.style.color = "var(--cp-text)";
              }}
            >
              <Plus size={9} /> 추가
            </button>
          </div>
          {filterCount === 0 ? (
            <Hint>필터 없음 — 테이블 전체 집계</Hint>
          ) : (
            <div style={{ display: "flex", flexDirection: "column", gap: 4 }}>
              {filters.map((f, i) => (
                <OptionFilterRow
                  key={i}
                  filter={f}
                  onChange={(p) => updateFilter(i, p)}
                  onRemove={() => removeFilter(i)}
                  tableName={ds.tableName}
                />
              ))}
            </div>
          )}

          {/* ── 외형 ───────────────────────────── */}
          <SectionLabel text="외형" />
          <CPRow label="아이콘">
            <IconPicker
              value={item.icon || ""}
              onChange={(v) => onChange({ icon: v || undefined })}
            />
          </CPRow>
          <CPRow label="값 색">
            <CPColor
              value={item.color || ""}
              onChange={(v) => onChange({ color: v || undefined })}
            />
          </CPRow>

          {/* ── 고급 ───────────────────────────── */}
          <AdvancedSection item={item} onChange={onChange} />
        </div>
      )}
    </div>
  );
}

// ───────────────────────────────────────────────────────
// KpiMetaSegment — CPSegment 톤의 메타 요약 (테이블 | 집계 | 컬럼 | 필터)
// 한 컨테이너 + vertical divider. 클릭 시 expand 토글.
// ───────────────────────────────────────────────────────
function KpiMetaSegment({
  tableValue,
  tableLabel: tableLabelProp,
  aggregationLabel,
  columnName,
  filterCount,
  expanded,
  onClick,
}: {
  /** 원본 table 이름 (영문 / 식별자) — tooltip 용 */
  tableValue?: string;
  /** 표시용 라벨 (사용자정보 등). 없으면 value 그대로 또는 "테이블 미설정" */
  tableLabel?: string;
  aggregationLabel: string;
  columnName?: string;
  filterCount: number;
  expanded?: boolean;
  onClick: () => void;
}) {
  const tableMissing = !tableValue;
  const tableLabel = tableLabelProp ?? tableValue ?? "테이블 미설정";

  const cells: Array<{
    key: string;
    content: React.ReactNode;
    color: string;
    weight: number;
    italic?: boolean;
  }> = [
    {
      key: "table",
      content: tableLabel,
      color: tableMissing ? "var(--cp-text-muted)" : "var(--cp-text)",
      weight: tableMissing ? 500 : 600,
      italic: tableMissing,
    },
    {
      key: "agg",
      content: aggregationLabel,
      color: "hsl(var(--primary))",
      weight: 700,
    },
  ];
  if (columnName) {
    cells.push({
      key: "col",
      content: columnName,
      color: "var(--cp-text-sec)",
      weight: 500,
    });
  }
  cells.push({
    key: "filter",
    content: (
      <span style={{ display: "inline-flex", alignItems: "center", gap: 3 }}>
        <FilterIcon size={9} />
        {filterCount}
      </span>
    ),
    color:
      filterCount > 0 ? "hsl(var(--primary))" : "var(--cp-text-muted)",
    weight: filterCount > 0 ? 700 : 500,
  });

  return (
    <button
      type="button"
      onClick={onClick}
      title={`${tableLabel}${
        tableValue && tableValue !== tableLabel ? ` (${tableValue})` : ""
      } · ${aggregationLabel}${columnName ? ` · ${columnName}` : ""} · 필터 ${filterCount}`}
      style={{
        display: "inline-flex",
        alignItems: "stretch",
        minHeight: 22,
        border: "none",
        background: "transparent",
        cursor: "pointer",
        fontFamily: "var(--v5-font-mono)",
        fontSize: 10,
        padding: 0,
        textAlign: "left",
      }}
    >
      {cells.map((cell, i) => (
        <span
          key={cell.key}
          style={{
            display: "inline-flex",
            alignItems: "center",
            padding: "0 10px",
            color: cell.color,
            fontWeight: cell.weight,
            fontStyle: cell.italic ? "italic" : "normal",
            borderLeft:
              i === 0 ? "none" : "1px solid var(--cp-border-subtle)",
            wordBreak: "break-all",
            minWidth: 0,
            transition: "color .15s ease",
            opacity: expanded ? 0.85 : 1,
          }}
        >
          {cell.content}
        </span>
      ))}
    </button>
  );
}

// ───────────────────────────────────────────────────────
// AdvancedSection — fallback value + delta (collapsible, default closed)
// ───────────────────────────────────────────────────────
function AdvancedSection({
  item,
  onChange,
}: {
  item: StatsItem;
  onChange: (p: Partial<StatsItem>) => void;
}) {
  const hasFallback = item.value !== undefined && item.value !== "";
  const [open, setOpen] = useState(hasFallback);
  const advAnim = useSlideToggle(open);

  return (
    <div style={{ marginTop: 4 }}>
      <button
        type="button"
        onClick={() => setOpen((x) => !x)}
        style={{
          width: "100%",
          display: "flex",
          alignItems: "center",
          justifyContent: "space-between",
          padding: "4px 0",
          border: "none",
          background: "transparent",
          cursor: "pointer",
          fontSize: 11,
          fontWeight: 700,
          letterSpacing: "-0.005em",
          color: "var(--cp-text-sec)",
          fontFamily: "var(--v5-font-sans)",
          transition: "color .15s ease",
        }}
        onMouseEnter={(e) => {
          (e.currentTarget as HTMLButtonElement).style.color =
            "var(--cp-text)";
        }}
        onMouseLeave={(e) => {
          (e.currentTarget as HTMLButtonElement).style.color =
            "var(--cp-text-sec)";
        }}
      >
        <span>
          고급
          {hasFallback && (
            <span style={{ marginLeft: 6, fontSize: 9, fontWeight: 600, color: "var(--cp-text-muted)" }}>
              · fallback
            </span>
          )}
        </span>
        <span
          style={{
            display: "inline-flex",
            alignItems: "center",
            justifyContent: "center",
            width: 18,
            height: 18,
            color: "var(--cp-text-sec)",
            transition: "transform .18s ease-out, color .15s ease",
            // close animation 끝날 때까지 회전 유지 (render 기준)
            transform: advAnim.render ? "rotate(90deg)" : "rotate(0deg)",
          }}
        >
          <ChevronRight size={14} strokeWidth={2.2} />
        </span>
      </button>
      {advAnim.render && (
        <div
          style={{
            display: "flex",
            flexDirection: "column",
            gap: 6,
            marginTop: 4,
            overflow: "hidden",
            animation: advAnim.closing
              ? "stats-kpi-slide-up .18s ease-out forwards"
              : "stats-kpi-slide-down .18s ease-out",
          }}
        >
          <CPRow label="Fallback 값" help="디자인 모드 / API 실패 시 보여줄 임시 값. 비우면 —">
            <CPText
              value={item.value?.toString() ?? ""}
              onChange={(v) =>
                onChange({ value: v === "" ? undefined : isNaN(Number(v)) ? v : Number(v) })
              }
              placeholder="비우면 —"
            />
          </CPRow>
        </div>
      )}
    </div>
  );
}

// ───────────────────────────────────────────────────────
// inline input/select styles (compact, collapsed row 용)
// ───────────────────────────────────────────────────────
function inputStyle({ mono = false }: { mono?: boolean } = {}): React.CSSProperties {
  return {
    height: 22,
    padding: "0 6px",
    fontSize: 11,
    fontFamily: mono ? "var(--v5-font-mono)" : "var(--v5-font-sans)",
    background: "var(--cp-surface)",
    border: "1px solid var(--cp-border)",
    borderRadius: 3,
    color: "var(--cp-text)",
    outline: "none",
    minWidth: 0,
  };
}

export default InvStatsConfigPanel;
