"use client";

import React, { useState, useEffect, useCallback, useRef } from "react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Textarea } from "@/components/ui/textarea";
import {
  Table,
  TableBody,
  TableCell,
  TableHead,
  TableHeader,
  TableRow,
} from "@/components/ui/table";
import {
  Dialog,
  DialogContent,
  DialogHeader,
  DialogTitle,
  DialogDescription,
  DialogFooter,
} from "@/components/ui/dialog";
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "@/components/ui/select";
import { ExcelUploadModal } from "@/components/common/ExcelUploadModal";
import { DynamicSearchFilter, FilterValue } from "@/components/common/DynamicSearchFilter";
import { exportToExcel } from "@/lib/utils/excelExport";
import {
  Plus, Trash2, Save, Loader2, FileSpreadsheet, Download,
  Pencil, Copy, Settings2, Check, ChevronsUpDown,
} from "lucide-react";
import { Badge } from "@/components/ui/badge";
import { ImageUpload } from "@/components/common/ImageUpload";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "@/components/ui/command";
import { EDataTable, EDataTableColumn } from "@/components/common/EDataTable";
import { cn } from "@/lib/utils";
import { apiClient } from "@/lib/api/client";
import { useAuth } from "@/hooks/useAuth";
import { useTableSettings } from "@/hooks/useTableSettings";
import { TableSettingsModal } from "@/components/common/TableSettingsModal";
import { toast } from "sonner";

// 검색 가능한 카테고리 콤보박스
function CategoryCombobox({ options, value, onChange, placeholder }: {
  options: { code: string; label: string }[];
  value: string;
  onChange: (v: string) => void;
  placeholder: string;
}) {
  const [open, setOpen] = useState(false);
  const selected = options.find((o) => o.code === value);
  return (
    <Popover open={open} onOpenChange={setOpen}>
      <PopoverTrigger asChild>
        <Button variant="outline" role="combobox" aria-expanded={open} className="h-9 w-full justify-between font-normal">
          <span className="truncate">{selected?.label || <span className="text-muted-foreground">{placeholder}</span>}</span>
          <ChevronsUpDown className="ml-2 h-3.5 w-3.5 shrink-0 opacity-50" />
        </Button>
      </PopoverTrigger>
      <PopoverContent className="p-0" style={{ width: "var(--radix-popover-trigger-width)" }} align="start">
        <Command>
          <CommandInput placeholder="검색..." className="h-8" />
          <CommandList>
            <CommandEmpty>검색 결과가 없어요</CommandEmpty>
            <CommandGroup className="max-h-[200px] overflow-auto">
              {options.map((opt) => (
                <CommandItem key={opt.code} value={opt.label} onSelect={() => { onChange(opt.code); setOpen(false); }}>
                  <Check className={cn("mr-2 h-3.5 w-3.5", value === opt.code ? "opacity-100" : "opacity-0")} />
                  {opt.label}
                </CommandItem>
              ))}
            </CommandGroup>
          </CommandList>
        </Command>
      </PopoverContent>
    </Popover>
  );
}

// 다중 선택 카테고리 콤보박스
function MultiCategoryCombobox({ options, value, onChange, placeholder }: {
  options: { code: string; label: string }[];
  value: string;
  onChange: (v: string) => void;
  placeholder: string;
}) {
  const [open, setOpen] = useState(false);
  const selectedCodes = value ? value.split(",").map((c) => c.trim()).filter(Boolean) : [];
  const selectedLabels = selectedCodes.map((code) => options.find((o) => o.code === code)?.label || code).filter(Boolean);

  const toggle = (code: string) => {
    const next = selectedCodes.includes(code)
      ? selectedCodes.filter((c) => c !== code)
      : [...selectedCodes, code];
    onChange(next.join(","));
  };

  return (
    <Popover open={open} onOpenChange={setOpen}>
      <PopoverTrigger asChild>
        <Button variant="outline" role="combobox" aria-expanded={open} className="h-9 w-full justify-between font-normal">
          <span className="truncate">
            {selectedLabels.length > 0
              ? selectedLabels.join(", ")
              : <span className="text-muted-foreground">{placeholder}</span>}
          </span>
          <ChevronsUpDown className="ml-2 h-3.5 w-3.5 shrink-0 opacity-50" />
        </Button>
      </PopoverTrigger>
      <PopoverContent className="p-0" style={{ width: "var(--radix-popover-trigger-width)" }} align="start">
        <Command>
          <CommandInput placeholder="검색..." className="h-8" />
          <CommandList>
            <CommandEmpty>검색 결과가 없어요</CommandEmpty>
            <CommandGroup className="max-h-[200px] overflow-auto">
              {options.map((opt) => (
                <CommandItem key={opt.code} value={opt.label} onSelect={() => toggle(opt.code)}>
                  <Check className={cn("mr-2 h-3.5 w-3.5", selectedCodes.includes(opt.code) ? "opacity-100" : "opacity-0")} />
                  {opt.label}
                </CommandItem>
              ))}
            </CommandGroup>
          </CommandList>
        </Command>
      </PopoverContent>
    </Popover>
  );
}

const TABLE_NAME = "item_info";

const GRID_COLUMNS = [
  { key: "item_number", label: "품목코드" },
  { key: "item_name", label: "품명" },
  { key: "image", label: "이미지", type: "image" },
  { key: "division", label: "관리품목" },
  { key: "type", label: "품목구분" },
  { key: "size", label: "규격" },
  { key: "unit", label: "단위" },
  { key: "material", label: "재질" },
  { key: "status", label: "상태" },
  { key: "selling_price", label: "판매가격", align: "right" as const, formatNumber: true },
  { key: "standard_price", label: "기준단가", align: "right" as const, formatNumber: true },
  { key: "weight", label: "중량", align: "right" as const },
  { key: "inventory_unit", label: "재고단위" },
  { key: "user_type01", label: "대분류" },
  { key: "user_type02", label: "중분류" },
  { key: "lead_time", label: "생산 리드타임(일)", align: "right" as const },
];

const FORM_FIELDS = [
  { key: "item_number", label: "품목코드", type: "numbering", required: true, placeholder: "자동 채번" },
  { key: "item_name", label: "품명", type: "text", required: true },
  { key: "division", label: "관리품목", type: "multi-category" },
  { key: "type", label: "품목구분", type: "category" },
  { key: "size", label: "규격", type: "text" },
  { key: "unit", label: "단위", type: "category" },
  { key: "material", label: "재질", type: "category" },
  { key: "status", label: "상태", type: "category" },
  { key: "weight", label: "중량", type: "text", placeholder: "숫자 입력 (예: 3.5)" },
  { key: "volum", label: "부피", type: "text", placeholder: "숫자 입력 (예: 100)" },
  { key: "specific_gravity", label: "비중", type: "text", placeholder: "숫자 입력 (예: 7.85)" },
  { key: "inventory_unit", label: "재고단위", type: "category" },
  { key: "selling_price", label: "판매가격", type: "text" },
  { key: "standard_price", label: "기준단가", type: "text" },
  { key: "currency_code", label: "통화", type: "category" },
  { key: "user_type01", label: "대분류", type: "category" },
  { key: "user_type02", label: "중분류", type: "category" },
  { key: "lead_time", label: "생산 리드타임(일)", type: "text", placeholder: "숫자 입력 (예: 7)" },
  { key: "image", label: "품목 이미지", type: "image" },
  { key: "meno", label: "메모", type: "textarea" },
];

const CATEGORY_COLUMNS = [
  "division", "type", "unit", "material", "status",
  "inventory_unit", "currency_code", "user_type01", "user_type02",
];

export default function ItemInfoPage() {
  const { user } = useAuth();
  const ts = useTableSettings("c16-item-info", TABLE_NAME, GRID_COLUMNS);
  const [items, setItems] = useState<any[]>([]);
  const [rawItems, setRawItems] = useState<any[]>([]);
  const [loading, setLoading] = useState(false);

  // 검색 필터 (DynamicSearchFilter)
  const [searchFilters, setSearchFilters] = useState<FilterValue[]>([]);

  // 모달
  const [isModalOpen, setIsModalOpen] = useState(false);
  const [isEditMode, setIsEditMode] = useState(false);
  const [editId, setEditId] = useState<string | null>(null);
  const [saving, setSaving] = useState(false);
  const [formData, setFormData] = useState<Record<string, any>>({});

  // 엑셀 업로드
  const [excelUploadOpen, setExcelUploadOpen] = useState(false);

  // 카테고리 옵션 (API에서 로드)
  const [categoryOptions, setCategoryOptions] = useState<Record<string, { code: string; label: string }[]>>({});

  // 선택된 행
  const [selectedId, setSelectedId] = useState<string | null>(null);

  // 채번 관련 상태
  const [numberingRule, setNumberingRule] = useState<any>(null);
  const [numberingParts, setNumberingParts] = useState<{ value: string; isManual: boolean; separator: string }[]>([]);
  const [manualInputValue, setManualInputValue] = useState<string>("");
  const [isNumberingLoading, setIsNumberingLoading] = useState(false);
  const numberingRuleIdRef = useRef<string | null>(null);

  // 프리뷰 코드에서 각 파트별 표시값을 추출
  const parsePreviewIntoParts = (previewCode: string, rule: any) => {
    if (!previewCode || !rule?.parts) return [];
    const sorted = [...rule.parts].sort((a: any, b: any) => a.order - b.order);
    const globalSep = rule.separator || "";

    // 1단계: 각 파트의 "예상 값"과 "구분자" 산출 (sequence, category 제외)
    const partMeta = sorted.map((part: any, idx: number) => {
      const sep = idx < sorted.length - 1
        ? (part.separatorAfter ?? part.autoConfig?.separatorAfter ?? globalSep)
        : "";
      const config = part.autoConfig || {};
      if (part.generationMethod === "manual") return { known: false, marker: "____", sep, isManual: true, partType: part.partType };
      switch (part.partType) {
        case "text": return { known: true, value: config.textValue || "", sep, isManual: false, partType: "text" };
        case "number": return { known: true, value: String(config.numberValue || 1).padStart(config.numberLength || 3, "0"), sep, isManual: false, partType: "number" };
        case "date": {
          const now = new Date();
          const y = String(now.getFullYear()), m = String(now.getMonth() + 1).padStart(2, "0"), d = String(now.getDate()).padStart(2, "0");
          const fmt = config.dateFormat || "YYYYMMDD";
          const map: Record<string, string> = { YYYY: y, YY: y.slice(2), YYYYMM: y + m, YYMM: y.slice(2) + m, YYYYMMDD: y + m + d, YYMMDD: y.slice(2) + m + d };
          return { known: true, value: map[fmt] || y + m + d, sep, isManual: false, partType: "date" };
        }
        // sequence, category: 알 수 없으므로 프리뷰 코드에서 추출
        default: return { known: false, sep, isManual: false, partType: part.partType };
      }
    });

    // 2단계: 프리뷰 코드를 앞에서부터 소비하면서 각 파트의 실제 값을 추출
    let remaining = previewCode;
    const results: { value: string; isManual: boolean; separator: string }[] = [];

    for (let i = 0; i < partMeta.length; i++) {
      const meta = partMeta[i];
      const nextMeta = i < partMeta.length - 1 ? partMeta[i + 1] : null;

      if (meta.isManual) {
        // manual 파트: "____" 마커 찾아서 스킵
        const markerIdx = remaining.indexOf("____");
        if (markerIdx >= 0) {
          remaining = remaining.substring(markerIdx + 4);
          // 이 파트 뒤 구분자 소비
          if (meta.sep && remaining.startsWith(meta.sep)) {
            remaining = remaining.substring(meta.sep.length);
          }
        }
        results.push({ value: "", isManual: true, separator: meta.sep });
        continue;
      }

      if (meta.known) {
        // 알려진 값: 프리뷰 코드에서 해당 값을 소비
        const valIdx = remaining.indexOf(meta.value);
        if (valIdx >= 0) {
          remaining = remaining.substring(valIdx + meta.value.length);
          // 구분자 소비
          if (meta.sep && remaining.startsWith(meta.sep)) {
            remaining = remaining.substring(meta.sep.length);
          }
        }
        results.push({ value: meta.value, isManual: false, separator: meta.sep });
      } else {
        // 미지의 값 (sequence, category 등): 다음 파트의 값 또는 구분자 기반으로 경계 탐색
        let endIdx = remaining.length;

        if (meta.sep) {
          // 이 파트 뒤 구분자로 끝나는 지점 찾기
          // 단, 다음 파트의 시작을 기준으로 역방향 탐색
          if (nextMeta) {
            // 다음 파트가 known이면 그 값이 시작되는 위치 탐색
            if (nextMeta.known && nextMeta.value) {
              // 구분자 + 다음 값 패턴으로 찾기
              const pattern = meta.sep + nextMeta.value;
              const patIdx = remaining.indexOf(pattern);
              if (patIdx >= 0) endIdx = patIdx;
            } else if (nextMeta.isManual) {
              // 다음이 manual이면 구분자 + "____" 패턴
              const pattern = meta.sep + "____";
              const patIdx = remaining.indexOf(pattern);
              if (patIdx >= 0) endIdx = patIdx;
            } else {
              // 구분자로 분리
              const sepIdx = remaining.indexOf(meta.sep);
              if (sepIdx >= 0) endIdx = sepIdx;
            }
          }
        } else if (nextMeta) {
          // 구분자 없이 다음 파트의 알려진 값으로 경계 탐색
          if (nextMeta.known && nextMeta.value) {
            const valIdx = remaining.indexOf(nextMeta.value);
            if (valIdx >= 0) endIdx = valIdx;
          } else if (nextMeta.isManual) {
            const markerIdx = remaining.indexOf("____");
            if (markerIdx >= 0) endIdx = markerIdx;
          }
        }

        const extracted = remaining.substring(0, endIdx);
        remaining = remaining.substring(endIdx);
        // 구분자 소비
        if (meta.sep && remaining.startsWith(meta.sep)) {
          remaining = remaining.substring(meta.sep.length);
        }
        results.push({ value: extracted, isManual: false, separator: meta.sep });
      }
    }

    return results;
  };

  // 카테고리 옵션 로드
  useEffect(() => {
    const loadCategories = async () => {
      try {
        const optMap: Record<string, { code: string; label: string }[]> = {};
        const flatten = (vals: any[]): { code: string; label: string }[] => {
          const result: { code: string; label: string }[] = [];
          for (const v of vals) {
            result.push({ code: v.valueCode, label: v.valueLabel });
            if (v.children?.length) result.push(...flatten(v.children));
          }
          return result;
        };

        await Promise.all(
          CATEGORY_COLUMNS.map(async (colName) => {
            try {
              const res = await apiClient.get(`/table-categories/${TABLE_NAME}/${colName}/values`);
              if (res.data?.success && res.data.data?.length > 0) {
                optMap[colName] = flatten(res.data.data);
              }
            } catch { /* skip */ }
          })
        );
        setCategoryOptions(optMap);
      } catch (err) {
        console.error("카테고리 로드 실패:", err);
      }
    };
    loadCategories();
  }, []);

  // 데이터 조회
  const fetchItems = useCallback(async () => {
    setLoading(true);
    try {
      const filters = searchFilters.map((f) => ({ columnName: f.columnName, operator: f.operator, value: f.value }));
      const res = await apiClient.post(`/table-management/tables/${TABLE_NAME}/data`, {
        page: 1,
        size: 99999,
        dataFilter: filters.length > 0 ? { enabled: true, filters } : undefined,
        autoFilter: true,
      });

      const raw = res.data?.data?.data || res.data?.data?.rows || [];
      const resolve = (col: string, code: string) => {
        if (!code) return "";
        // 쉼표 구분 다중값 지원
        if (code.includes(",")) {
          return code.split(",").map((c) => {
            const trimmed = c.trim();
            if (!trimmed || trimmed === "s") return "";
            return categoryOptions[col]?.find((o) => o.code === trimmed)?.label || trimmed;
          }).filter(Boolean).join(", ");
        }
        return categoryOptions[col]?.find((o) => o.code === code)?.label || code;
      };
      setRawItems(raw);
      const data = raw.map((r: any) => {
        const converted = { ...r };
        for (const col of CATEGORY_COLUMNS) {
          if (converted[col]) converted[col] = resolve(col, converted[col]);
        }
        return converted;
      });
      setItems(data);
    } catch (err) {
      console.error("품목 조회 실패:", err);
      toast.error("품목 목록을 불러오는데 실패했어요.");
    } finally {
      setLoading(false);
    }
  }, [categoryOptions, searchFilters]);

  useEffect(() => {
    fetchItems();
  }, [fetchItems]);

  // 채번 미리보기 로드
  const loadNumberingPreview = async (currentFormData?: Record<string, any>, currentManualValue?: string) => {
    try {
      setIsNumberingLoading(true);

      // 규칙 조회 (캐싱)
      let rule = numberingRule;
      if (!rule) {
        const ruleRes = await apiClient.get(`/numbering-rules/by-column/${TABLE_NAME}/item_number`);
        rule = ruleRes.data?.data;
        if (rule) {
          setNumberingRule(rule);
          numberingRuleIdRef.current = rule.ruleId;
        }
      }

      if (!rule?.ruleId) return { code: "", parts: [] as { value: string; isManual: boolean; separator: string }[] };

      // preview 호출 (formData + manualInputValue 전달)
      const previewRes = await apiClient.post(`/numbering-rules/${rule.ruleId}/preview`, {
        formData: currentFormData || {},
        manualInputValue: currentManualValue || undefined,
      });

      const generatedCode = previewRes.data?.data?.generatedCode || "";
      // 파트별 표시값 추출
      const parts = parsePreviewIntoParts(generatedCode, rule);
      setNumberingParts(parts);
      return { code: generatedCode, parts };
    } catch { /* 채번 규칙 없으면 무시 */ }
    finally {
      setIsNumberingLoading(false);
    }
    return { code: "", parts: [] as { value: string; isManual: boolean; separator: string }[] };
  };

  // 파트 값으로부터 전체 코드 조합 (수동 입력값 포함)
  const buildCodeFromParts = (parts: { value: string; isManual: boolean; separator: string }[], manualVal: string) => {
    return parts.map((p, idx) => {
      const val = p.isManual ? manualVal : p.value;
      const sep = idx < parts.length - 1 ? p.separator : "";
      return val + sep;
    }).join("");
  };

  // 등록 모달 열기
  const openRegisterModal = async () => {
    setFormData({});
    setManualInputValue("");
    setNumberingParts([]);
    setIsEditMode(false);
    setEditId(null);
    setIsModalOpen(true);
    // 채번 미리보기
    const result = await loadNumberingPreview({});
    if (result.code) {
      const hasManual = result.parts.some(p => p.isManual);
      const displayCode = hasManual ? buildCodeFromParts(result.parts, "") : result.code;
      setFormData(prev => ({ ...prev, item_number: displayCode }));
    }
  };

  // 수정 모달 열기
  const openEditModal = (item: any) => {
    const raw = rawItems.find((r) => r.id === item.id) || item;
    setFormData({ ...raw });
    setManualInputValue("");
    setNumberingParts([]);
    setIsEditMode(true);
    setEditId(item.id);
    setIsModalOpen(true);
  };

  // 복사 모달 열기
  const openCopyModal = async (item: any) => {
    const raw = rawItems.find((r) => r.id === item.id) || item;
    const { id, item_number, created_date, updated_date, writer, ...rest } = raw;
    setFormData(rest);
    setManualInputValue("");
    setNumberingParts([]);
    setIsEditMode(false);
    setEditId(null);
    setIsModalOpen(true);
    // 복사된 formData 기반으로 preview
    const result = await loadNumberingPreview(rest);
    if (result.code) {
      const hasManual = result.parts.some(p => p.isManual);
      const displayCode = hasManual ? buildCodeFromParts(result.parts, "") : result.code;
      setFormData(prev => ({ ...prev, item_number: displayCode }));
    }
  };

  // 카테고리 변경 시 채번 preview 재호출
  useEffect(() => {
    if (isEditMode || !isModalOpen || !numberingRuleIdRef.current) return;

    const hasCategoryPart = numberingRule?.parts?.some(
      (p: any) => p.partType === "category" && p.generationMethod === "auto"
    );
    if (!hasCategoryPart) return;

    const timer = setTimeout(async () => {
      const result = await loadNumberingPreview(formData, manualInputValue);
      if (result.parts.length > 0) {
        setFormData(prev => ({ ...prev, item_number: buildCodeFromParts(result.parts, manualInputValue) }));
      }
    }, 300);

    return () => clearTimeout(timer);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [...CATEGORY_COLUMNS.map(col => formData[col])]);

  // 수동 입력값 변경 시 preview 갱신 (순번 재계산)
  useEffect(() => {
    if (isEditMode || !isModalOpen || !numberingRuleIdRef.current) return;
    if (!numberingParts.some(p => p.isManual)) return;

    const timer = setTimeout(async () => {
      const result = await loadNumberingPreview(formData, manualInputValue);
      if (result.parts.length > 0) {
        setFormData(prev => ({ ...prev, item_number: buildCodeFromParts(result.parts, manualInputValue) }));
      }
    }, 500);

    return () => clearTimeout(timer);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [manualInputValue]);

  // 저장
  const handleSave = async () => {
    if (!formData.item_name) {
      toast.error("품명은 필수 입력이에요.");
      return;
    }

    setSaving(true);
    try {
      if (isEditMode && editId) {
        // 수정: item_number는 변경하지 않음
        const { id, created_date, updated_date, writer, company_code, ...updateFields } = formData;
        await apiClient.put(`/table-management/tables/${TABLE_NAME}/edit`, {
          originalData: { id: editId },
          updatedData: updateFields,
        });
        toast.success("수정되었어요.");
      } else {
        // 신규 등록: allocateCode 호출하여 실제 순번 확보
        let finalItemNumber = formData.item_number || "";

        if (numberingRuleIdRef.current) {
          try {
            const hasManual = numberingParts.some(p => p.isManual);
            const userInputCode = hasManual && manualInputValue
              ? manualInputValue
              : undefined;

            const allocRes = await apiClient.post(
              `/numbering-rules/${numberingRuleIdRef.current}/allocate`,
              { formData, userInputCode }
            );

            if (allocRes.data?.success && allocRes.data?.data?.generatedCode) {
              finalItemNumber = allocRes.data.data.generatedCode;
            }
          } catch (err) {
            console.error("채번 할당 실패:", err);
            toast.error("품목코드 할당에 실패했어요. 다시 시도해주세요.");
            setSaving(false);
            return;
          }
        }

        const { id, created_date, updated_date, ...insertFields } = formData;
        await apiClient.post(`/table-management/tables/${TABLE_NAME}/add`, {
          id: crypto.randomUUID(),
          ...insertFields,
          item_number: finalItemNumber,
        });
        toast.success("등록되었어요.");
      }
      setIsModalOpen(false);
      fetchItems();
    } catch (err: any) {
      console.error("저장 실패:", err);
      toast.error(err.response?.data?.message || "저장에 실패했어요.");
    } finally {
      setSaving(false);
    }
  };

  // 삭제
  const handleDelete = async () => {
    if (!selectedId) {
      toast.error("삭제할 품목을 선택해 주세요.");
      return;
    }
    if (!confirm("선택한 품목을 삭제할까요?")) return;

    try {
      await apiClient.delete(`/table-management/tables/${TABLE_NAME}/delete`, {
        data: [{ id: selectedId }],
      });
      toast.success("삭제되었어요.");
      setSelectedId(null);
      fetchItems();
    } catch (err) {
      console.error("삭제 실패:", err);
      toast.error("삭제에 실패했어요.");
    }
  };

  // 엑셀 다운로드
  const handleExcelDownload = async () => {
    if (items.length === 0) {
      toast.error("다운로드할 데이터가 없어요.");
      return;
    }
    const exportData = items.map((item) => {
      const row: Record<string, any> = {};
      for (const col of GRID_COLUMNS) {
        row[col.label] = item[col.key] || "";
      }
      return row;
    });
    await exportToExcel(exportData, "품목정보.xlsx", "품목정보");
    toast.success("엑셀 다운로드 완료");
  };

  return (
    <div className="flex h-full flex-col gap-0">
      {/* 검색 필터 바 */}
      <DynamicSearchFilter
        tableName={TABLE_NAME}
        filterId="c16-item-info"
        onFilterChange={setSearchFilters}
        externalFilterConfig={ts.filterConfig}
      />

      {/* 액션 바 */}
      <div className="flex items-center justify-between px-4 py-2.5 border-b">
        <div className="flex items-center gap-2">
          <span className="text-sm font-semibold">품목 관리</span>
          <Badge variant="secondary" className="font-mono text-xs">{items.length}건</Badge>
        </div>
        <div className="flex items-center gap-2">
          <Button variant="outline" size="sm" onClick={() => setExcelUploadOpen(true)}>
            <FileSpreadsheet className="w-4 h-4 mr-1.5" /> 엑셀 업로드
          </Button>
          <Button variant="outline" size="sm" onClick={handleExcelDownload}>
            <Download className="w-4 h-4 mr-1.5" /> 엑셀 다운로드
          </Button>
          <div className="mx-1 h-5 w-px bg-border" />
          <Button size="sm" onClick={openRegisterModal}>
            <Plus className="w-4 h-4 mr-1.5" /> 품목 등록
          </Button>
          <Button
            variant="outline"
            size="sm"
            disabled={!selectedId}
            onClick={() => {
              const item = items.find((i) => i.id === selectedId);
              if (item) openCopyModal(item);
            }}
          >
            <Copy className="w-4 h-4 mr-1.5" /> 복사
          </Button>
          <Button
            variant="outline"
            size="sm"
            disabled={!selectedId}
            onClick={() => {
              const item = items.find((i) => i.id === selectedId);
              if (item) openEditModal(item);
            }}
          >
            <Pencil className="w-4 h-4 mr-1.5" /> 수정
          </Button>
          <Button variant="destructive" size="sm" disabled={!selectedId} onClick={handleDelete}>
            <Trash2 className="w-4 h-4 mr-1.5" /> 삭제
          </Button>
          <Button variant="ghost" size="sm" onClick={() => ts.setOpen(true)} title="테이블 설정">
            <Settings2 className="h-4 w-4" />
          </Button>
        </div>
      </div>

      {/* 메인 테이블 */}
      <EDataTable
        columns={ts.visibleColumns.map((col): EDataTableColumn => ({
          key: col.key,
          label: col.label,
          align: (col as any).type === "image" ? "center" : col.align as "left" | "center" | "right" | undefined,
          formatNumber: (col as any).formatNumber,
          width: (col as any).type === "image" ? "w-[50px]" : undefined,
          render: (col as any).type === "image" ? (val: any) => (
            val ? (
              <img src={String(val).startsWith("http") || String(val).startsWith("/") ? val : `/api/files/preview/${val}`} alt="" className="h-8 w-8 rounded object-cover border border-border mx-auto" onError={(e) => { (e.target as HTMLImageElement).style.display = "none"; }} />
            ) : <div className="h-8 w-8 rounded bg-muted mx-auto" />
          ) : undefined,
        }))}
        data={ts.groupData(items)}
        loading={loading}
        emptyMessage="등록된 품목이 없어요"
        selectedId={selectedId}
        onSelect={(id) => setSelectedId(id)}
        onRowDoubleClick={(row) => openEditModal(row)}
        showRowNumber
        draggableColumns={false}
      />

      {/* 등록/수정 모달 */}
      <Dialog open={isModalOpen} onOpenChange={(open) => {
        setIsModalOpen(open);
        if (!open) {
          setNumberingParts([]);
          setManualInputValue("");
          setNumberingRule(null);
          numberingRuleIdRef.current = null;
        }
      }}>
        <DialogContent className="sm:max-w-[600px] w-[95vw] max-h-[90vh] flex flex-col p-0 overflow-hidden">
          <DialogHeader className="shrink-0 px-6 pt-5 pb-3 border-b">
            <DialogTitle>{isEditMode ? "품목 수정" : "품목 등록"}</DialogTitle>
            <DialogDescription>
              {isEditMode ? "품목 정보를 수정해요." : "새로운 품목을 등록해요."}
            </DialogDescription>
          </DialogHeader>

          <div className="flex-1 overflow-y-auto">
            <div className="grid grid-cols-2 gap-4 p-6">
              {FORM_FIELDS.map((field) => (
                <div
                  key={field.key}
                  className={cn("space-y-1.5", (field.type === "textarea" || field.type === "image") && "col-span-2")}
                >
                  <Label className="text-xs font-semibold uppercase tracking-wide text-muted-foreground">
                    {field.label}
                    {field.required && <span className="text-destructive ml-1">*</span>}
                  </Label>
                  {field.type === "image" ? (
                    <ImageUpload
                      value={formData[field.key] || ""}
                      onChange={(v) => setFormData((prev) => ({ ...prev, [field.key]: v }))}
                      tableName={TABLE_NAME}
                      recordId={formData.id || ""}
                      columnName={field.key}
                      height="h-32"
                    />
                  ) : field.type === "multi-category" ? (
                    <MultiCategoryCombobox
                      options={categoryOptions[field.key] || []}
                      value={formData[field.key] || ""}
                      onChange={(v) => setFormData((prev) => ({ ...prev, [field.key]: v }))}
                      placeholder={`${field.label} 선택`}
                    />
                  ) : field.type === "category" ? (
                    <CategoryCombobox
                      options={categoryOptions[field.key] || []}
                      value={formData[field.key] || ""}
                      onChange={(v) => setFormData((prev) => ({ ...prev, [field.key]: v }))}
                      placeholder={`${field.label} 선택`}
                    />
                  ) : field.type === "textarea" ? (
                    <Textarea
                      value={formData[field.key] || ""}
                      onChange={(e) => setFormData((prev) => ({ ...prev, [field.key]: e.target.value }))}
                      placeholder={field.label}
                      rows={3}
                    />
                  ) : field.type === "numbering" ? (
                    // 채번 세그먼트 UI
                    isEditMode ? (
                      <Input
                        value={formData[field.key] || ""}
                        disabled
                        className="h-9 bg-muted"
                      />
                    ) : isNumberingLoading && numberingParts.length === 0 ? (
                      <div className="flex h-9 items-center gap-2 rounded-md border px-3">
                        <Loader2 className="h-3.5 w-3.5 animate-spin text-muted-foreground" />
                        <span className="text-sm text-muted-foreground">생성 중...</span>
                      </div>
                    ) : numberingParts.some(p => p.isManual) ? (
                      // 파트별 세그먼트 렌더링 (수동 입력 파트 있음)
                      <div className="flex h-9 items-center rounded-md border border-input">
                        {numberingParts.map((part, idx) => {
                          const isFirst = idx === 0;
                          const isLast = idx === numberingParts.length - 1;
                          if (part.isManual) {
                            return (
                              <React.Fragment key={idx}>
                                <input
                                  type="text"
                                  value={manualInputValue}
                                  onChange={(e) => {
                                    const val = e.target.value;
                                    setManualInputValue(val);
                                    setFormData(prev => ({
                                      ...prev,
                                      item_number: buildCodeFromParts(numberingParts, val),
                                    }));
                                  }}
                                  placeholder="입력"
                                  className="h-full min-w-[60px] flex-1 border-0 bg-transparent px-2 text-sm outline-none"
                                />
                                {part.separator && !isLast && (
                                  <span className="text-muted-foreground text-sm">{part.separator}</span>
                                )}
                              </React.Fragment>
                            );
                          }
                          // auto 파트: 회색 배경 읽기전용
                          return (
                            <React.Fragment key={idx}>
                              <span className={cn(
                                "flex h-full items-center bg-muted px-2 text-sm text-muted-foreground whitespace-nowrap",
                                isFirst && "rounded-l-[5px]",
                                isLast && "rounded-r-[5px]",
                              )}>
                                {part.value}
                              </span>
                              {part.separator && !isLast && (
                                <span className="text-muted-foreground text-sm">{part.separator}</span>
                              )}
                            </React.Fragment>
                          );
                        })}
                      </div>
                    ) : (
                      // 전체 auto: 읽기전용 표시
                      <Input
                        value={formData[field.key] || ""}
                        disabled
                        placeholder="자동 채번"
                        className="h-9 bg-muted"
                      />
                    )
                  ) : ["selling_price", "standard_price"].includes(field.key) ? (
                    <Input
                      value={formData[field.key] ? Number(String(formData[field.key]).replace(/,/g, "")).toLocaleString() : ""}
                      onChange={(e) => {
                        const raw = e.target.value.replace(/[^\d.-]/g, "");
                        setFormData((prev) => ({ ...prev, [field.key]: raw }));
                      }}
                      placeholder={field.placeholder || field.label}
                      className="h-9 text-right"
                    />
                  ) : (
                    <Input
                      value={formData[field.key] || ""}
                      onChange={(e) => setFormData((prev) => ({ ...prev, [field.key]: e.target.value }))}
                      placeholder={field.placeholder || field.label}
                      className="h-9"
                    />
                  )}
                </div>
              ))}
            </div>
          </div>

          <DialogFooter className="shrink-0 border-t px-6 py-3">
            <Button variant="outline" onClick={() => setIsModalOpen(false)}>취소</Button>
            <Button onClick={handleSave} disabled={saving}>
              {saving
                ? <Loader2 className="w-4 h-4 mr-1.5 animate-spin" />
                : <Save className="w-4 h-4 mr-1.5" />
              }
              저장
            </Button>
          </DialogFooter>
        </DialogContent>
      </Dialog>

      {/* 테이블 설정 모달 */}
      <TableSettingsModal
        open={ts.open}
        onOpenChange={ts.setOpen}
        tableName={ts.tableName}
        settingsId={ts.settingsId}
        defaultVisibleKeys={ts.defaultVisibleKeys}
        onSave={ts.applySettings}
      />

      {/* 엑셀 업로드 모달 */}
      <ExcelUploadModal
        open={excelUploadOpen}
        onOpenChange={setExcelUploadOpen}
        tableName={TABLE_NAME}
        userId={user?.userId}
        onSuccess={() => {
          fetchItems();
        }}
      />
    </div>
  );
}
