"use client";

// 생산관리 > M-BOM 관리 — 단건 상세 + 트리 (read-only / 편집).
//
// 운영판 통합:
//   wace mBomHeaderPopup.jsp (헤더 메타)
// + wace mBomPopupLeft.jsp   (좌측 트리 — read-only/편집)
//
// 4분기 (운영판 mBomPopupLeft.do):
//   SAVED         저장된 mbom_header.status='Y' 의 트리 (생산정보 포함, 편집 시 UPDATE)
//   ASSIGNED_EBOM source_bom_type='EBOM' + source_ebom_objid → bom_part_qty 트리 (편집 시 신규 CREATE)
//   ASSIGNED_MBOM source_bom_type='MBOM' + source_mbom_objid → mbom_detail 구조만
//   TEMPLATE      Machine 이외 + 동일 part_no 의 mbom_header 템플릿
//   NONE          빈 트리
//
// PR-B1 — 셀 인라인 편집 + 저장 (운영 saveMbom.do 1:1).
// 행 추가/삭제(mBomCenterBtnPopup) / BOM 복사 / 구매리스트 / 변경이력 — PR-B2~ 분리.

import React, { useEffect, useMemo, useState } from "react";
import {
  Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter,
} from "@/components/ui/dialog";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Loader2, Folder, Pencil, Save, X, History, Plus, Trash2, Link as LinkIcon } from "lucide-react";
import { toast } from "sonner";
import { cn } from "@/lib/utils";
import { mbomApi, MbomDetail, MbomTreeResponse, MbomBomDataType, MbomTreeRow, MbomSaveRow } from "@/lib/api/mbom";
import { MbomHistoryDialog } from "./MbomHistoryDialog";
import { MbomAddPartDialog, PickedPart } from "./MbomAddPartDialog";
import { MbomAssignDialog } from "./MbomAssignDialog";

interface Props {
  open: boolean;
  onOpenChange: (open: boolean) => void;
  projectObjid: string | null;
  onSaved?: () => void;
}

const BOM_DATA_TYPE_LABEL: Record<MbomBomDataType, { text: string; color: string }> = {
  SAVED:         { text: "저장된 M-BOM",   color: "bg-emerald-600" },
  ASSIGNED_EBOM: { text: "할당된 E-BOM",   color: "bg-sky-600" },
  ASSIGNED_MBOM: { text: "할당된 M-BOM",   color: "bg-indigo-600" },
  TEMPLATE:      { text: "M-BOM 템플릿",   color: "bg-amber-600" },
  NONE:          { text: "BOM 없음",       color: "bg-slate-500" },
};

// 편집 모드에서 사용자가 직접 수정 가능한 필드 (운영판 wace fieldMapping 기반)
type EditableField =
  | "qty" | "supply_type" | "make_or_buy"
  | "raw_material_no" | "raw_material" | "raw_material_size"
  | "required_qty" | "order_qty" | "production_qty"
  | "processing_vendor" | "processing_deadline" | "grinding_deadline"
  | "remark";

export function MbomDetailDialog({ open, onOpenChange, projectObjid, onSaved }: Props) {
  const [detail, setDetail] = useState<MbomDetail | null>(null);
  const [tree, setTree] = useState<MbomTreeResponse | null>(null);
  const [loading, setLoading] = useState(false);
  const [editMode, setEditMode] = useState(false);
  const [saving, setSaving] = useState(false);
  const [editableRows, setEditableRows] = useState<MbomTreeRow[]>([]);
  const [dirty, setDirty] = useState(false);
  const [historyOpen, setHistoryOpen] = useState(false);
  const [addPartOpen, setAddPartOpen] = useState(false);
  const [assignOpen, setAssignOpen] = useState(false);
  const [selectedChildObjids, setSelectedChildObjids] = useState<Set<string>>(new Set());

  const loadTree = (objid: string) => {
    setLoading(true);
    return Promise.all([
      mbomApi.getDetail(objid),
      mbomApi.getTree(objid),
    ])
      .then(([d, t]) => {
        setDetail(d);
        setTree(t);
        setEditableRows((t?.rows ?? []).map(r => ({ ...r })));
        setDirty(false);
      })
      .catch((e: any) => {
        toast.error(e?.response?.data?.message ?? e?.message ?? "M-BOM 조회 실패");
      })
      .finally(() => setLoading(false));
  };

  useEffect(() => {
    if (!open || !projectObjid) {
      setDetail(null); setTree(null); setEditableRows([]);
      setEditMode(false); setDirty(false);
      setSelectedChildObjids(new Set());
      return;
    }
    void loadTree(projectObjid);
  }, [open, projectObjid]);

  // 편집 모드 off 시 선택 해제
  useEffect(() => {
    if (!editMode) setSelectedChildObjids(new Set());
  }, [editMode]);

  const maxLevel = Math.max(1, tree?.max_level ?? 1);
  const rows = editMode ? editableRows : (tree?.rows ?? []);
  const bomDataType: MbomBomDataType = tree?.bom_data_type ?? "NONE";
  const meta = BOM_DATA_TYPE_LABEL[bomDataType];
  const canEdit = bomDataType !== "NONE";

  const levelHeaders = useMemo(() => {
    const h: number[] = [];
    for (let i = 1; i <= maxLevel; i++) h.push(i);
    return h;
  }, [maxLevel]);

  const updateRow = (idx: number, field: EditableField, value: any) => {
    setEditableRows(prev => {
      const next = [...prev];
      next[idx] = { ...next[idx], [field]: value };
      return next;
    });
    setDirty(true);
  };

  // 체크박스 토글
  const toggleSelect = (childObjid: string | null | undefined) => {
    if (!childObjid) return;
    setSelectedChildObjids(prev => {
      const next = new Set(prev);
      if (next.has(childObjid)) next.delete(childObjid);
      else next.add(childObjid);
      return next;
    });
  };
  const toggleSelectAll = () => {
    if (selectedChildObjids.size === editableRows.length) setSelectedChildObjids(new Set());
    else setSelectedChildObjids(new Set(editableRows.map(r => String(r.child_objid ?? r.objid))));
  };

  // BFS — 주어진 child_objid 의 모든 하위 노드 child_objid 집합
  const getDescendants = (rootChildObjid: string): Set<string> => {
    const result = new Set<string>();
    const queue = [rootChildObjid];
    while (queue.length) {
      const parent = queue.shift()!;
      for (const r of editableRows) {
        const c = String(r.child_objid ?? "");
        if (!c) continue;
        if (r.parent_objid === parent && !result.has(c)) {
          result.add(c);
          queue.push(c);
        }
      }
    }
    return result;
  };

  // 행 추가 — PART 검색 다이얼로그에서 반환된 parts 를 editableRows 에 append.
  // 부모 = 선택된 행 1개 (없으면 root, 2개 이상이면 첫 번째).
  const handleAddParts = (parts: PickedPart[]) => {
    const firstSelected = selectedChildObjids.size > 0 ? Array.from(selectedChildObjids)[0] : null;
    const parentRow = firstSelected ? editableRows.find(r => String(r.child_objid) === firstSelected) : null;
    const parentChildObjid = parentRow ? String(parentRow.child_objid) : null;
    const parentLevel = parentRow ? Number(parentRow.level ?? 1) : 0;
    const base = Date.now();
    const newRows: MbomTreeRow[] = parts.map((p, i) => ({
      objid: "" as any,                               // 빈값 → 백엔드 createObjId
      parent_objid: parentChildObjid,
      child_objid: `temp-${base}-${i}` as any,        // 클라이언트 임시 ID (백엔드 그대로 저장)
      seq: 999,
      level: parentLevel + 1,
      part_objid: p.objid as any,
      part_no: p.part_no,
      part_name: p.part_name,
      qty: 1,
      item_qty: 1,
      unit: p.unit,
      unit_title: p.unit,
      supply_type: null, make_or_buy: null,
      raw_material_no: null, raw_material: null, raw_material_size: null,
      required_qty: null, order_qty: null, production_qty: null,
      processing_vendor: null, processing_deadline: null, grinding_deadline: null,
      cu01_cnt: 0, cu02_cnt: 0, cu03_cnt: 0,
      remark: null,
      status: "ACTIVE",
      sub_part_cnt: 0,
      processing_vendor_name: null,
    } as any));
    setEditableRows(prev => [...prev, ...newRows]);
    setDirty(true);
    toast.success(`${parts.length}개 행 추가${parentChildObjid ? ` (부모: ${parentRow?.part_no ?? ""})` : " (root)"}`);
  };

  // 선택 삭제 — cascade. 선택된 child_objid + 하위 트리 전부 제거.
  const handleDeleteSelected = () => {
    if (selectedChildObjids.size === 0) return;
    const allToRemove = new Set<string>();
    for (const sel of selectedChildObjids) {
      allToRemove.add(sel);
      for (const d of getDescendants(sel)) allToRemove.add(d);
    }
    if (!window.confirm(`${allToRemove.size}개 행을 삭제합니다 (선택 ${selectedChildObjids.size} + 하위 ${allToRemove.size - selectedChildObjids.size}). 계속할까요?`)) return;
    setEditableRows(prev => prev.filter(r => !allToRemove.has(String(r.child_objid ?? r.objid))));
    setSelectedChildObjids(new Set());
    setDirty(true);
  };

  const handleEditToggle = () => {
    if (editMode && dirty) {
      if (!window.confirm("편집 중인 변경사항이 사라집니다. 취소하시겠습니까?")) return;
      setEditableRows((tree?.rows ?? []).map(r => ({ ...r })));
      setDirty(false);
    }
    setEditMode(!editMode);
  };

  const handleSave = async () => {
    if (!projectObjid) return;
    if (rows.length === 0) {
      toast.error("저장할 트리가 비어있습니다");
      return;
    }
    setSaving(true);
    try {
      const isUpdate = bomDataType === "SAVED";
      const payload = {
        project_obj_id: projectObjid,
        is_update: isUpdate,
        rows: editableRows.map<MbomSaveRow>(r => ({
          objid: r.objid,
          parent_objid: r.parent_objid,
          child_objid: r.child_objid,
          seq: r.seq,
          level: r.level,
          part_objid: r.part_objid,
          part_no: r.part_no,
          part_name: r.part_name,
          qty: r.qty,
          item_qty: r.item_qty,
          unit: r.unit,
          supply_type: r.supply_type,
          make_or_buy: r.make_or_buy,
          raw_material_no: r.raw_material_no,
          raw_material_spec: r.raw_material_spec,
          raw_material: r.raw_material,
          raw_material_size: (r as any).raw_material_size ?? r.size,
          processing_vendor: r.processing_vendor,
          processing_deadline: r.processing_deadline,
          grinding_deadline: r.grinding_deadline,
          required_qty: r.required_qty,
          order_qty: r.order_qty,
          production_qty: r.production_qty,
          remark: r.remark,
        })),
      };
      const result = await mbomApi.save(payload);
      toast.success(`M-BOM ${result.mode === "CREATE" ? "생성" : "수정"} 완료 (${result.mbom_no})`);
      setEditMode(false);
      setDirty(false);
      await loadTree(projectObjid);
      onSaved?.();
    } catch (e: any) {
      toast.error(e?.response?.data?.message ?? e?.message ?? "저장 실패");
    } finally {
      setSaving(false);
    }
  };

  return (
    <Dialog open={open} onOpenChange={(v) => {
      if (!v && editMode && dirty) {
        if (!window.confirm("저장하지 않은 변경사항이 있습니다. 닫으시겠습니까?")) return;
      }
      onOpenChange(v);
    }}>
      <DialogContent className="max-w-[1600px] w-[97vw] max-h-[92vh] flex flex-col p-0 overflow-hidden">
        <DialogHeader className="bg-blue-600 px-4 py-3">
          <DialogTitle className="text-white flex items-center gap-3">
            <span>M-BOM 관리 — {editMode ? "본 편집" : "단건 상세"}</span>
            <span className={cn("rounded px-2 py-0.5 text-xs font-semibold", meta.color)}>{meta.text}</span>
            {editMode && dirty && (
              <span className="rounded bg-orange-500 px-2 py-0.5 text-xs font-semibold">변경됨</span>
            )}
          </DialogTitle>
        </DialogHeader>

        {/* 헤더 메타 (운영판 mBomHeaderPopup.jsp 1:1) */}
        {detail && (
          <div className="grid grid-cols-4 gap-x-6 gap-y-1.5 border-b px-4 py-3 text-xs">
            <MetaRow label="프로젝트번호" value={detail.project_no} />
            <MetaRow label="주문유형"     value={detail.category_name} />
            <MetaRow label="제품구분"     value={detail.product_name} />
            <MetaRow label="국내/해외"    value={detail.area_name} />
            <MetaRow label="고객사"       value={detail.customer_name} />
            <MetaRow label="유/무상"      value={paidLabel(detail.paid_type)} />
            <MetaRow label="품번"         value={detail.part_no} />
            <MetaRow label="품명"         value={detail.part_name} />
            <MetaRow label="수주수량"     value={fmtNum(detail.quantity)} numeric />
            <MetaRow label="총생산수량"   value={fmtNum(detail.total_prod_qty)} numeric />
            <MetaRow label="요청납기"     value={detail.req_del_date} />
            <MetaRow label="접수일"       value={detail.receipt_date} />
            <MetaRow label="M-BOM 품번"   value={detail.mbom_part_no || "—"} />
            <MetaRow label="M-BOM 저장일" value={detail.mbom_regdate || "—"} />
          </div>
        )}

        <div className="flex items-center justify-between border-b px-4 py-2">
          <div className="text-xs text-muted-foreground">
            총 {rows.length.toLocaleString()}행 · MAX_LEVEL = {maxLevel}
            {tree?.bom_report_objid && (
              <span className="ml-2 text-muted-foreground/70">BOM_OBJID = {tree.bom_report_objid}</span>
            )}
          </div>
          <div className="flex items-center gap-2">
            {!editMode && (
              <Button size="sm" variant="outline" onClick={() => setHistoryOpen(true)} disabled={loading || !projectObjid}>
                <History className="w-3 h-3 mr-1" /> 변경이력
              </Button>
            )}
            {!editMode && (
              <Button size="sm" variant="outline" onClick={() => setAssignOpen(true)} disabled={loading || !projectObjid}>
                <LinkIcon className="w-3 h-3 mr-1" />
                {bomDataType === "NONE" ? "BOM 할당" : "할당 변경"}
              </Button>
            )}
            {canEdit && !editMode && (
              <Button size="sm" variant="outline" onClick={handleEditToggle} disabled={loading}>
                <Pencil className="w-3 h-3 mr-1" /> 본 편집
              </Button>
            )}
            {editMode && (
              <>
                <Button size="sm" variant="outline" onClick={() => setAddPartOpen(true)} disabled={saving}>
                  <Plus className="w-3 h-3 mr-1" /> 행 추가
                </Button>
                <Button size="sm" variant="outline" onClick={handleDeleteSelected} disabled={saving || selectedChildObjids.size === 0}>
                  <Trash2 className="w-3 h-3 mr-1" /> 선택 삭제 {selectedChildObjids.size > 0 && `(${selectedChildObjids.size})`}
                </Button>
                <Button size="sm" variant="outline" onClick={handleEditToggle} disabled={saving}>
                  <X className="w-3 h-3 mr-1" /> 취소
                </Button>
                <Button size="sm" onClick={handleSave} disabled={!dirty || saving}>
                  {saving ? <Loader2 className="w-3 h-3 mr-1 animate-spin" /> : <Save className="w-3 h-3 mr-1" />}
                  저장 ({bomDataType === "SAVED" ? "수정" : "신규"})
                </Button>
              </>
            )}
          </div>
        </div>

        <div className="flex-1 min-h-0 overflow-auto">
          {loading ? (
            <div className="flex h-64 items-center justify-center">
              <Loader2 className="h-6 w-6 animate-spin" />
            </div>
          ) : (
            <table className="text-xs border-collapse w-max min-w-full">
              <thead className="bg-yellow-100 dark:bg-yellow-900/30 sticky top-0">
                <tr>
                  {editMode && (
                    <th className="border px-1 py-1 w-[32px] text-center">
                      <input type="checkbox"
                        checked={editableRows.length > 0 && selectedChildObjids.size === editableRows.length}
                        onChange={toggleSelectAll} />
                    </th>
                  )}
                  {levelHeaders.map((i) => (
                    <th key={`l${i}`} className="border px-2 py-1 w-[36px] text-center font-bold">{i}</th>
                  ))}
                  <th className="border px-2 py-1 min-w-[150px] text-left">품번</th>
                  <th className="border px-2 py-1 min-w-[180px] text-left">품명</th>
                  <th className="border px-2 py-1 min-w-[60px]  text-right">수량</th>
                  <th className="border px-2 py-1 min-w-[70px]  text-right">항목수량</th>
                  <th className="border px-2 py-1 min-w-[60px]  text-center">단위</th>
                  <th className="border px-2 py-1 min-w-[70px]  text-center">자/사급</th>
                  <th className="border px-2 py-1 min-w-[80px]  text-center">Make/Buy</th>
                  <th className="border px-2 py-1 min-w-[120px] text-left">소재품번</th>
                  <th className="border px-2 py-1 min-w-[120px] text-left">소재</th>
                  <th className="border px-2 py-1 min-w-[100px] text-left">규격</th>
                  <th className="border px-2 py-1 min-w-[80px]  text-right">필요수량</th>
                  <th className="border px-2 py-1 min-w-[80px]  text-right">주문수량</th>
                  <th className="border px-2 py-1 min-w-[80px]  text-right">생산수량</th>
                  <th className="border px-2 py-1 min-w-[110px] text-left">가공업체</th>
                  <th className="border px-2 py-1 min-w-[100px] text-center">가공납기</th>
                  <th className="border px-2 py-1 min-w-[100px] text-center">연삭납기</th>
                  <th className="border px-2 py-1 min-w-[40px]  text-center">3D</th>
                  <th className="border px-2 py-1 min-w-[40px]  text-center">2D</th>
                  <th className="border px-2 py-1 min-w-[40px]  text-center">PDF</th>
                  <th className="border px-2 py-1 min-w-[120px] text-left">비고</th>
                </tr>
              </thead>
              <tbody>
                {rows.length === 0 && (
                  <tr>
                    <td colSpan={levelHeaders.length + 19 + (editMode ? 1 : 0)} className="py-8 text-center text-muted-foreground">
                      {bomDataType === "NONE" ? "표시할 BOM이 없습니다." : "트리가 비어있습니다."}
                    </td>
                  </tr>
                )}
                {rows.map((r, idx) => {
                  const lv = Number(r.level ?? 1);
                  const cid = String(r.child_objid ?? r.objid ?? "");
                  const isSelected = editMode && cid && selectedChildObjids.has(cid);
                  const isNew = editMode && String(cid).startsWith("temp-");
                  return (
                    <tr key={`${r.objid}_${cid}_${idx}`} className={cn("hover:bg-muted/30",
                      isSelected && "bg-blue-50 dark:bg-blue-950/30",
                      isNew && "bg-emerald-50 dark:bg-emerald-950/20")}>
                      {editMode && (
                        <td className="border px-1 py-0.5 text-center">
                          <input type="checkbox"
                            checked={!!isSelected}
                            onChange={() => toggleSelect(cid)} />
                        </td>
                      )}
                      {levelHeaders.map((i) => (
                        <td key={`lc${i}`} className={cn("border px-1 py-0.5 text-center", i === lv && "font-bold")}>
                          {i === lv ? "*" : ""}
                        </td>
                      ))}
                      <td className="border px-2 py-0.5 whitespace-nowrap">{r.part_no}</td>
                      <td className="border px-2 py-0.5">{r.part_name}</td>
                      <EditableNumCell editable={editMode} value={r.qty} onChange={(v) => updateRow(idx, "qty", v)} />
                      <td className="border px-2 py-0.5 text-right">{fmtNum(r.item_qty)}</td>
                      <td className="border px-2 py-0.5 text-center">{r.unit_title ?? r.unit ?? ""}</td>
                      <EditableSelectCell editable={editMode} value={r.supply_type} options={SUPPLY_TYPE_OPTIONS}
                        onChange={(v) => updateRow(idx, "supply_type", v)} />
                      <EditableSelectCell editable={editMode} value={r.make_or_buy} options={MAKE_OR_BUY_OPTIONS}
                        onChange={(v) => updateRow(idx, "make_or_buy", v)} />
                      <EditableTextCell editable={editMode} value={r.raw_material_no}
                        onChange={(v) => updateRow(idx, "raw_material_no", v)} />
                      <EditableTextCell editable={editMode} value={r.raw_material}
                        onChange={(v) => updateRow(idx, "raw_material", v)} />
                      <EditableTextCell editable={editMode} value={(r as any).raw_material_size ?? r.size}
                        onChange={(v) => updateRow(idx, "raw_material_size", v)} />
                      <EditableNumCell editable={editMode} value={r.required_qty}
                        onChange={(v) => updateRow(idx, "required_qty", v)} />
                      <EditableNumCell editable={editMode} value={r.order_qty}
                        onChange={(v) => updateRow(idx, "order_qty", v)} />
                      <EditableNumCell editable={editMode} value={r.production_qty}
                        onChange={(v) => updateRow(idx, "production_qty", v)} />
                      <EditableTextCell editable={editMode} value={r.processing_vendor_name ?? r.processing_vendor}
                        onChange={(v) => updateRow(idx, "processing_vendor", v)} />
                      <EditableDateCell editable={editMode} value={r.processing_deadline}
                        onChange={(v) => updateRow(idx, "processing_deadline", v)} />
                      <EditableDateCell editable={editMode} value={r.grinding_deadline}
                        onChange={(v) => updateRow(idx, "grinding_deadline", v)} />
                      <td className="border px-2 py-0.5 text-center"><FolderCell n={r.cu01_cnt} /></td>
                      <td className="border px-2 py-0.5 text-center"><FolderCell n={r.cu02_cnt} /></td>
                      <td className="border px-2 py-0.5 text-center"><FolderCell n={r.cu03_cnt} /></td>
                      <EditableTextCell editable={editMode} value={r.remark}
                        onChange={(v) => updateRow(idx, "remark", v)} />
                    </tr>
                  );
                })}
              </tbody>
            </table>
          )}
        </div>

        <DialogFooter className="border-t bg-muted/20 px-4 py-3 sm:justify-center">
          <Button variant="outline" onClick={() => onOpenChange(false)}>닫기</Button>
        </DialogFooter>
      </DialogContent>

      <MbomHistoryDialog
        open={historyOpen}
        onOpenChange={setHistoryOpen}
        projectObjid={projectObjid}
      />

      <MbomAssignDialog
        open={assignOpen}
        onOpenChange={setAssignOpen}
        projectObjid={projectObjid}
        currentEbomObjid={
          detail?.source_bom_type === "EBOM" ? detail.source_ebom_objid : null
        }
        onAssigned={() => {
          if (projectObjid) void loadTree(projectObjid);
          onSaved?.();
        }}
      />

      <MbomAddPartDialog
        open={addPartOpen}
        onOpenChange={setAddPartOpen}
        onConfirm={handleAddParts}
        parentLabel={
          selectedChildObjids.size === 1
            ? editableRows.find(r => String(r.child_objid) === Array.from(selectedChildObjids)[0])?.part_no ?? undefined
            : selectedChildObjids.size > 1
              ? `${Array.from(selectedChildObjids).length}개 선택 — 첫 항목 부모`
              : "root (최상위)"
        }
      />
    </Dialog>
  );
}

const SUPPLY_TYPE_OPTIONS = [
  { value: "", label: "—" },
  { value: "자급", label: "자급" },
  { value: "사급", label: "사급" },
];
const MAKE_OR_BUY_OPTIONS = [
  { value: "", label: "—" },
  { value: "Make", label: "Make" },
  { value: "Buy", label: "Buy" },
];

function EditableNumCell({ editable, value, onChange }: { editable: boolean; value: any; onChange: (v: any) => void }) {
  if (!editable) return <td className="border px-2 py-0.5 text-right">{fmtNum(value)}</td>;
  return (
    <td className="border px-1 py-0.5 text-right">
      <Input
        type="number"
        step="any"
        className="h-6 px-1 py-0 text-xs text-right border-blue-300 focus-visible:ring-1"
        value={value ?? ""}
        onChange={(e) => onChange(e.target.value === "" ? null : e.target.value)}
      />
    </td>
  );
}
function EditableTextCell({ editable, value, onChange }: { editable: boolean; value: any; onChange: (v: any) => void }) {
  if (!editable) return <td className="border px-2 py-0.5">{value ?? ""}</td>;
  return (
    <td className="border px-1 py-0.5">
      <Input
        type="text"
        className="h-6 px-1 py-0 text-xs border-blue-300 focus-visible:ring-1"
        value={value ?? ""}
        onChange={(e) => onChange(e.target.value)}
      />
    </td>
  );
}
function EditableDateCell({ editable, value, onChange }: { editable: boolean; value: any; onChange: (v: any) => void }) {
  if (!editable) return <td className="border px-2 py-0.5 text-center">{value ?? ""}</td>;
  return (
    <td className="border px-1 py-0.5">
      <Input
        type="date"
        className="h-6 px-1 py-0 text-xs border-blue-300 focus-visible:ring-1"
        value={value ?? ""}
        onChange={(e) => onChange(e.target.value)}
      />
    </td>
  );
}
function EditableSelectCell({
  editable, value, options, onChange,
}: { editable: boolean; value: any; options: { value: string; label: string }[]; onChange: (v: any) => void }) {
  if (!editable) return <td className="border px-2 py-0.5 text-center">{value ?? ""}</td>;
  return (
    <td className="border px-1 py-0.5">
      <select
        className="h-6 px-1 py-0 text-xs w-full border border-blue-300 rounded"
        value={value ?? ""}
        onChange={(e) => onChange(e.target.value || null)}
      >
        {options.map(o => <option key={o.value} value={o.value}>{o.label}</option>)}
      </select>
    </td>
  );
}

function FolderCell({ n }: { n: any }) {
  const has = Number(n ?? 0) > 0;
  return (
    <span className="inline-flex items-center justify-center">
      <Folder className={cn("w-4 h-4",
        has ? "fill-[#1a73e8] text-[#1a73e8]" : "fill-white text-muted-foreground/40")} />
    </span>
  );
}

function MetaRow({ label, value, numeric }: { label: string; value: any; numeric?: boolean }) {
  return (
    <div className="flex items-baseline gap-2">
      <span className="text-muted-foreground w-[80px] shrink-0">{label}</span>
      <span className={cn("font-medium", numeric && "tabular-nums")}>
        {value != null && value !== "" ? value : "—"}
      </span>
    </div>
  );
}

function fmtNum(v: any): string {
  if (v == null || v === "") return "";
  const n = Number(v);
  if (!isFinite(n)) return String(v);
  return Number.isInteger(n)
    ? n.toLocaleString()
    : n.toLocaleString(undefined, { maximumFractionDigits: 4 });
}

function paidLabel(v: string | null | undefined): string {
  if (v === "paid") return "유상";
  if (v === "free") return "무상";
  return v ?? "";
}
