"use client";

/**
 * pop-card-list 설정 패널
 *
 * 2개 탭:
 * [기본 설정] - 테이블 선택 + 조인/정렬 + 레이아웃 설정
 * [카드 템플릿] - 헤더/이미지/본문/입력/계산/담기 설정
 */

import React, { useState, useEffect, useMemo } from "react";
import { ChevronDown, ChevronRight, Plus, Trash2, Database, Check, ChevronsUpDown } from "lucide-react";
import { useCollapsibleSections } from "@/hooks/pop/useCollapsibleSections";
import { useAuth } from "@/hooks/useAuth";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "@/components/ui/command";
import type { GridMode } from "@/components/pop/designer/types/pop-layout";
import { GRID_BREAKPOINTS } from "@/components/pop/designer/types/pop-layout";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Switch } from "@/components/ui/switch";
import {
  Select,
  SelectContent,
  SelectGroup,
  SelectItem,
  SelectLabel,
  SelectTrigger,
  SelectValue,
} from "@/components/ui/select";
import { cn } from "@/lib/utils";
import type {
  PopCardListConfig,
  CardListDataSource,
  CardSortConfig,
  CardTemplateConfig,
  CardHeaderConfig,
  CardImageConfig,
  CardBodyConfig,
  CardFieldBinding,
  FieldValueType,
  FormulaOperator,
  FormulaRightType,
  CardColumnJoin,
  CardColumnFilter,
  CardScrollDirection,
  FilterOperator,
  CardInputFieldConfig,
  CardPackageConfig,
  CardCartActionConfig,
  CardResponsiveConfig,
  ResponsiveDisplayMode,
  CartListModeConfig,
  CardListSaveMapping,
  CardListSaveMappingEntry,
} from "../types";
import { screenApi } from "@/lib/api/screen";
import {
  CARD_SCROLL_DIRECTION_LABELS,
  RESPONSIVE_DISPLAY_LABELS,
  DEFAULT_CARD_IMAGE,
} from "../types";
import {
  fetchTableList,
  fetchTableColumns,
  type TableInfo,
  type ColumnInfo,
} from "../pop-dashboard/utils/dataFetcher";
import { TableCombobox } from "../pop-shared/TableCombobox";

// ===== 테이블별 그룹화된 컬럼 =====

interface ColumnGroup {
  tableName: string;
  displayName: string;
  columns: ColumnInfo[];
}

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

interface ConfigPanelProps {
  config: PopCardListConfig | undefined;
  onUpdate: (config: PopCardListConfig) => void;
  currentMode?: GridMode;
  currentColSpan?: number;
}

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

const DEFAULT_DATA_SOURCE: CardListDataSource = {
  tableName: "",
};

const DEFAULT_HEADER: CardHeaderConfig = {
  codeField: undefined,
  titleField: undefined,
};

const DEFAULT_IMAGE: CardImageConfig = {
  enabled: true,
  imageColumn: undefined,
  defaultImage: DEFAULT_CARD_IMAGE,
};

const DEFAULT_BODY: CardBodyConfig = {
  fields: [],
};

const DEFAULT_TEMPLATE: CardTemplateConfig = {
  header: DEFAULT_HEADER,
  image: DEFAULT_IMAGE,
  body: DEFAULT_BODY,
};

const DEFAULT_CONFIG: PopCardListConfig = {
  dataSource: DEFAULT_DATA_SOURCE,
  cardTemplate: DEFAULT_TEMPLATE,
  scrollDirection: "vertical",
  gridColumns: 2,
  gridRows: 3,
  cardSize: "large",
};

// ===== 색상 옵션 (본문 필드 텍스트 색상) =====

const COLOR_OPTIONS = [
  { value: "__default__", label: "기본" },
  { value: "#ef4444", label: "빨간색" },
  { value: "#f97316", label: "주황색" },
  { value: "#eab308", label: "노란색" },
  { value: "#22c55e", label: "초록색" },
  { value: "#3b82f6", label: "파란색" },
  { value: "#8b5cf6", label: "보라색" },
  { value: "#6b7280", label: "회색" },
];

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

export function PopCardListConfigPanel({ config, onUpdate, currentMode, currentColSpan }: ConfigPanelProps) {
  const [activeTab, setActiveTab] = useState<"basic" | "template">("basic");
  const sections = useCollapsibleSections("pop-card-list");

  const cfg: PopCardListConfig = config || DEFAULT_CONFIG;

  const updateConfig = (partial: Partial<PopCardListConfig>) => {
    onUpdate({ ...cfg, ...partial });
  };

  const isCartListMode = !!cfg.cartListMode?.enabled;
  const hasTable = !!cfg.dataSource?.tableName;

  return (
    <div className="flex h-full flex-col">
      {/* 탭 헤더 - 2탭 */}
      <div className="flex border-b">
        <button
          type="button"
          className={`flex-1 px-2 py-2 text-xs font-medium transition-colors ${
            activeTab === "basic"
              ? "border-b-2 border-primary text-primary"
              : "text-muted-foreground hover:text-foreground"
          }`}
          onClick={() => setActiveTab("basic")}
        >
          기본 설정
        </button>
        <button
          type="button"
          className={`flex-1 px-2 py-2 text-xs font-medium transition-colors ${
            activeTab === "template"
              ? "border-b-2 border-primary text-primary"
              : hasTable && !isCartListMode
                ? "text-muted-foreground hover:text-foreground"
                : "text-muted-foreground/50 cursor-not-allowed"
          }`}
          onClick={() => hasTable && !isCartListMode && setActiveTab("template")}
          disabled={!hasTable || isCartListMode}
        >
          카드 템플릿
        </button>
      </div>

      {/* 탭 내용 */}
      <div className="flex-1 overflow-y-auto p-3">
        {activeTab === "basic" && (
          <BasicSettingsTab
            config={cfg}
            onUpdate={updateConfig}
            currentMode={currentMode}
            currentColSpan={currentColSpan}
            sections={sections}
          />
        )}
        {activeTab === "template" && (
          isCartListMode ? (
            <div className="flex h-40 items-center justify-center text-center">
              <div className="text-muted-foreground">
                <p className="text-sm">원본 화면의 카드 설정을 자동으로 사용합니다</p>
                <p className="mt-1 text-xs">카드 디자인을 변경하려면 원본 화면에서 수정하세요</p>
              </div>
            </div>
          ) : (
            <CardTemplateTab config={cfg} onUpdate={updateConfig} sections={sections} />
          )
        )}
      </div>
    </div>
  );
}

// ===== 기본 설정 탭 (테이블 + 레이아웃 통합) =====

type SectionsApi = { isOpen: (key: string) => boolean; toggle: (key: string) => void };

function BasicSettingsTab({
  config,
  onUpdate,
  currentMode,
  currentColSpan,
  sections,
}: {
  config: PopCardListConfig;
  onUpdate: (partial: Partial<PopCardListConfig>) => void;
  currentMode?: GridMode;
  currentColSpan?: number;
  sections: SectionsApi;
}) {
  const dataSource = config.dataSource || DEFAULT_DATA_SOURCE;
  const [tables, setTables] = useState<TableInfo[]>([]);
  const [columns, setColumns] = useState<ColumnInfo[]>([]);
  const [joinColumnsMap, setJoinColumnsMap] = useState<Record<string, ColumnInfo[]>>({});

  useEffect(() => {
    fetchTableList().then(setTables);
  }, []);

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

  // 조인 테이블 컬럼 로드
  const joinsKey = useMemo(
    () => JSON.stringify((dataSource.joins || []).map((j) => j.targetTable)),
    [dataSource.joins]
  );
  useEffect(() => {
    const joins = dataSource.joins || [];
    const targetTables = joins
      .map((j) => j.targetTable)
      .filter((t): t is string => !!t);
    if (targetTables.length === 0) {
      setJoinColumnsMap({});
      return;
    }
    Promise.all(
      targetTables.map(async (table) => {
        const cols = await fetchTableColumns(table);
        return { table, cols };
      })
    ).then((results) => {
      const map: Record<string, ColumnInfo[]> = {};
      results.forEach(({ table, cols }) => { map[table] = cols; });
      setJoinColumnsMap(map);
    });
  }, [joinsKey]); // eslint-disable-line react-hooks/exhaustive-deps

  const getTableDisplayName = (tableName: string) => {
    const found = tables.find((t) => t.tableName === tableName);
    return found?.displayName || tableName;
  };

  const columnGroups: ColumnGroup[] = useMemo(() => {
    const groups: ColumnGroup[] = [];
    if (dataSource.tableName && columns.length > 0) {
      groups.push({
        tableName: dataSource.tableName,
        displayName: getTableDisplayName(dataSource.tableName),
        columns,
      });
    }
    (dataSource.joins || []).forEach((join) => {
      if (join.targetTable && joinColumnsMap[join.targetTable]) {
        groups.push({
          tableName: join.targetTable,
          displayName: getTableDisplayName(join.targetTable),
          columns: joinColumnsMap[join.targetTable],
        });
      }
    });
    return groups;
  }, [dataSource.tableName, columns, dataSource.joins, joinColumnsMap, tables]); // eslint-disable-line react-hooks/exhaustive-deps

  const recommendation = useMemo(() => {
    if (!currentMode) return null;
    const cols = GRID_BREAKPOINTS[currentMode].columns;
    if (cols >= 25) return { rows: 3, cols: 2 };
    if (cols >= 18) return { rows: 3, cols: 1 };
    return { rows: 2, cols: 1 };
  }, [currentMode]);

  const maxColumns = useMemo(() => {
    if (!currentColSpan) return 2;
    return currentColSpan >= 8 ? 2 : 1;
  }, [currentColSpan]);

  const modeLabel = currentMode ? GRID_BREAKPOINTS[currentMode].label : null;

  useEffect(() => {
    if (!recommendation) return;
    const currentRows = config.gridRows || 3;
    const currentCols = config.gridColumns || 2;
    if (currentRows !== recommendation.rows || currentCols !== recommendation.cols) {
      onUpdate({
        gridRows: recommendation.rows,
        gridColumns: recommendation.cols,
      });
    }
  }, [currentMode]); // eslint-disable-line react-hooks/exhaustive-deps

  const isCartListMode = !!config.cartListMode?.enabled;

  const updateDataSource = (partial: Partial<CardListDataSource>) => {
    onUpdate({ dataSource: { ...dataSource, ...partial } });
  };

  return (
    <div className="space-y-4">
      {/* 장바구니 목록 모드 */}
      <CollapsibleSection sectionKey="basic-cart-mode" title="장바구니 목록 모드" sections={sections}>
        <CartListModeSection
          cartListMode={config.cartListMode}
          onUpdate={(cartListMode) => onUpdate({ cartListMode })}
        />
      </CollapsibleSection>

      {/* 테이블 선택 (장바구니 모드 시 숨김) */}
      {!isCartListMode && (
        <CollapsibleSection sectionKey="basic-table" title="테이블 선택" sections={sections}>
          <div className="space-y-3">
            <div>
              <Label className="text-[10px] text-muted-foreground">데이터 테이블</Label>
              <TableCombobox
                tables={tables}
                value={dataSource.tableName || ""}
                onSelect={(val) => {
                  onUpdate({
                    dataSource: {
                      tableName: val,
                      joins: undefined,
                      filters: undefined,
                      sort: undefined,
                      limit: undefined,
                    },
                    cardTemplate: DEFAULT_TEMPLATE,
                  });
                }}
              />
            </div>

            {dataSource.tableName && (
              <div className="flex items-center gap-2 rounded-md border bg-muted/30 p-2">
                <Database className="h-4 w-4 text-primary" />
                <span className="text-xs font-medium">{dataSource.tableName}</span>
              </div>
            )}
          </div>
        </CollapsibleSection>
      )}

      {/* 조인 설정 (장바구니 모드 시 숨김) */}
      {!isCartListMode && dataSource.tableName && (
        <CollapsibleSection
          sectionKey="basic-join"
          title="조인 설정"
          sections={sections}
          badge={
            dataSource.joins && dataSource.joins.length > 0
              ? `${dataSource.joins.length}개`
              : undefined
          }
        >
          <JoinSettingsSection
            dataSource={dataSource}
            tables={tables}
            onUpdate={updateDataSource}
          />
        </CollapsibleSection>
      )}

      {/* 정렬 기준 (장바구니 모드 시 숨김) */}
      {!isCartListMode && dataSource.tableName && (
        <CollapsibleSection
          sectionKey="basic-sort"
          title="정렬 기준"
          sections={sections}
          badge={
            dataSource.sort
              ? Array.isArray(dataSource.sort)
                ? dataSource.sort.length > 0 ? `${dataSource.sort.length}개` : undefined
                : "1개"
              : undefined
          }
        >
          <SortSettingsSection
            dataSource={dataSource}
            columnGroups={columnGroups}
            onUpdate={updateDataSource}
          />
        </CollapsibleSection>
      )}

      {/* 필터 기준 (장바구니 모드 시 숨김) */}
      {!isCartListMode && dataSource.tableName && (
        <CollapsibleSection
          sectionKey="basic-filter"
          title="필터 기준"
          sections={sections}
          badge={
            dataSource.filters && dataSource.filters.length > 0
              ? `${dataSource.filters.length}개`
              : undefined
          }
        >
          <FilterCriteriaSection
            dataSource={dataSource}
            columnGroups={columnGroups}
            onUpdate={updateDataSource}
          />
        </CollapsibleSection>
      )}

      {/* 필터 필수 설정 (장바구니 모드 아닐 때만) */}
      {!isCartListMode && dataSource.tableName && (
        <CollapsibleSection sectionKey="basic-require-filter" title="필터 필수" sections={sections}>
          <div className="space-y-3">
            <div className="flex items-center justify-between">
              <Label className="text-[10px]">필터 선택 전 데이터 숨김</Label>
              <Switch
                checked={!!config.requireFilter}
                onCheckedChange={(checked) => onUpdate({ requireFilter: checked })}
              />
            </div>
            {config.requireFilter && (
              <div>
                <Label className="text-[10px] text-muted-foreground">안내 문구</Label>
                <Input
                  value={config.requireFilterMessage || ""}
                  onChange={(e) => onUpdate({ requireFilterMessage: e.target.value })}
                  placeholder="필터를 먼저 선택해주세요."
                  className="h-7 text-[10px]"
                />
              </div>
            )}
          </div>
        </CollapsibleSection>
      )}

      {/* 저장 매핑 (장바구니 모드일 때만) */}
      {isCartListMode && (
        <CollapsibleSection
          sectionKey="basic-save-mapping"
          title="저장 매핑"
          sections={sections}
          badge={
            config.saveMapping?.mappings && config.saveMapping.mappings.length > 0
              ? `${config.saveMapping.mappings.length}개`
              : undefined
          }
        >
          <SaveMappingSection
            saveMapping={config.saveMapping}
            onUpdate={(saveMapping) => onUpdate({ saveMapping })}
            cartListMode={config.cartListMode}
          />
        </CollapsibleSection>
      )}

      {/* 레이아웃 설정 */}
      <CollapsibleSection sectionKey="basic-layout" title="레이아웃 설정" sections={sections}>
        <div className="space-y-3">
          {modeLabel && (
            <div className="flex items-center gap-1.5 rounded-md bg-muted/50 px-2.5 py-1.5">
              <span className="text-[10px] text-muted-foreground">현재:</span>
              <span className="rounded bg-primary/10 px-1.5 py-0.5 text-[10px] font-medium text-primary">
                {modeLabel}
              </span>
            </div>
          )}

          <div>
            <Label className="text-[10px]">스크롤 방향</Label>
            <div className="mt-1.5 space-y-1.5">
              {(["horizontal", "vertical"] as CardScrollDirection[]).map((dir) => (
                <button
                  key={dir}
                  type="button"
                  className={`flex w-full items-center gap-2 rounded-md border px-3 py-2 text-xs transition-colors ${
                    config.scrollDirection === dir
                      ? "border-primary bg-primary/10 text-primary"
                      : "border-input bg-background hover:bg-accent"
                  }`}
                  onClick={() => onUpdate({ scrollDirection: dir })}
                >
                  <div
                    className={`h-3 w-3 rounded-full border-2 ${
                      config.scrollDirection === dir
                        ? "border-primary bg-primary"
                        : "border-muted-foreground"
                    }`}
                  />
                  {CARD_SCROLL_DIRECTION_LABELS[dir]}
                </button>
              ))}
            </div>
          </div>

          <div>
            <Label className="text-[10px]">카드 배치 (행 x 열)</Label>
            <div className="mt-1.5 flex items-center gap-2">
              <Input
                type="number"
                min={1}
                max={10}
                value={config.gridRows || 3}
                onChange={(e) =>
                  onUpdate({ gridRows: parseInt(e.target.value, 10) || 3 })
                }
                className="h-7 w-16 text-center text-xs"
              />
              <span className="text-xs text-muted-foreground">x</span>
              <Input
                type="number"
                min={1}
                max={maxColumns}
                value={Math.min(config.gridColumns || 2, maxColumns)}
                onChange={(e) =>
                  onUpdate({ gridColumns: Math.min(parseInt(e.target.value, 10) || 1, maxColumns) })
                }
                className="h-7 w-16 text-center text-xs"
                disabled={maxColumns === 1}
              />
            </div>

            <p className="mt-1 text-[9px] text-muted-foreground">
              {config.scrollDirection === "horizontal"
                ? "격자로 배치, 가로 스크롤"
                : "격자로 배치, 세로 스크롤"}
            </p>
            <p className="mt-0.5 text-[9px] text-muted-foreground">
              {maxColumns === 1
                ? "현재 모드에서 열 최대 1 (모드 변경 시 자동 적용)"
                : "모드 변경 시 열/행 자동 적용 / 열 최대 2"}
            </p>
          </div>
        </div>
      </CollapsibleSection>
    </div>
  );
}

// (DataSourceTab 제거됨 - 조인/정렬 설정이 BasicSettingsTab으로 통합)

// ===== 카드 템플릿 탭 =====

function CardTemplateTab({
  config,
  onUpdate,
  sections,
}: {
  config: PopCardListConfig;
  onUpdate: (partial: Partial<PopCardListConfig>) => void;
  sections: SectionsApi;
}) {
  const dataSource = config.dataSource || DEFAULT_DATA_SOURCE;
  const template = config.cardTemplate || DEFAULT_TEMPLATE;
  const [tables, setTables] = useState<TableInfo[]>([]);
  const [mainColumns, setMainColumns] = useState<ColumnInfo[]>([]);
  const [joinColumnsMap, setJoinColumnsMap] = useState<Record<string, ColumnInfo[]>>({});

  // 테이블 목록 로드 (한글명 표시용)
  useEffect(() => {
    fetchTableList().then(setTables);
  }, []);

  // 메인 테이블 컬럼 로드
  useEffect(() => {
    if (dataSource.tableName) {
      fetchTableColumns(dataSource.tableName).then(setMainColumns);
    } else {
      setMainColumns([]);
    }
  }, [dataSource.tableName]);

  // 조인 테이블 컬럼 로드
  const joinsKey = useMemo(
    () => JSON.stringify((dataSource.joins || []).map((j) => j.targetTable)),
    [dataSource.joins]
  );
  useEffect(() => {
    const joins = dataSource.joins || [];
    const targetTables = joins
      .map((j) => j.targetTable)
      .filter((t): t is string => !!t);

    if (targetTables.length === 0) {
      setJoinColumnsMap({});
      return;
    }

    Promise.all(
      targetTables.map(async (table) => {
        const cols = await fetchTableColumns(table);
        return { table, cols };
      })
    ).then((results) => {
      const map: Record<string, ColumnInfo[]> = {};
      results.forEach(({ table, cols }) => {
        map[table] = cols;
      });
      setJoinColumnsMap(map);
    });
  }, [joinsKey]); // eslint-disable-line react-hooks/exhaustive-deps

  const getTableDisplayName = (tableName: string) => {
    const found = tables.find((t) => t.tableName === tableName);
    return found?.displayName || tableName;
  };

  // 테이블별 그룹화된 컬럼 목록
  const columnGroups: ColumnGroup[] = useMemo(() => {
    const groups: ColumnGroup[] = [];

    if (dataSource.tableName && mainColumns.length > 0) {
      groups.push({
        tableName: dataSource.tableName,
        displayName: getTableDisplayName(dataSource.tableName),
        columns: mainColumns,
      });
    }

    const joins = dataSource.joins || [];
    joins.forEach((join) => {
      if (join.targetTable && joinColumnsMap[join.targetTable]) {
        groups.push({
          tableName: join.targetTable,
          displayName: getTableDisplayName(join.targetTable),
          columns: joinColumnsMap[join.targetTable],
        });
      }
    });

    return groups;
  }, [dataSource.tableName, mainColumns, dataSource.joins, joinColumnsMap, tables]); // eslint-disable-line react-hooks/exhaustive-deps

  // 하위 호환: 단일 배열 (조인 없는 섹션용)
  const columns = mainColumns;

  const updateTemplate = (partial: Partial<CardTemplateConfig>) => {
    onUpdate({
      cardTemplate: { ...template, ...partial },
    });
  };

  if (!dataSource.tableName) {
    return (
      <div className="flex h-40 items-center justify-center text-center">
        <div className="text-muted-foreground">
          <p className="text-sm">테이블을 먼저 선택해주세요</p>
          <p className="mt-1 text-xs">기본 설정 탭에서 테이블을 선택하세요</p>
        </div>
      </div>
    );
  }

  return (
    <div className="space-y-4">
      {/* 헤더 설정 */}
      <CollapsibleSection sectionKey="tpl-header" title="헤더 설정" sections={sections}>
        <HeaderSettingsSection
          header={template.header || DEFAULT_HEADER}
          columnGroups={columnGroups}
          onUpdate={(header) => updateTemplate({ header })}
        />
      </CollapsibleSection>

      {/* 이미지 설정 */}
      <CollapsibleSection sectionKey="tpl-image" title="이미지 설정" sections={sections}>
        <ImageSettingsSection
          image={template.image || DEFAULT_IMAGE}
          columnGroups={columnGroups}
          onUpdate={(image) => updateTemplate({ image })}
        />
      </CollapsibleSection>

      {/* 본문 필드 */}
      <CollapsibleSection
        sectionKey="tpl-body"
        title="본문 필드"
        sections={sections}
        badge={`${template.body?.fields?.length || 0}개`}
      >
        <BodyFieldsSection
          body={template.body || DEFAULT_BODY}
          columnGroups={columnGroups}
          onUpdate={(body) => updateTemplate({ body })}
        />
      </CollapsibleSection>

      {/* 입력 필드 설정 */}
      <CollapsibleSection sectionKey="tpl-input" title="입력 필드" sections={sections}>
        <InputFieldSettingsSection
          inputField={config.inputField}
          columns={columns}
          tables={tables}
          onUpdate={(inputField) => onUpdate({ inputField })}
        />
      </CollapsibleSection>

      {/* 포장등록 설정 */}
      <CollapsibleSection sectionKey="tpl-package" title="포장등록 (계산기)" sections={sections}>
        <PackageSettingsSection
          packageConfig={config.packageConfig}
          onUpdate={(packageConfig) => onUpdate({ packageConfig })}
        />
      </CollapsibleSection>

      {/* 담기 버튼 설정 */}
      <CollapsibleSection sectionKey="tpl-cart" title="담기 버튼" sections={sections}>
        <CartActionSettingsSection
          cartAction={config.cartAction}
          onUpdate={(cartAction) => onUpdate({ cartAction })}
          cardTemplate={template}
          tableName={dataSource.tableName}
        />
      </CollapsibleSection>

      {/* 반응형 표시 설정 */}
      <CollapsibleSection sectionKey="tpl-responsive" title="반응형 표시" sections={sections}>
        <ResponsiveDisplaySection
          config={config}
          onUpdate={onUpdate}
        />
      </CollapsibleSection>

    </div>
  );
}

// TableCombobox: pop-shared/TableCombobox.tsx에서 import

// ===== 테이블별 그룹화된 컬럼 셀렉트 =====

function GroupedColumnSelect({
  columnGroups,
  value,
  onValueChange,
  placeholder = "컬럼 선택",
  allowNone = false,
  noneLabel = "선택 안함",
  className,
}: {
  columnGroups: ColumnGroup[];
  value: string | undefined;
  onValueChange: (value: string | undefined) => void;
  placeholder?: string;
  allowNone?: boolean;
  noneLabel?: string;
  className?: string;
}) {
  return (
    <Select
      value={value || (allowNone ? "__none__" : "__placeholder__")}
      onValueChange={(val) => {
        if (val === "__none__") onValueChange(undefined);
        else if (val === "__placeholder__") return;
        else onValueChange(val);
      }}
    >
      <SelectTrigger className={cn("h-7 text-xs", className)}>
        <SelectValue placeholder={placeholder} />
      </SelectTrigger>
      <SelectContent>
        {allowNone && (
          <SelectItem value="__none__">{noneLabel}</SelectItem>
        )}
        {!allowNone && (
          <SelectItem value="__placeholder__" disabled>
            {placeholder}
          </SelectItem>
        )}
        {columnGroups.map((group) => (
          <SelectGroup key={group.tableName}>
            <SelectLabel className="text-[10px] font-semibold text-muted-foreground px-2 py-1">
              {group.displayName}
            </SelectLabel>
            {group.columns.map((col) => (
              <SelectItem
                key={`${group.tableName}.${col.name}`}
                value={col.name}
              >
                {col.name}
              </SelectItem>
            ))}
          </SelectGroup>
        ))}
      </SelectContent>
    </Select>
  );
}

// ===== 접기/펴기 섹션 컴포넌트 =====

function CollapsibleSection({
  sectionKey,
  title,
  badge,
  sections,
  children,
}: {
  sectionKey: string;
  title: string;
  badge?: string;
  sections: SectionsApi;
  children: React.ReactNode;
}) {
  const open = sections.isOpen(sectionKey);

  return (
    <div className="rounded-md border">
      <button
        type="button"
        className="flex w-full items-center justify-between px-3 py-2 text-left transition-colors hover:bg-muted/50"
        onClick={() => sections.toggle(sectionKey)}
      >
        <div className="flex items-center gap-2">
          {open ? (
            <ChevronDown className="h-3.5 w-3.5 text-muted-foreground" />
          ) : (
            <ChevronRight className="h-3.5 w-3.5 text-muted-foreground" />
          )}
          <span className="text-xs font-medium">{title}</span>
        </div>
        {badge && (
          <span className="rounded bg-muted px-1.5 py-0.5 text-[10px] text-muted-foreground">
            {badge}
          </span>
        )}
      </button>
      {open && <div className="border-t px-3 py-3">{children}</div>}
    </div>
  );
}

// ===== 장바구니 목록 모드 설정 =====

interface SourceCardListInfo {
  componentId: string;
  label: string;
}

function CartListModeSection({
  cartListMode,
  onUpdate,
}: {
  cartListMode?: CartListModeConfig;
  onUpdate: (config: CartListModeConfig) => void;
}) {
  const mode: CartListModeConfig = cartListMode || { enabled: false };
  const [screens, setScreens] = useState<{ id: number; name: string; code: string }[]>([]);
  const [sourceCardLists, setSourceCardLists] = useState<SourceCardListInfo[]>([]);
  const [loadingComponents, setLoadingComponents] = useState(false);
  const [screenOpen, setScreenOpen] = useState(false);
  const { companyCode } = useAuth();

  useEffect(() => {
    screenApi
      .getScreens({ size: 500, companyCode: companyCode || undefined })
      .then((res) => {
        if (res?.data) {
          setScreens(
            res.data.map((s) => ({
              id: s.screenId,
              name: s.screenName || `화면 ${s.screenId}`,
              code: s.screenCode || "",
            }))
          );
        }
      })
      .catch(() => {});
  }, [companyCode]);

  useEffect(() => {
    if (!mode.sourceScreenId) {
      setSourceCardLists([]);
      return;
    }
    setLoadingComponents(true);
    screenApi
      .getLayoutPop(mode.sourceScreenId)
      .then((layoutJson: any) => {
        const componentsMap = layoutJson?.components || {};
        const componentList = Object.values(componentsMap) as any[];
        const cardLists: SourceCardListInfo[] = componentList
          .filter((c: any) => c.type === "pop-card-list")
          .map((c: any) => ({
            componentId: c.id || "",
            label: c.label || "카드 목록",
          }));
        setSourceCardLists(cardLists);
      })
      .catch(() => {
        setSourceCardLists([]);
      })
      .finally(() => setLoadingComponents(false));
  }, [mode.sourceScreenId]);

  const selectedScreen = screens.find((s) => s.id === mode.sourceScreenId);

  return (
    <div className="space-y-3">
      <div className="flex items-center justify-between">
        <Label className="text-[10px]">장바구니 목록 모드</Label>
        <Switch
          checked={mode.enabled}
          onCheckedChange={(enabled) => onUpdate({ ...mode, enabled })}
        />
      </div>

      <p className="text-[9px] text-muted-foreground">
        활성화하면 cart_items 테이블에서 데이터를 조회하고,
        원본 화면의 카드 설정을 자동으로 상속합니다.
      </p>

      {mode.enabled && (
        <>
          {/* 원본 화면 선택 (검색 가능 Combobox) */}
          <div>
            <Label className="text-[10px] text-muted-foreground">원본 화면</Label>
            <Popover open={screenOpen} onOpenChange={setScreenOpen}>
              <PopoverTrigger asChild>
                <Button
                  variant="outline"
                  role="combobox"
                  aria-expanded={screenOpen}
                  className="mt-1 h-7 w-full justify-between text-xs"
                >
                  {selectedScreen
                    ? `${selectedScreen.name} (${selectedScreen.id})`
                    : "화면 검색..."}
                  <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="화면 이름 또는 ID 검색..." className="text-xs" />
                  <CommandList>
                    <CommandEmpty className="py-2 text-center text-xs">
                      검색 결과가 없습니다.
                    </CommandEmpty>
                    <CommandGroup>
                      {screens.map((s) => (
                        <CommandItem
                          key={s.id}
                          value={`${s.name} ${s.id} ${s.code}`}
                          onSelect={() => {
                            onUpdate({
                              ...mode,
                              sourceScreenId: mode.sourceScreenId === s.id ? undefined : s.id,
                              sourceComponentId: mode.sourceScreenId === s.id ? undefined : mode.sourceComponentId,
                            });
                            setScreenOpen(false);
                          }}
                          className="text-xs"
                        >
                          <Check
                            className={cn(
                              "mr-2 h-3 w-3",
                              mode.sourceScreenId === s.id ? "opacity-100" : "opacity-0",
                            )}
                          />
                          <div className="flex flex-col">
                            <span>{s.name}</span>
                            <span className="text-[9px] text-muted-foreground">ID: {s.id}</span>
                          </div>
                        </CommandItem>
                      ))}
                    </CommandGroup>
                  </CommandList>
                </Command>
              </PopoverContent>
            </Popover>
          </div>

          {/* 원본 컴포넌트 선택 */}
          {mode.sourceScreenId && (
            <div>
              <Label className="text-[10px] text-muted-foreground">원본 카드 목록</Label>
              {loadingComponents ? (
                <div className="mt-1 flex h-7 items-center px-2 text-xs text-muted-foreground">
                  로딩 중...
                </div>
              ) : sourceCardLists.length === 0 ? (
                <div className="mt-1 rounded-md border border-dashed bg-muted/30 px-2 py-1.5 text-[10px] text-muted-foreground">
                  원본 화면에 담기 설정이 있는 카드 목록이 없습니다.
                </div>
              ) : (
                <Select
                  value={mode.sourceComponentId ? `__comp_${mode.sourceComponentId}` : "__none__"}
                  onValueChange={(val) => {
                    if (val === "__none__") {
                      onUpdate({ ...mode, sourceComponentId: undefined });
                    } else {
                      const compId = val.replace("__comp_", "");
                      onUpdate({ ...mode, sourceComponentId: compId });
                    }
                  }}
                >
                  <SelectTrigger className="mt-1 h-7 text-xs">
                    <SelectValue placeholder="카드 목록 선택" />
                  </SelectTrigger>
                  <SelectContent>
                    <SelectItem value="__none__">선택 안함</SelectItem>
                    {sourceCardLists.map((c) => (
                      <SelectItem key={c.componentId} value={`__comp_${c.componentId}`}>
                        {c.label}
                      </SelectItem>
                    ))}
                  </SelectContent>
                </Select>
              )}
              <p className="mt-1 text-[9px] text-muted-foreground">
                원본 화면의 카드 디자인이 자동으로 적용됩니다.
              </p>
            </div>
          )}
        </>
      )}
    </div>
  );
}

// ===== 헤더 설정 섹션 =====

function HeaderSettingsSection({
  header,
  columnGroups,
  onUpdate,
}: {
  header: CardHeaderConfig;
  columnGroups: ColumnGroup[];
  onUpdate: (header: CardHeaderConfig) => void;
}) {
  return (
    <div className="space-y-3">
      {/* 코드 필드 */}
      <div>
        <Label className="text-[10px]">코드 필드</Label>
        <GroupedColumnSelect
          columnGroups={columnGroups}
          value={header.codeField}
          onValueChange={(val) => onUpdate({ ...header, codeField: val })}
          placeholder="컬럼 선택 (선택사항)"
          allowNone
          noneLabel="선택 안함"
          className="mt-1"
        />
        <p className="mt-1 text-[10px] text-muted-foreground">
          카드 헤더 왼쪽에 표시될 코드 (예: ITEM032)
        </p>
      </div>

      {/* 제목 필드 */}
      <div>
        <Label className="text-[10px]">제목 필드</Label>
        <GroupedColumnSelect
          columnGroups={columnGroups}
          value={header.titleField}
          onValueChange={(val) => onUpdate({ ...header, titleField: val })}
          placeholder="컬럼 선택 (선택사항)"
          allowNone
          noneLabel="선택 안함"
          className="mt-1"
        />
        <p className="mt-1 text-[10px] text-muted-foreground">
          카드 헤더 오른쪽에 표시될 제목 (예: 너트 M10)
        </p>
      </div>
    </div>
  );
}

// ===== 이미지 설정 섹션 =====

function ImageSettingsSection({
  image,
  columnGroups,
  onUpdate,
}: {
  image: CardImageConfig;
  columnGroups: ColumnGroup[];
  onUpdate: (image: CardImageConfig) => void;
}) {
  return (
    <div className="space-y-3">
      {/* 이미지 사용 여부 */}
      <div className="flex items-center justify-between">
        <Label className="text-[10px]">이미지 사용</Label>
        <Switch
          checked={image.enabled}
          onCheckedChange={(checked) => onUpdate({ ...image, enabled: checked })}
        />
      </div>

      {image.enabled && (
        <>
          {/* 기본 이미지 미리보기 */}
          <div className="rounded-md border bg-muted/30 p-3">
            <div className="mb-2 text-[10px] text-muted-foreground">
              기본 이미지 미리보기
            </div>
            <div className="flex justify-center">
              <div className="flex h-16 w-16 items-center justify-center rounded-md border bg-card">
                <img
                  src={image.defaultImage || DEFAULT_CARD_IMAGE}
                  alt="기본 이미지"
                  className="h-12 w-12 object-contain"
                />
              </div>
            </div>
          </div>

          {/* 기본 이미지 URL */}
          <div>
            <Label className="text-[10px]">기본 이미지 URL</Label>
            <Input
              value={image.defaultImage || ""}
              onChange={(e) =>
                onUpdate({
                  ...image,
                  defaultImage: e.target.value || DEFAULT_CARD_IMAGE,
                })
              }
              placeholder="이미지 URL 입력"
              className="mt-1 h-7 text-xs"
            />
            <p className="mt-1 text-[10px] text-muted-foreground">
              이미지가 없는 항목에 표시될 기본 이미지
            </p>
          </div>

          {/* 이미지 컬럼 선택 */}
          <div>
            <Label className="text-[10px]">이미지 URL 컬럼 (선택)</Label>
            <GroupedColumnSelect
              columnGroups={columnGroups}
              value={image.imageColumn}
              onValueChange={(val) => onUpdate({ ...image, imageColumn: val })}
              placeholder="컬럼 선택 (선택사항)"
              allowNone
              noneLabel="선택 안함 (기본 이미지 사용)"
              className="mt-1"
            />
            <p className="mt-1 text-[10px] text-muted-foreground">
              DB에서 이미지 URL을 가져올 컬럼. URL이 없으면 기본 이미지 사용
            </p>
          </div>
        </>
      )}
    </div>
  );
}

// ===== 본문 필드 섹션 =====

function BodyFieldsSection({
  body,
  columnGroups,
  onUpdate,
}: {
  body: CardBodyConfig;
  columnGroups: ColumnGroup[];
  onUpdate: (body: CardBodyConfig) => void;
}) {
  const fields = body.fields || [];

  const addField = () => {
    const newField: CardFieldBinding = {
      id: `field-${Date.now()}`,
      label: "",
      valueType: "column",
      columnName: "",
    };
    onUpdate({ fields: [...fields, newField] });
  };

  // 필드 업데이트
  const updateField = (index: number, updated: CardFieldBinding) => {
    const newFields = [...fields];
    newFields[index] = updated;
    onUpdate({ fields: newFields });
  };

  // 필드 삭제
  const deleteField = (index: number) => {
    const newFields = fields.filter((_, i) => i !== index);
    onUpdate({ fields: newFields });
  };

  // 필드 순서 이동
  const moveField = (index: number, direction: "up" | "down") => {
    const newIndex = direction === "up" ? index - 1 : index + 1;
    if (newIndex < 0 || newIndex >= fields.length) return;

    const newFields = [...fields];
    [newFields[index], newFields[newIndex]] = [
      newFields[newIndex],
      newFields[index],
    ];
    onUpdate({ fields: newFields });
  };

  return (
    <div className="space-y-3">
      {/* 필드 목록 */}
      {fields.length === 0 ? (
        <div className="rounded-md border border-dashed bg-muted/30 p-4 text-center">
          <p className="text-xs text-muted-foreground">
            본문에 표시할 필드를 추가하세요
          </p>
        </div>
      ) : (
        <div className="space-y-2">
          {fields.map((field, index) => (
            <FieldEditor
              key={field.id}
              field={field}
              index={index}
              columnGroups={columnGroups}
              totalCount={fields.length}
              onUpdate={(updated) => updateField(index, updated)}
              onDelete={() => deleteField(index)}
              onMove={(dir) => moveField(index, dir)}
            />
          ))}
        </div>
      )}

      {/* 필드 추가 버튼 */}
      <Button
        variant="outline"
        size="sm"
        className="w-full text-xs"
        onClick={addField}
      >
        <Plus className="mr-1 h-3 w-3" />
        필드 추가
      </Button>
    </div>
  );
}

// ===== 필드 편집기 =====

const VALUE_TYPE_OPTIONS: { value: FieldValueType; label: string }[] = [
  { value: "column", label: "DB 컬럼" },
  { value: "formula", label: "계산식" },
];

const FORMULA_OPERATOR_OPTIONS: { value: FormulaOperator; label: string }[] = [
  { value: "+", label: "+ (더하기)" },
  { value: "-", label: "- (빼기)" },
  { value: "*", label: "* (곱하기)" },
  { value: "/", label: "/ (나누기)" },
];

const FORMULA_RIGHT_TYPE_OPTIONS: { value: FormulaRightType; label: string }[] = [
  { value: "column", label: "DB 컬럼" },
  { value: "input", label: "입력값" },
];

function FieldEditor({
  field,
  index,
  columnGroups,
  totalCount,
  onUpdate,
  onDelete,
  onMove,
}: {
  field: CardFieldBinding;
  index: number;
  columnGroups: ColumnGroup[];
  totalCount: number;
  onUpdate: (field: CardFieldBinding) => void;
  onDelete: () => void;
  onMove: (direction: "up" | "down") => void;
}) {
  const valueType = field.valueType || "column";
  const rightType = field.formulaRightType || "input";

  return (
    <div className="rounded-md border bg-card p-2">
      <div className="flex items-start gap-2">
        {/* 순서 이동 버튼 */}
        <div className="flex flex-col gap-0.5 pt-1">
          <button
            type="button"
            className="rounded p-0.5 text-muted-foreground hover:bg-muted disabled:opacity-30"
            onClick={() => onMove("up")}
            disabled={index === 0}
          >
            <ChevronDown className="h-3 w-3 rotate-180" />
          </button>
          <button
            type="button"
            className="rounded p-0.5 text-muted-foreground hover:bg-muted disabled:opacity-30"
            onClick={() => onMove("down")}
            disabled={index === totalCount - 1}
          >
            <ChevronDown className="h-3 w-3" />
          </button>
        </div>

        {/* 필드 설정 */}
        <div className="flex-1 space-y-2">
          {/* 1행: 라벨 + 값 유형 */}
          <div className="flex gap-2">
            <div className="flex-1">
              <Label className="text-[10px]">라벨</Label>
              <Input
                value={field.label}
                onChange={(e) => onUpdate({ ...field, label: e.target.value })}
                placeholder="예: 미입고"
                className="mt-1 h-7 text-xs"
              />
            </div>
            <div className="w-24">
              <Label className="text-[10px]">값 유형</Label>
              <Select
                value={valueType}
                onValueChange={(val: FieldValueType) => {
                  const base = { ...field, valueType: val };
                  if (val === "column") {
                    base.formulaLeft = undefined;
                    base.formulaOperator = undefined;
                    base.formulaRightType = undefined;
                    base.formulaRight = undefined;
                    base.formula = undefined;
                    base.unit = undefined;
                  } else {
                    base.columnName = undefined;
                  }
                  onUpdate(base);
                }}
              >
                <SelectTrigger className="mt-1 h-7 text-xs">
                  <SelectValue />
                </SelectTrigger>
                <SelectContent>
                  {VALUE_TYPE_OPTIONS.map((opt) => (
                    <SelectItem key={opt.value} value={opt.value}>
                      {opt.label}
                    </SelectItem>
                  ))}
                </SelectContent>
              </Select>
            </div>
          </div>

          {/* 2행: 컬럼 선택 또는 수식 빌더 */}
          {valueType === "column" ? (
            <div>
              <Label className="text-[10px]">컬럼</Label>
              <GroupedColumnSelect
                columnGroups={columnGroups}
                value={field.columnName || undefined}
                onValueChange={(val) => onUpdate({ ...field, columnName: val || "" })}
                placeholder="컬럼 선택"
                className="mt-1"
              />
            </div>
          ) : (
            <>
              {/* 왼쪽 값: DB 컬럼 */}
              <div>
                <Label className="text-[10px]">왼쪽 값 (DB 컬럼)</Label>
                <GroupedColumnSelect
                  columnGroups={columnGroups}
                  value={field.formulaLeft || undefined}
                  onValueChange={(val) => onUpdate({ ...field, formulaLeft: val || undefined })}
                  placeholder="컬럼 선택"
                  className="mt-1"
                />
              </div>

              {/* 연산자 */}
              <div>
                <Label className="text-[10px]">연산자</Label>
                <Select
                  value={field.formulaOperator || "__none__"}
                  onValueChange={(val) =>
                    onUpdate({ ...field, formulaOperator: val === "__none__" ? undefined : val as FormulaOperator })
                  }
                >
                  <SelectTrigger className="mt-1 h-7 text-xs">
                    <SelectValue placeholder="선택" />
                  </SelectTrigger>
                  <SelectContent>
                    <SelectItem value="__none__" disabled>선택</SelectItem>
                    {FORMULA_OPERATOR_OPTIONS.map((opt) => (
                      <SelectItem key={opt.value} value={opt.value}>
                        {opt.label}
                      </SelectItem>
                    ))}
                  </SelectContent>
                </Select>
              </div>

              {/* 오른쪽 값 유형 + 값 */}
              <div className="space-y-1.5">
                <Label className="text-[10px]">오른쪽 값</Label>
                <Select
                  value={rightType}
                  onValueChange={(val: FormulaRightType) => {
                    const base = { ...field, formulaRightType: val };
                    if (val === "input") {
                      base.formulaRight = undefined;
                    }
                    onUpdate(base);
                  }}
                >
                  <SelectTrigger className="h-7 text-xs">
                    <SelectValue />
                  </SelectTrigger>
                  <SelectContent>
                    {FORMULA_RIGHT_TYPE_OPTIONS.map((opt) => (
                      <SelectItem key={opt.value} value={opt.value}>
                        {opt.label}
                      </SelectItem>
                    ))}
                  </SelectContent>
                </Select>

                {rightType === "column" && (
                  <GroupedColumnSelect
                    columnGroups={columnGroups}
                    value={field.formulaRight || undefined}
                    onValueChange={(val) => onUpdate({ ...field, formulaRight: val || undefined })}
                    placeholder="컬럼 선택"
                  />
                )}

                {rightType === "input" && (
                  <p className="text-[9px] text-muted-foreground">
                    카드의 숫자 입력 필드 값이 사용됩니다
                  </p>
                )}
              </div>

              {/* 단위 */}
              <div>
                <Label className="text-[10px]">단위</Label>
                <Input
                  value={field.unit || ""}
                  onChange={(e) => onUpdate({ ...field, unit: e.target.value })}
                  className="mt-1 h-7 text-xs"
                  placeholder="EA"
                />
              </div>

              {/* 수식 미리보기 */}
              {field.formulaLeft && field.formulaOperator && (
                <div className="rounded bg-muted/50 px-2 py-1.5">
                  <p className="text-[9px] text-muted-foreground">수식 미리보기</p>
                  <p className="font-mono text-xs font-medium">
                    {field.formulaLeft} {field.formulaOperator} {rightType === "input" ? "$input" : (field.formulaRight || "?")}
                  </p>
                </div>
              )}
            </>
          )}

          {/* 텍스트 색상 */}
          <div>
            <Label className="text-[10px]">텍스트 색상</Label>
            <Select
              value={field.textColor || "__default__"}
              onValueChange={(val) =>
                onUpdate({ ...field, textColor: val === "__default__" ? undefined : val })
              }
            >
              <SelectTrigger className="mt-1 h-7 text-xs">
                <SelectValue placeholder="기본" />
              </SelectTrigger>
              <SelectContent>
                {COLOR_OPTIONS.map((opt) => (
                  <SelectItem key={opt.value} value={opt.value}>
                    <div className="flex items-center gap-2">
                      {opt.value !== "__default__" && (
                        <div
                          className="h-3 w-3 rounded-full"
                          style={{ backgroundColor: opt.value }}
                        />
                      )}
                      {opt.label}
                    </div>
                  </SelectItem>
                ))}
              </SelectContent>
            </Select>
          </div>
        </div>

        {/* 삭제 버튼 */}
        <Button
          variant="ghost"
          size="icon"
          className="h-6 w-6 shrink-0 text-destructive"
          onClick={onDelete}
        >
          <Trash2 className="h-3 w-3" />
        </Button>
      </div>
    </div>
  );
}


// ===== 입력 필드 설정 섹션 =====

function InputFieldSettingsSection({
  inputField,
  columns,
  tables,
  onUpdate,
}: {
  inputField?: CardInputFieldConfig;
  columns: ColumnInfo[];
  tables: TableInfo[];
  onUpdate: (inputField: CardInputFieldConfig) => void;
}) {
  const field = inputField || {
    enabled: false,
    unit: "EA",
  };

  // 하위 호환: maxColumn -> limitColumn 마이그레이션
  const effectiveLimitColumn = field.limitColumn || field.maxColumn;

  const updateField = (partial: Partial<CardInputFieldConfig>) => {
    onUpdate({ ...field, ...partial });
  };

  // 저장 테이블 컬럼 로드
  const [saveTableColumns, setSaveTableColumns] = useState<ColumnInfo[]>([]);
  useEffect(() => {
    if (field.saveTable) {
      fetchTableColumns(field.saveTable).then(setSaveTableColumns);
    } else {
      setSaveTableColumns([]);
    }
  }, [field.saveTable]);

  return (
    <div className="space-y-3">
      {/* 활성화 스위치 */}
      <div className="flex items-center justify-between">
        <Label className="text-[10px]">입력 필드 사용</Label>
        <Switch
          checked={field.enabled}
          onCheckedChange={(enabled) => updateField({ enabled })}
        />
      </div>

      {field.enabled && (
        <>
          {/* 단위 */}
          <div>
            <Label className="text-[10px] text-muted-foreground">단위</Label>
            <Input
              value={field.unit || ""}
              onChange={(e) => updateField({ unit: e.target.value })}
              className="mt-1 h-7 text-xs"
              placeholder="EA"
            />
          </div>

          {/* 제한 기준 컬럼 */}
          <div>
            <Label className="text-[10px] text-muted-foreground">
              제한 기준 컬럼
            </Label>
            <Select
              value={effectiveLimitColumn || "__none__"}
              onValueChange={(val) =>
                updateField({ limitColumn: val === "__none__" ? undefined : val, maxColumn: undefined })
              }
            >
              <SelectTrigger className="mt-1 h-7 text-xs">
                <SelectValue placeholder="컬럼 선택" />
              </SelectTrigger>
              <SelectContent>
                <SelectItem value="__none__">제한 없음</SelectItem>
                {columns.map((col) => (
                  <SelectItem key={col.name} value={col.name}>
                    {col.name}
                  </SelectItem>
                ))}
              </SelectContent>
            </Select>
            <p className="mt-1 text-[9px] text-muted-foreground">
              각 카드 행의 해당 컬럼 값이 숫자패드 최대값 (예: order_qty)
            </p>
          </div>

          {/* 저장 대상 테이블 (검색 가능) */}
          <div>
            <Label className="text-[10px] text-muted-foreground">
              저장 대상 테이블
            </Label>
            <TableCombobox
              tables={tables}
              value={field.saveTable || ""}
              onSelect={(tableName) =>
                updateField({
                  saveTable: tableName || undefined,
                  saveColumn: undefined,
                })
              }
            />
          </div>

          {/* 저장 대상 컬럼 */}
          {field.saveTable && (
            <div>
              <Label className="text-[10px] text-muted-foreground">
                저장 대상 컬럼
              </Label>
              <Select
                value={field.saveColumn || "__none__"}
                onValueChange={(val) =>
                  updateField({ saveColumn: val === "__none__" ? undefined : val })
                }
              >
                <SelectTrigger className="mt-1 h-7 text-xs">
                  <SelectValue placeholder="컬럼 선택" />
                </SelectTrigger>
                <SelectContent>
                  <SelectItem value="__none__">선택 안함</SelectItem>
                  {saveTableColumns.map((col) => (
                    <SelectItem key={col.name} value={col.name}>
                      {col.name}
                    </SelectItem>
                  ))}
                </SelectContent>
              </Select>
              <p className="mt-1 text-[9px] text-muted-foreground">
                입력값이 저장될 DB 컬럼
              </p>
            </div>
          )}

        </>
      )}
    </div>
  );
}

// ===== 포장등록 설정 섹션 =====

import { PACKAGE_UNITS } from "./PackageUnitModal";

function PackageSettingsSection({
  packageConfig,
  onUpdate,
}: {
  packageConfig?: CardPackageConfig;
  onUpdate: (config: CardPackageConfig) => void;
}) {
  const config: CardPackageConfig = packageConfig || {
    enabled: false,
  };

  const updateConfig = (partial: Partial<CardPackageConfig>) => {
    onUpdate({ ...config, ...partial });
  };

  const enabledSet = new Set(config.enabledUnits ?? PACKAGE_UNITS.map((u) => u.value));

  const toggleUnit = (value: string) => {
    const next = new Set(enabledSet);
    if (next.has(value)) {
      next.delete(value);
    } else {
      next.add(value);
    }
    updateConfig({ enabledUnits: Array.from(next) });
  };

  const addCustomUnit = () => {
    const existing = config.customUnits || [];
    const id = `custom_${Date.now()}`;
    updateConfig({
      customUnits: [...existing, { id, label: "" }],
    });
  };

  const updateCustomUnit = (id: string, label: string) => {
    const existing = config.customUnits || [];
    updateConfig({
      customUnits: existing.map((cu) => (cu.id === id ? { ...cu, label } : cu)),
    });
  };

  const removeCustomUnit = (id: string) => {
    const existing = config.customUnits || [];
    updateConfig({
      customUnits: existing.filter((cu) => cu.id !== id),
    });
  };

  return (
    <div className="space-y-3">
      <div className="flex items-center justify-between">
        <Label className="text-[10px]">포장등록 사용</Label>
        <Switch
          checked={config.enabled}
          onCheckedChange={(enabled) => updateConfig({ enabled })}
        />
      </div>

      {config.enabled && (
        <>
          {/* 기본 포장 단위 체크박스 */}
          <div>
            <Label className="text-[10px] text-muted-foreground">사용할 포장 단위</Label>
            <div className="mt-1 grid grid-cols-3 gap-1.5">
              {PACKAGE_UNITS.map((unit) => (
                <button
                  key={unit.value}
                  type="button"
                  onClick={() => toggleUnit(unit.value)}
                  className={cn(
                    "flex items-center gap-1.5 rounded-md border px-2 py-1.5 text-[10px] transition-colors",
                    enabledSet.has(unit.value)
                      ? "border-primary bg-primary/5 text-primary"
                      : "border-muted text-muted-foreground hover:bg-muted/50"
                  )}
                >
                  <span>{unit.emoji}</span>
                  <span>{unit.label}</span>
                </button>
              ))}
            </div>
          </div>

          {/* 커스텀 단위 */}
          <div>
            <Label className="text-[10px] text-muted-foreground">커스텀 단위</Label>
            <div className="mt-1 space-y-1.5">
              {(config.customUnits || []).map((cu) => (
                <div key={cu.id} className="flex items-center gap-1.5">
                  <Input
                    value={cu.label}
                    onChange={(e) => updateCustomUnit(cu.id, e.target.value)}
                    className="h-7 flex-1 text-xs"
                    placeholder="단위 이름 (예: 파렛트)"
                  />
                  <Button
                    variant="ghost"
                    size="icon"
                    className="h-7 w-7 shrink-0"
                    onClick={() => removeCustomUnit(cu.id)}
                  >
                    <Trash2 className="h-3 w-3" />
                  </Button>
                </div>
              ))}
            </div>
            <Button
              variant="outline"
              size="sm"
              className="mt-1.5 h-7 w-full text-[10px]"
              onClick={addCustomUnit}
            >
              <Plus className="mr-1 h-3 w-3" />
              단위 추가
            </Button>
          </div>

          {/* 계산 결과 안내 메시지 */}
          <div className="flex items-center justify-between">
            <Label className="text-[10px]">계산 결과 안내 메시지</Label>
            <Switch
              checked={config.showSummaryMessage !== false}
              onCheckedChange={(checked) => updateConfig({ showSummaryMessage: checked })}
            />
          </div>
          <p className="text-muted-foreground text-[9px]">
            포장 등록 시 계산 결과를 안내 메시지로 표시합니다
          </p>
        </>
      )}
    </div>
  );
}

// ===== 조인 설정 섹션 (테이블 선택 -> 컬럼 자동 매칭) =====

// 두 테이블 간 매칭 가능한 컬럼 쌍 찾기
function findMatchingColumns(
  sourceCols: ColumnInfo[],
  targetCols: ColumnInfo[],
): Array<{ source: string; target: string; confidence: "high" | "medium" }> {
  const matches: Array<{ source: string; target: string; confidence: "high" | "medium" }> = [];
  const sourceNames = sourceCols.map((c) => c.name);
  const targetNames = targetCols.map((c) => c.name);

  for (const src of sourceNames) {
    for (const tgt of targetNames) {
      // 정확히 같은 이름 (예: item_code = item_code)
      if (src === tgt) {
        matches.push({ source: src, target: tgt, confidence: "high" });
        continue;
      }
      // 소스가 _id/_code/_no 로 끝나고, 타겟 테이블에 같은 이름이 있는 경우
      // 예: source.customer_code -> target.customer_code (이미 위에서 처리됨)
      // 소스 컬럼명이 타겟 컬럼명을 포함하거나, 타겟이 소스를 포함
      const suffixes = ["_id", "_code", "_no", "_number", "_key"];
      const srcBase = suffixes.reduce((name, s) => name.endsWith(s) ? name.slice(0, -s.length) : name, src);
      const tgtBase = suffixes.reduce((name, s) => name.endsWith(s) ? name.slice(0, -s.length) : name, tgt);
      if (srcBase && tgtBase && srcBase === tgtBase && src !== tgt) {
        matches.push({ source: src, target: tgt, confidence: "medium" });
      }
    }
  }

  // confidence 높은 순 정렬
  return matches.sort((a, b) => (a.confidence === "high" ? -1 : 1) - (b.confidence === "high" ? -1 : 1));
}

function JoinSettingsSection({
  dataSource,
  tables,
  onUpdate,
}: {
  dataSource: CardListDataSource;
  tables: TableInfo[];
  onUpdate: (partial: Partial<CardListDataSource>) => void;
}) {
  const joins = dataSource.joins || [];
  const [sourceColumns, setSourceColumns] = useState<ColumnInfo[]>([]);
  const [targetColumnsMap, setTargetColumnsMap] = useState<Record<string, ColumnInfo[]>>({});

  useEffect(() => {
    if (dataSource.tableName) {
      fetchTableColumns(dataSource.tableName).then(setSourceColumns);
    } else {
      setSourceColumns([]);
    }
  }, [dataSource.tableName]);

  const getTableLabel = (tableName: string) => {
    const found = tables.find((t) => t.tableName === tableName);
    return found?.displayName || tableName;
  };

  // 대상 테이블 컬럼 로드 + 자동 매칭
  const loadTargetAndAutoMatch = (index: number, join: CardColumnJoin, targetTable: string) => {
    const updated = { ...join, targetTable, sourceColumn: "", targetColumn: "" };

    const doMatch = (targetCols: ColumnInfo[]) => {
      const matches = findMatchingColumns(sourceColumns, targetCols);
      if (matches.length > 0) {
        updated.sourceColumn = matches[0].source;
        updated.targetColumn = matches[0].target;
      }
      const newJoins = [...joins];
      newJoins[index] = updated;
      onUpdate({ joins: newJoins });
    };

    if (targetColumnsMap[targetTable]) {
      doMatch(targetColumnsMap[targetTable]);
    } else {
      fetchTableColumns(targetTable).then((cols) => {
        setTargetColumnsMap((prev) => ({ ...prev, [targetTable]: cols }));
        doMatch(cols);
      });
    }
  };

  const updateJoin = (index: number, updated: CardColumnJoin) => {
    const newJoins = [...joins];
    newJoins[index] = updated;
    onUpdate({ joins: newJoins });
  };

  const deleteJoin = (index: number) => {
    const newJoins = joins.filter((_, i) => i !== index);
    onUpdate({ joins: newJoins.length > 0 ? newJoins : undefined });
  };

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

  return (
    <div className="space-y-3">
      {joins.length === 0 ? (
        <div className="rounded-md border border-dashed bg-muted/30 p-3 text-center">
          <p className="text-xs text-muted-foreground">
            다른 테이블을 연결하면 추가 정보를 카드에 표시할 수 있습니다
          </p>
        </div>
      ) : (
        <div className="space-y-2">
          {joins.map((join, index) => {
            const targetCols = targetColumnsMap[join.targetTable] || [];
            const matchingPairs = join.targetTable
              ? findMatchingColumns(sourceColumns, targetCols)
              : [];
            const hasAutoMatch = join.sourceColumn && join.targetColumn;

            return (
              <div key={index} className="rounded-md border bg-card p-2 space-y-2">
                <div className="flex items-center justify-between">
                  <span className="text-[10px] font-medium">
                    {join.targetTable ? getTableLabel(join.targetTable) : "테이블 선택"}
                  </span>
                  <Button variant="ghost" size="icon" className="h-5 w-5 text-destructive" onClick={() => deleteJoin(index)}>
                    <Trash2 className="h-3 w-3" />
                  </Button>
                </div>

                {/* 대상 테이블 선택 (검색 가능) */}
                <TableCombobox
                  tables={tables.filter((t) => t.tableName !== dataSource.tableName)}
                  value={join.targetTable || ""}
                  onSelect={(val) => loadTargetAndAutoMatch(index, join, val)}
                />

                {/* 자동 매칭 결과 또는 수동 선택 */}
                {join.targetTable && (
                  <div className="space-y-1.5">
                    {/* 자동 매칭 성공: 결과 표시 */}
                    {hasAutoMatch ? (
                      <div className="flex items-center gap-1.5 rounded-md bg-primary/5 border border-primary/20 px-2 py-1.5">
                        <Check className="h-3 w-3 shrink-0 text-primary" />
                        <span className="text-[10px] text-primary">
                          자동 연결됨: {join.sourceColumn} = {join.targetColumn}
                        </span>
                      </div>
                    ) : (
                      <div className="rounded-md bg-muted/50 px-2 py-1.5">
                        <span className="text-[9px] text-muted-foreground">
                          자동 매칭되는 컬럼이 없습니다. 직접 선택하세요.
                        </span>
                      </div>
                    )}

                    {/* 다른 매칭 후보가 있으면 표시 */}
                    {matchingPairs.length > 1 && (
                      <div className="space-y-1">
                        <span className="text-[9px] text-muted-foreground">다른 연결 기준:</span>
                        {matchingPairs.map((pair) => {
                          const isActive = join.sourceColumn === pair.source && join.targetColumn === pair.target;
                          if (isActive) return null;
                          return (
                            <button
                              key={`${pair.source}-${pair.target}`}
                              type="button"
                              className="flex w-full items-center gap-1.5 rounded border border-input px-2 py-1 text-left text-[9px] transition-colors hover:bg-accent/50"
                              onClick={() => updateJoin(index, { ...join, sourceColumn: pair.source, targetColumn: pair.target })}
                            >
                              <span>{pair.source} = {pair.target}</span>
                              {pair.confidence === "high" && (
                                <span className="rounded bg-primary/10 px-1 text-[8px] text-primary">정확</span>
                              )}
                            </button>
                          );
                        })}
                      </div>
                    )}

                    {/* 수동 선택 (펼치기) */}
                    <details className="group">
                      <summary className="cursor-pointer text-[9px] text-muted-foreground hover:text-foreground">
                        직접 컬럼 선택
                      </summary>
                      <div className="mt-1.5 flex items-center gap-1">
                        <Select
                          value={join.sourceColumn || ""}
                          onValueChange={(val) => updateJoin(index, { ...join, sourceColumn: val })}
                        >
                          <SelectTrigger className="h-6 flex-1 text-[10px]">
                            <SelectValue placeholder="소스 컬럼" />
                          </SelectTrigger>
                          <SelectContent>
                            {sourceColumns.map((col) => (
                              <SelectItem key={col.name} value={col.name}>{col.name}</SelectItem>
                            ))}
                          </SelectContent>
                        </Select>
                        <span className="text-[10px] text-muted-foreground">=</span>
                        <Select
                          value={join.targetColumn || ""}
                          onValueChange={(val) => updateJoin(index, { ...join, targetColumn: val })}
                        >
                          <SelectTrigger className="h-6 flex-1 text-[10px]">
                            <SelectValue placeholder="대상 컬럼" />
                          </SelectTrigger>
                          <SelectContent>
                            {targetCols.map((col) => (
                              <SelectItem key={col.name} value={col.name}>{col.name}</SelectItem>
                            ))}
                          </SelectContent>
                        </Select>
                      </div>
                    </details>
                  </div>
                )}
              </div>
            );
          })}
        </div>
      )}

      <Button variant="outline" size="sm" className="w-full text-xs" onClick={addJoin}>
        <Plus className="mr-1 h-3 w-3" />
        테이블 연결 추가
      </Button>
    </div>
  );
}

// ===== 필터 설정 섹션 =====

function FilterSettingsSection({
  dataSource,
  columns,
  onUpdate,
}: {
  dataSource: CardListDataSource;
  columns: ColumnInfo[];
  onUpdate: (partial: Partial<CardListDataSource>) => void;
}) {
  const filters = dataSource.filters || [];

  const operators: { value: FilterOperator; label: string }[] = [
    { value: "=", label: "=" },
    { value: "!=", label: "!=" },
    { value: ">", label: ">" },
    { value: ">=", label: ">=" },
    { value: "<", label: "<" },
    { value: "<=", label: "<=" },
    { value: "like", label: "LIKE" },
  ];

  // 필터 추가
  const addFilter = () => {
    const newFilter: CardColumnFilter = {
      column: "",
      operator: "=",
      value: "",
    };
    onUpdate({ filters: [...filters, newFilter] });
  };

  // 필터 업데이트
  const updateFilter = (index: number, updated: CardColumnFilter) => {
    const newFilters = [...filters];
    newFilters[index] = updated;
    onUpdate({ filters: newFilters });
  };

  // 필터 삭제
  const deleteFilter = (index: number) => {
    const newFilters = filters.filter((_, i) => i !== index);
    onUpdate({ filters: newFilters.length > 0 ? newFilters : undefined });
  };

  return (
    <div className="space-y-3">
      {filters.length === 0 ? (
        <div className="rounded-md border border-dashed bg-muted/30 p-3 text-center">
          <p className="text-xs text-muted-foreground">
            필터 조건을 추가하여 데이터를 필터링할 수 있습니다
          </p>
        </div>
      ) : (
        <div className="space-y-2">
          {filters.map((filter, index) => (
            <div
              key={index}
              className="space-y-1.5 rounded-md border bg-card p-2"
            >
              <div className="flex items-center justify-between">
                <span className="text-[10px] font-medium text-muted-foreground">
                  조건 {index + 1}
                </span>
                <Button
                  variant="ghost"
                  size="icon"
                  className="h-6 w-6 text-destructive"
                  onClick={() => deleteFilter(index)}
                >
                  <Trash2 className="h-3 w-3" />
                </Button>
              </div>
              <Select
                value={filter.column || ""}
                onValueChange={(val) =>
                  updateFilter(index, { ...filter, column: val })
                }
              >
                <SelectTrigger className="h-8 text-xs">
                  <SelectValue placeholder="컬럼 선택" />
                </SelectTrigger>
                <SelectContent>
                  {columns.map((col) => (
                    <SelectItem key={col.name} value={col.name}>
                      {col.name}
                    </SelectItem>
                  ))}
                </SelectContent>
              </Select>
              <div className="flex items-center gap-2">
                <Select
                  value={filter.operator}
                  onValueChange={(val) =>
                    updateFilter(index, {
                      ...filter,
                      operator: val as FilterOperator,
                    })
                  }
                >
                  <SelectTrigger className="h-8 w-20 shrink-0 text-xs">
                    <SelectValue />
                  </SelectTrigger>
                  <SelectContent>
                    {operators.map((op) => (
                      <SelectItem key={op.value} value={op.value}>
                        {op.label}
                      </SelectItem>
                    ))}
                  </SelectContent>
                </Select>
                <Input
                  value={filter.value}
                  onChange={(e) =>
                    updateFilter(index, { ...filter, value: e.target.value })
                  }
                  placeholder="값 입력"
                  className="h-8 flex-1 text-xs"
                />
              </div>
            </div>
          ))}
        </div>
      )}

      <Button
        variant="outline"
        size="sm"
        className="w-full text-xs"
        onClick={addFilter}
      >
        <Plus className="mr-1 h-3 w-3" />
        필터 추가
      </Button>
    </div>
  );
}

// ===== 정렬 설정 섹션 (다중 정렬) =====

function SortSettingsSection({
  dataSource,
  columnGroups,
  onUpdate,
}: {
  dataSource: CardListDataSource;
  columnGroups: ColumnGroup[];
  onUpdate: (partial: Partial<CardListDataSource>) => void;
}) {
  // 하위 호환: 이전 형식(단일 객체)이 저장되어 있을 수 있음
  const sorts: CardSortConfig[] = Array.isArray(dataSource.sort)
    ? dataSource.sort
    : dataSource.sort && typeof dataSource.sort === "object"
      ? [dataSource.sort as CardSortConfig]
      : [];

  const addSort = () => {
    onUpdate({ sort: [...sorts, { column: "", direction: "desc" }] });
  };

  const updateSort = (index: number, updated: CardSortConfig) => {
    const newSorts = [...sorts];
    newSorts[index] = updated;
    onUpdate({ sort: newSorts });
  };

  const deleteSort = (index: number) => {
    const newSorts = sorts.filter((_, i) => i !== index);
    onUpdate({ sort: newSorts.length > 0 ? newSorts : undefined });
  };

  return (
    <div className="space-y-3">
      <p className="text-[10px] text-muted-foreground">
        화면 로드 시 적용되는 기본 정렬 순서입니다. 위에 있는 항목이 우선 적용됩니다.
      </p>

      {sorts.length === 0 ? (
        <div className="rounded-md border border-dashed bg-muted/30 p-3 text-center">
          <p className="text-xs text-muted-foreground">정렬 기준이 없습니다</p>
        </div>
      ) : (
        <div className="space-y-2">
          {sorts.map((sort, index) => (
            <div key={index} className="flex items-center gap-1.5">
              <span className="shrink-0 text-[10px] text-muted-foreground w-3 text-right">
                {index + 1}
              </span>
              <div className="flex-1">
                <GroupedColumnSelect
                  columnGroups={columnGroups}
                  value={sort.column || undefined}
                  onValueChange={(val) => updateSort(index, { ...sort, column: val || "" })}
                  placeholder="컬럼 선택"
                />
              </div>
              <button
                type="button"
                className={`shrink-0 rounded border px-2 py-1 text-[10px] ${
                  sort.direction === "asc"
                    ? "border-primary bg-primary/10 text-primary"
                    : "border-input text-muted-foreground hover:border-primary/50"
                }`}
                onClick={() => updateSort(index, { ...sort, direction: "asc" })}
              >
                오름
              </button>
              <button
                type="button"
                className={`shrink-0 rounded border px-2 py-1 text-[10px] ${
                  sort.direction === "desc"
                    ? "border-primary bg-primary/10 text-primary"
                    : "border-input text-muted-foreground hover:border-primary/50"
                }`}
                onClick={() => updateSort(index, { ...sort, direction: "desc" })}
              >
                내림
              </button>
              <Button
                variant="ghost"
                size="icon"
                className="h-5 w-5 shrink-0 text-destructive"
                onClick={() => deleteSort(index)}
              >
                <Trash2 className="h-3 w-3" />
              </Button>
            </div>
          ))}
        </div>
      )}

      <Button
        variant="outline"
        size="sm"
        className="w-full text-xs"
        onClick={addSort}
      >
        <Plus className="mr-1 h-3 w-3" />
        정렬 기준 추가
      </Button>
    </div>
  );
}

// ===== 표시 개수 설정 섹션 =====

function LimitSettingsSection({
  dataSource,
  onUpdate,
}: {
  dataSource: CardListDataSource;
  onUpdate: (partial: Partial<CardListDataSource>) => void;
}) {
  const limit = dataSource.limit || { mode: "all" as const };
  const isLimited = limit.mode === "limited";

  return (
    <div className="space-y-3">
      {/* 모드 선택 */}
      <div className="flex gap-2">
        <button
          type="button"
          className={`flex-1 rounded border px-2 py-1.5 text-xs ${
            !isLimited ? "border-primary bg-primary/10" : "border-input"
          }`}
          onClick={() => onUpdate({ limit: { mode: "all" } })}
        >
          전체 보기
        </button>
        <button
          type="button"
          className={`flex-1 rounded border px-2 py-1.5 text-xs ${
            isLimited ? "border-primary bg-primary/10" : "border-input"
          }`}
          onClick={() => onUpdate({ limit: { mode: "limited", count: 10 } })}
        >
          개수 제한
        </button>
      </div>

      {isLimited && (
        <div>
          <Label className="text-[10px]">표시 개수</Label>
          <Input
            type="number"
            min={1}
            value={limit.count || 10}
            onChange={(e) =>
              onUpdate({
                limit: {
                  mode: "limited",
                  count: parseInt(e.target.value, 10) || 10,
                },
              })
            }
            className="mt-1 h-7 text-xs"
          />
        </div>
      )}
    </div>
  );
}

// ===== 행 식별 키 컬럼 선택 =====

function KeyColumnSelect({
  tableName,
  value,
  onValueChange,
}: {
  tableName?: string;
  value: string;
  onValueChange: (v: string) => void;
}) {
  const [columns, setColumns] = useState<ColumnInfo[]>([]);

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

  const options = useMemo(() => {
    const seen = new Set<string>();
    const unique: ColumnInfo[] = [];
    const hasId = columns.some((c) => c.name === "id");
    if (!hasId) {
      unique.push({ name: "id", type: "uuid", udtName: "uuid" });
      seen.add("id");
    }
    for (const c of columns) {
      if (!seen.has(c.name)) {
        seen.add(c.name);
        unique.push(c);
      }
    }
    return unique;
  }, [columns]);

  return (
    <Select value={value} onValueChange={onValueChange}>
      <SelectTrigger className="mt-1 h-7 text-xs">
        <SelectValue placeholder="id" />
      </SelectTrigger>
      <SelectContent>
        {options.map((c) => (
          <SelectItem key={c.name} value={c.name} className="text-xs">
            {c.name === "id" ? "id (UUID, 기본)" : c.name}
          </SelectItem>
        ))}
      </SelectContent>
    </Select>
  );
}

// ===== 담기 버튼 설정 섹션 =====

function CartActionSettingsSection({
  cartAction,
  onUpdate,
  cardTemplate,
  tableName,
}: {
  cartAction?: CardCartActionConfig;
  onUpdate: (cartAction: CardCartActionConfig) => void;
  cardTemplate?: CardTemplateConfig;
  tableName?: string;
}) {
  const action: CardCartActionConfig = cartAction || {
    saveMode: "cart",
    label: "담기",
    cancelLabel: "취소",
  };

  const update = (partial: Partial<CardCartActionConfig>) => {
    onUpdate({ ...action, ...partial });
  };

  const saveMode = action.saveMode || "cart";

  // 카드 템플릿에서 사용 중인 필드 목록 수집
  const usedFields = useMemo(() => {
    const fields: { name: string; label: string; source: string }[] = [];

    if (cardTemplate?.header?.codeField) {
      fields.push({ name: cardTemplate.header.codeField, label: "코드 (헤더)", source: "헤더" });
    }
    if (cardTemplate?.header?.titleField) {
      fields.push({ name: cardTemplate.header.titleField, label: "제목 (헤더)", source: "헤더" });
    }
    if (cardTemplate?.body?.fields) {
      for (const f of cardTemplate.body.fields) {
        if (f.valueType === "column" && f.columnName) {
          fields.push({ name: f.columnName, label: f.label || f.columnName, source: "본문" });
        }
      }
    }
    return fields;
  }, [cardTemplate]);

  return (
    <div className="space-y-3">
      {/* 저장 방식 */}
      <div>
        <Label className="text-[10px]">저장 방식</Label>
        <Select
          value={saveMode}
          onValueChange={(v) => update({ saveMode: v as "cart" | "direct" })}
        >
          <SelectTrigger className="mt-1 h-7 text-xs">
            <SelectValue />
          </SelectTrigger>
          <SelectContent>
            <SelectItem value="cart" className="text-xs">장바구니에 담기</SelectItem>
            <SelectItem value="direct" className="text-xs">바로 저장 (향후 지원)</SelectItem>
          </SelectContent>
        </Select>
      </div>

      {/* 행 식별 키 컬럼 */}
      {saveMode === "cart" && (
        <div>
          <Label className="text-[10px]">행 식별 키 컬럼</Label>
          <KeyColumnSelect
            tableName={tableName}
            value={action.keyColumn || "id"}
            onValueChange={(v) => update({ keyColumn: v })}
          />
          <p className="text-muted-foreground mt-1 text-[10px]">
            각 행을 고유하게 식별하는 컬럼입니다. 기본값: id (UUID)
          </p>
        </div>
      )}

      {/* 담기 라벨 */}
      <div>
        <Label className="text-[10px]">담기 라벨</Label>
        <Input
          value={action.label || ""}
          onChange={(e) => update({ label: e.target.value })}
          placeholder="담기"
          className="mt-1 h-7 text-xs"
        />
      </div>

      {/* 취소 라벨 */}
      <div>
        <Label className="text-[10px]">취소 라벨</Label>
        <Input
          value={action.cancelLabel || ""}
          onChange={(e) => update({ cancelLabel: e.target.value })}
          placeholder="취소"
          className="mt-1 h-7 text-xs"
        />
      </div>

      {/* 저장 데이터 정보 (읽기 전용) */}
      <div className="space-y-2">
        <Label className="text-[10px]">저장 데이터 정보</Label>
        <div className="rounded-md border bg-muted/30 p-2">
          <p className="text-muted-foreground text-[10px]">
            담기 시 <span className="font-medium text-foreground">{tableName || "(테이블 미선택)"}</span>의
            모든 컬럼 데이터가 JSON으로 저장됩니다.
          </p>

          {usedFields.length > 0 && (
            <div className="mt-2">
              <p className="text-muted-foreground mb-1 text-[10px]">카드에 표시 중인 필드:</p>
              <div className="space-y-0.5">
                {usedFields.map((f) => (
                  <div key={`${f.source}-${f.name}`} className="flex items-center gap-1.5 text-[10px]">
                    <span className="inline-block w-8 rounded bg-muted px-1 text-center text-[9px] text-muted-foreground">
                      {f.source}
                    </span>
                    <code className="font-mono text-foreground">{f.name}</code>
                    <span className="text-muted-foreground">- {f.label}</span>
                  </div>
                ))}
              </div>
            </div>
          )}

          <p className="text-muted-foreground mt-2 text-[10px]">
            + 입력 수량, 포장 단위 등 추가 정보도 함께 저장됩니다.
            <br />
            장바구니 목록 화면에서 대상 테이블로 매핑 설정이 가능합니다.
          </p>
        </div>
      </div>
    </div>
  );
}

// ===== 반응형 표시 설정 섹션 =====

const RESPONSIVE_MODES: ResponsiveDisplayMode[] = ["required", "shrink", "hidden"];

function ResponsiveDisplaySection({
  config,
  onUpdate,
}: {
  config: PopCardListConfig;
  onUpdate: (partial: Partial<PopCardListConfig>) => void;
}) {
  const template = config.cardTemplate || { header: {}, image: { enabled: false }, body: { fields: [] } };
  const responsive = config.responsiveDisplay || {};
  const bodyFields = template.body?.fields || [];

  const hasHeader = !!template.header?.codeField || !!template.header?.titleField;
  const hasImage = !!template.image?.enabled;
  const hasFields = bodyFields.length > 0;

  const updateResponsive = (partial: Partial<CardResponsiveConfig>) => {
    onUpdate({ responsiveDisplay: { ...responsive, ...partial } });
  };

  const updateFieldMode = (fieldId: string, mode: ResponsiveDisplayMode) => {
    updateResponsive({
      fields: { ...(responsive.fields || {}), [fieldId]: mode },
    });
  };

  if (!hasHeader && !hasImage && !hasFields) {
    return (
      <div className="rounded-md border border-dashed bg-muted/30 p-4 text-center">
        <p className="text-xs text-muted-foreground">
          카드 템플릿에 항목을 먼저 추가하세요
        </p>
      </div>
    );
  }

  return (
    <div className="space-y-3">
      <p className="text-[10px] text-muted-foreground">
        화면이 좁아질 때 각 항목이 어떻게 표시될지 설정합니다.
      </p>

      <div className="flex gap-3 rounded-md bg-muted/30 px-2.5 py-1.5">
        {RESPONSIVE_MODES.map((mode) => (
          <span key={mode} className="text-[10px] text-muted-foreground">
            <span className="font-medium">{RESPONSIVE_DISPLAY_LABELS[mode]}</span>
            {mode === "required" && " = 항상 표시"}
            {mode === "shrink" && " = 축소 가능"}
            {mode === "hidden" && " = 숨김 가능"}
          </span>
        ))}
      </div>

      <div className="space-y-1">
        {template.header?.codeField && (
          <ResponsiveDisplayRow
            label="코드"
            sublabel={template.header.codeField}
            value={responsive.code || "required"}
            onChange={(mode) => updateResponsive({ code: mode })}
          />
        )}
        {template.header?.titleField && (
          <ResponsiveDisplayRow
            label="제목"
            sublabel={template.header.titleField}
            value={responsive.title || "required"}
            onChange={(mode) => updateResponsive({ title: mode })}
          />
        )}
        {hasImage && (
          <ResponsiveDisplayRow
            label="이미지"
            value={responsive.image || "shrink"}
            onChange={(mode) => updateResponsive({ image: mode })}
          />
        )}
        {bodyFields.map((field) => (
          <ResponsiveDisplayRow
            key={field.id}
            label={field.label || "(라벨 없음)"}
            sublabel={field.columnName}
            value={responsive.fields?.[field.id] || "shrink"}
            onChange={(mode) => updateFieldMode(field.id, mode)}
          />
        ))}
      </div>
    </div>
  );
}

function ResponsiveDisplayRow({
  label,
  sublabel,
  value,
  onChange,
}: {
  label: string;
  sublabel?: string;
  value: ResponsiveDisplayMode;
  onChange: (mode: ResponsiveDisplayMode) => void;
}) {
  return (
    <div className="flex items-center gap-2 rounded-md border bg-card px-2 py-1.5">
      <div className="flex-1 min-w-0">
        <span className="text-xs font-medium truncate block">{label}</span>
        {sublabel && (
          <span className="text-[10px] text-muted-foreground truncate block">
            {sublabel}
          </span>
        )}
      </div>
      <div className="flex shrink-0 gap-0.5">
        {RESPONSIVE_MODES.map((mode) => (
          <button
            key={mode}
            type="button"
            className={`rounded px-2 py-0.5 text-[10px] transition-colors ${
              value === mode
                ? mode === "required"
                  ? "bg-primary text-primary-foreground"
                  : mode === "shrink"
                    ? "bg-amber-500/20 text-amber-700 border border-amber-500/30"
                    : "bg-muted text-muted-foreground border border-input"
                : "text-muted-foreground hover:bg-muted/50"
            }`}
            onClick={() => onChange(mode)}
          >
            {RESPONSIVE_DISPLAY_LABELS[mode]}
          </button>
        ))}
      </div>
    </div>
  );
}

// ===== 필터 기준 섹션 (columnGroups 기반) =====

const FILTER_OPERATORS: { value: FilterOperator; label: string }[] = [
  { value: "=", label: "=" },
  { value: "!=", label: "!=" },
  { value: ">", label: ">" },
  { value: "<", label: "<" },
  { value: ">=", label: ">=" },
  { value: "<=", label: "<=" },
  { value: "like", label: "포함" },
];

function FilterCriteriaSection({
  dataSource,
  columnGroups,
  onUpdate,
}: {
  dataSource: CardListDataSource;
  columnGroups: ColumnGroup[];
  onUpdate: (partial: Partial<CardListDataSource>) => void;
}) {
  const filters = dataSource.filters || [];

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

  const updateFilter = (index: number, updated: CardColumnFilter) => {
    const next = [...filters];
    next[index] = updated;
    onUpdate({ filters: next });
  };

  const deleteFilter = (index: number) => {
    const next = filters.filter((_, i) => i !== index);
    onUpdate({ filters: next.length > 0 ? next : undefined });
  };

  return (
    <div className="space-y-3">
      <p className="text-[10px] text-muted-foreground">
        데이터 조회 시 적용할 필터 조건입니다.
      </p>

      {filters.length === 0 ? (
        <div className="rounded-md border border-dashed bg-muted/30 p-3 text-center">
          <p className="text-xs text-muted-foreground">필터 조건이 없습니다</p>
        </div>
      ) : (
        <div className="space-y-2">
          {filters.map((filter, index) => (
            <div key={index} className="space-y-1.5 rounded-md border bg-card p-2">
              <div className="flex items-center justify-between">
                <span className="text-[10px] font-medium text-muted-foreground">
                  조건 {index + 1}
                </span>
                <Button
                  variant="ghost"
                  size="icon"
                  className="h-6 w-6 text-destructive"
                  onClick={() => deleteFilter(index)}
                >
                  <Trash2 className="h-3 w-3" />
                </Button>
              </div>
              <GroupedColumnSelect
                columnGroups={columnGroups}
                value={filter.column || undefined}
                onValueChange={(val) => updateFilter(index, { ...filter, column: val || "" })}
                placeholder="컬럼 선택"
              />
              <div className="flex items-center gap-2">
                <Select
                  value={filter.operator}
                  onValueChange={(val) =>
                    updateFilter(index, { ...filter, operator: val as FilterOperator })
                  }
                >
                  <SelectTrigger className="h-8 w-20 shrink-0 text-xs">
                    <SelectValue />
                  </SelectTrigger>
                  <SelectContent>
                    {FILTER_OPERATORS.map((op) => (
                      <SelectItem key={op.value} value={op.value}>
                        {op.label}
                      </SelectItem>
                    ))}
                  </SelectContent>
                </Select>
                <Input
                  value={filter.value}
                  onChange={(e) => updateFilter(index, { ...filter, value: e.target.value })}
                  placeholder="값 입력"
                  className="h-8 flex-1 text-xs"
                />
              </div>
            </div>
          ))}
        </div>
      )}

      <Button variant="outline" size="sm" className="w-full text-xs" onClick={addFilter}>
        <Plus className="mr-1 h-3 w-3" />
        필터 추가
      </Button>
    </div>
  );
}

// ===== 저장 매핑 섹션 (장바구니 -> 대상 테이블) =====

const CART_META_FIELDS = [
  { value: "__cart_quantity", label: "입력 수량" },
  { value: "__cart_package_unit", label: "포장 단위" },
  { value: "__cart_package_entries", label: "포장 내역" },
  { value: "__cart_memo", label: "메모" },
  { value: "__cart_row_key", label: "원본 키" },
];

interface CardDisplayedField {
  sourceField: string;
  label: string;
  badge: string;
}

function SaveMappingSection({
  saveMapping,
  onUpdate,
  cartListMode,
}: {
  saveMapping?: CardListSaveMapping;
  onUpdate: (mapping: CardListSaveMapping) => void;
  cartListMode?: CartListModeConfig;
}) {
  const mapping: CardListSaveMapping = saveMapping || { targetTable: "", mappings: [] };
  const [tables, setTables] = useState<TableInfo[]>([]);
  const [targetColumns, setTargetColumns] = useState<ColumnInfo[]>([]);
  const [sourceColumns, setSourceColumns] = useState<ColumnInfo[]>([]);
  const [sourceTableName, setSourceTableName] = useState("");
  const [cardDisplayedFields, setCardDisplayedFields] = useState<CardDisplayedField[]>([]);

  useEffect(() => {
    fetchTableList().then(setTables);
  }, []);

  // 원본 화면에서 테이블 컬럼 + 카드 템플릿 필드 추출
  useEffect(() => {
    if (!cartListMode?.sourceScreenId) {
      setSourceColumns([]);
      setSourceTableName("");
      setCardDisplayedFields([]);
      return;
    }

    screenApi
      .getLayoutPop(cartListMode.sourceScreenId)
      .then((layoutJson: any) => {
        const componentsMap = layoutJson?.components || {};
        const componentList = Object.values(componentsMap) as any[];

        const matched = cartListMode.sourceComponentId
          ? componentList.find((c: any) => c.id === cartListMode.sourceComponentId)
          : componentList.find((c: any) => c.type === "pop-card-list");

        const tableName = matched?.config?.dataSource?.tableName;
        if (tableName) {
          setSourceTableName(tableName);
          fetchTableColumns(tableName).then(setSourceColumns);
        }

        // 카드 템플릿에서 표시 중인 필드 추출
        const cardTemplate = matched?.config?.cardTemplate;
        const inputFieldConfig = matched?.config?.inputField;
        const packageConfig = matched?.config?.packageConfig;
        const displayed: CardDisplayedField[] = [];

        if (cardTemplate?.header?.codeField) {
          displayed.push({
            sourceField: cardTemplate.header.codeField,
            label: cardTemplate.header.codeField,
            badge: "헤더",
          });
        }
        if (cardTemplate?.header?.titleField) {
          displayed.push({
            sourceField: cardTemplate.header.titleField,
            label: cardTemplate.header.titleField,
            badge: "헤더",
          });
        }
        for (const f of cardTemplate?.body?.fields || []) {
          if (f.valueType === "column" && f.columnName) {
            displayed.push({
              sourceField: f.columnName,
              label: f.label || f.columnName,
              badge: "본문",
            });
          } else if (f.valueType === "formula" && f.label) {
            const formulaKey = `__formula_${f.id || f.label}`;
            displayed.push({
              sourceField: formulaKey,
              label: f.label,
              badge: "수식",
            });
          }
        }
        if (inputFieldConfig?.enabled) {
          displayed.push({
            sourceField: "__cart_quantity",
            label: "입력 수량",
            badge: "입력",
          });
        }
        if (packageConfig?.enabled) {
          displayed.push({
            sourceField: "__cart_package_unit",
            label: "포장 단위",
            badge: "포장",
          });
          displayed.push({
            sourceField: "__cart_package_entries",
            label: "포장 내역",
            badge: "포장",
          });
        }

        setCardDisplayedFields(displayed);
      })
      .catch(() => {
        setSourceColumns([]);
        setSourceTableName("");
        setCardDisplayedFields([]);
      });
  }, [cartListMode?.sourceScreenId, cartListMode?.sourceComponentId]);

  useEffect(() => {
    if (mapping.targetTable) {
      fetchTableColumns(mapping.targetTable).then(setTargetColumns);
    } else {
      setTargetColumns([]);
    }
  }, [mapping.targetTable]);

  // 카드에 표시된 필드 set (빠른 조회용)
  const cardFieldSet = useMemo(
    () => new Set(cardDisplayedFields.map((f) => f.sourceField)),
    [cardDisplayedFields]
  );

  const getSourceFieldLabel = (field: string) => {
    const cardField = cardDisplayedFields.find((f) => f.sourceField === field);
    if (cardField) return cardField.label;
    const meta = CART_META_FIELDS.find((f) => f.value === field);
    if (meta) return meta.label;
    return field;
  };

  const getFieldBadge = (field: string) => {
    const cardField = cardDisplayedFields.find((f) => f.sourceField === field);
    return cardField?.badge || null;
  };

  const isCartMeta = (field: string) => field.startsWith("__cart_");

  const getSourceTableDisplayName = () => {
    if (!sourceTableName) return "원본 데이터";
    const found = tables.find((t) => t.tableName === sourceTableName);
    return found?.displayName || sourceTableName;
  };

  const mappedSourceFields = useMemo(
    () => new Set(mapping.mappings.map((m) => m.sourceField)),
    [mapping.mappings]
  );

  // 카드에 표시된 필드가 로드되면 매핑에 누락된 필드를 자동 추가 (매핑 안함으로)
  useEffect(() => {
    if (!mapping.targetTable || cardDisplayedFields.length === 0) return;
    const existing = new Set(mapping.mappings.map((m) => m.sourceField));
    const missing = cardDisplayedFields.filter((f) => !existing.has(f.sourceField));
    if (missing.length === 0) return;
    onUpdate({
      ...mapping,
      mappings: [
        ...mapping.mappings,
        ...missing.map((f) => ({ sourceField: f.sourceField, targetColumn: "" })),
      ],
    });
  }, [cardDisplayedFields]); // eslint-disable-line react-hooks/exhaustive-deps

  // 카드에 표시된 필드 중 아직 매핑되지 않은 것
  const unmappedCardFields = useMemo(
    () => cardDisplayedFields.filter((f) => !mappedSourceFields.has(f.sourceField)),
    [cardDisplayedFields, mappedSourceFields]
  );

  // 카드에 없고 매핑도 안 된 원본 컬럼
  const availableExtraSourceFields = useMemo(
    () => sourceColumns.filter((col) => !cardFieldSet.has(col.name) && !mappedSourceFields.has(col.name)),
    [sourceColumns, cardFieldSet, mappedSourceFields]
  );

  // 카드에 없고 매핑도 안 된 장바구니 메타
  const availableExtraCartFields = useMemo(
    () => CART_META_FIELDS.filter((f) => !cardFieldSet.has(f.value) && !mappedSourceFields.has(f.value)),
    [cardFieldSet, mappedSourceFields]
  );

  // 대상 테이블 선택 -> 카드 표시 필드 전체 자동 매핑
  const updateTargetTable = (targetTable: string) => {
    fetchTableColumns(targetTable).then((targetCols) => {
      setTargetColumns(targetCols);

      const targetNameSet = new Set(targetCols.map((c) => c.name));
      const autoMappings: CardListSaveMappingEntry[] = [];

      for (const field of cardDisplayedFields) {
        autoMappings.push({
          sourceField: field.sourceField,
          targetColumn: targetNameSet.has(field.sourceField) ? field.sourceField : "",
        });
      }

      onUpdate({ targetTable, mappings: autoMappings });
    });
  };

  const addFieldMapping = (sourceField: string) => {
    const matched = targetColumns.find((tc) => tc.name === sourceField);
    onUpdate({
      ...mapping,
      mappings: [
        ...mapping.mappings,
        { sourceField, targetColumn: matched?.name || "" },
      ],
    });
  };

  const updateEntry = (index: number, updated: CardListSaveMappingEntry) => {
    const next = [...mapping.mappings];
    next[index] = updated;
    onUpdate({ ...mapping, mappings: next });
  };

  const deleteEntry = (index: number) => {
    const next = mapping.mappings.filter((_, i) => i !== index);
    onUpdate({ ...mapping, mappings: next });
  };

  const autoMatchedCount = mapping.mappings.filter((m) => m.targetColumn).length;

  // 매핑 행 렌더링 (공용)
  const renderMappingRow = (entry: CardListSaveMappingEntry, index: number) => {
    const badge = getFieldBadge(entry.sourceField);
    return (
      <div
        key={`${entry.sourceField}-${index}`}
        className="flex items-center gap-1.5 rounded-md border bg-card px-2 py-1.5"
      >
        <div className="flex min-w-0 flex-1 flex-col">
          <div className="flex items-center gap-1">
            <span className="truncate text-xs font-medium">
              {getSourceFieldLabel(entry.sourceField)}
            </span>
            {badge && (
              <span className="shrink-0 rounded bg-primary/10 px-1 py-0.5 text-[8px] font-medium text-primary">
                {badge}
              </span>
            )}
          </div>
          {isCartMeta(entry.sourceField) ? (
            !badge && <span className="text-[9px] text-muted-foreground">장바구니</span>
          ) : entry.sourceField.startsWith("__formula_") ? null : (
            <span className="truncate text-[9px] text-muted-foreground">
              {entry.sourceField}
            </span>
          )}
        </div>

        <span className="shrink-0 text-[10px] text-muted-foreground">→</span>

        <div className="flex-1">
          <Select
            value={entry.targetColumn || "__none__"}
            onValueChange={(val) =>
              updateEntry(index, {
                ...entry,
                targetColumn: val === "__none__" ? "" : val,
              })
            }
          >
            <SelectTrigger className="h-7 text-xs">
              <SelectValue placeholder="대상 컬럼 선택" />
            </SelectTrigger>
            <SelectContent>
              <SelectItem value="__none__">매핑 안함</SelectItem>
              {targetColumns.map((col) => (
                <SelectItem key={col.name} value={col.name}>
                  {col.name}
                </SelectItem>
              ))}
            </SelectContent>
          </Select>
        </div>

        <Button
          variant="ghost"
          size="icon"
          className="h-6 w-6 shrink-0 text-destructive"
          onClick={() => deleteEntry(index)}
        >
          <Trash2 className="h-3 w-3" />
        </Button>
      </div>
    );
  };

  // 매핑 목록을 카드필드 / 추가필드로 분리
  const cardMappings: { entry: CardListSaveMappingEntry; index: number }[] = [];
  const extraMappings: { entry: CardListSaveMappingEntry; index: number }[] = [];
  mapping.mappings.forEach((entry, index) => {
    if (cardFieldSet.has(entry.sourceField)) {
      cardMappings.push({ entry, index });
    } else {
      extraMappings.push({ entry, index });
    }
  });

  return (
    <div className="space-y-3">
      <p className="text-[10px] text-muted-foreground">
        대상 테이블을 선택하면 카드에 배치된 필드가 자동으로 매핑됩니다.
      </p>

      <div>
        <Label className="text-[10px] text-muted-foreground">대상 테이블</Label>
        <TableCombobox
          tables={tables}
          value={mapping.targetTable}
          onSelect={updateTargetTable}
        />
      </div>

      {!mapping.targetTable ? (
        <div className="rounded-md border border-dashed bg-muted/30 p-3 text-center">
          <p className="text-xs text-muted-foreground">대상 테이블을 먼저 선택하세요</p>
        </div>
      ) : (
        <>
          {/* 자동 매핑 안내 */}
          {autoMatchedCount > 0 && (
            <div className="flex items-center gap-1.5 rounded-md border border-primary/20 bg-primary/5 px-2.5 py-1.5">
              <Check className="h-3.5 w-3.5 shrink-0 text-primary" />
              <span className="text-[10px] text-primary">
                이름 일치 {autoMatchedCount}개 필드 자동 매핑
              </span>
            </div>
          )}

          {/* --- 카드에 표시된 필드 --- */}
          {(cardMappings.length > 0 || unmappedCardFields.length > 0) && (
            <div className="space-y-1.5">
              <div className="flex items-center gap-1.5">
                <div className="h-px flex-1 bg-border" />
                <span className="shrink-0 text-[10px] font-medium text-muted-foreground">
                  카드에 표시된 필드
                </span>
                <div className="h-px flex-1 bg-border" />
              </div>

              {cardMappings.map(({ entry, index }) => renderMappingRow(entry, index))}

              {/* 카드 필드 중 매핑 안 된 것 -> 칩으로 추가 */}
              {unmappedCardFields.length > 0 && (
                <div className="flex flex-wrap gap-1">
                  {unmappedCardFields.map((f) => (
                    <button
                      key={f.sourceField}
                      type="button"
                      onClick={() => addFieldMapping(f.sourceField)}
                      className="inline-flex items-center gap-1 rounded-md border border-primary/30 bg-primary/5 px-2 py-1 text-[10px] text-primary transition-colors hover:bg-primary/10"
                    >
                      <Plus className="h-2.5 w-2.5" />
                      {f.label}
                      <span className="rounded bg-primary/10 px-0.5 text-[8px]">{f.badge}</span>
                    </button>
                  ))}
                </div>
              )}
            </div>
          )}

          {/* --- 추가로 저장할 필드 --- */}
          {(extraMappings.length > 0 || availableExtraSourceFields.length > 0 || availableExtraCartFields.length > 0) && (
            <div className="space-y-1.5">
              <div className="flex items-center gap-1.5">
                <div className="h-px flex-1 bg-border" />
                <span className="shrink-0 text-[10px] font-medium text-muted-foreground">
                  추가 저장 필드
                </span>
                <div className="h-px flex-1 bg-border" />
              </div>

              {extraMappings.map(({ entry, index }) => renderMappingRow(entry, index))}

              {/* 추가 가능한 필드 칩 */}
              {(availableExtraSourceFields.length > 0 || availableExtraCartFields.length > 0) && (
                <div className="flex flex-wrap gap-1">
                  {availableExtraSourceFields.map((col) => (
                    <button
                      key={col.name}
                      type="button"
                      onClick={() => addFieldMapping(col.name)}
                      className="inline-flex items-center gap-1 rounded-md border border-input px-2 py-1 text-[10px] transition-colors hover:bg-accent"
                    >
                      <Plus className="h-2.5 w-2.5" />
                      {col.name}
                    </button>
                  ))}
                  {availableExtraCartFields.map((f) => (
                    <button
                      key={f.value}
                      type="button"
                      onClick={() => addFieldMapping(f.value)}
                      className="inline-flex items-center gap-1 rounded-md border border-dashed border-input px-2 py-1 text-[10px] text-muted-foreground transition-colors hover:bg-accent hover:text-foreground"
                    >
                      <Plus className="h-2.5 w-2.5" />
                      {f.label}
                    </button>
                  ))}
                </div>
              )}
            </div>
          )}
        </>
      )}
    </div>
  );
}
