// Price Comparison Module — Procurement-led supplier quotation comparison
// Pages exported on window:
//   PriceComparisonListPage  — list / KPIs / tabs / filters
//   CreatePriceComparisonPage — header + items + supplier quotes editor
//   PriceValidityPage         — approved prices history (grouped by item)
//
// Backend status flow: draft → pending → approved | revision → pending | rejected | cancelled

// ---------- Shared constants ----------
const PC_STATUS_META = {
  draft:     { label: "แบบร่าง",      color: "amber",   dot: "#f59e0b" },
  pending:   { label: "รออนุมัติ",   color: "amber",   dot: "#f59e0b" },
  revision:  { label: "ขอแก้ไข",     color: "rose",    dot: "#ef4444" },
  approved:  { label: "อนุมัติแล้ว", color: "emerald", dot: "#10b981" },
  rejected:  { label: "ถูกปฏิเสธ",   color: "rose",    dot: "#ef4444" },
  cancelled: { label: "ยกเลิก",      color: "slate",   dot: "#94a3b8" },
};

const PC_SELECTION_REASONS = [
  { code: "lowest_price",     label: "ราคาต่ำที่สุด" },
  { code: "better_delivery",  label: "ส่งมอบดีกว่า" },
  { code: "better_quality",   label: "คุณภาพดีกว่า" },
  { code: "approved_vendor",  label: "เป็น Approved Vendor" },
  { code: "better_payment",   label: "เงื่อนไขชำระเงินดีกว่า" },
  { code: "urgent",           label: "การจัดซื้อเร่งด่วน" },
  { code: "other",            label: "อื่นๆ (ระบุเหตุผล)" },
];

const PC_CURRENCIES = ["THB", "USD", "EUR", "JPY", "GBP", "CNY", "SGD", "MYR", "HKD", "AUD"];

// formatMoney is provided by src/ui.jsx (canonical implementation, attached to window).
// formatMoneyTHB is a thin local alias used in legacy spots — defaults to THB anyway.
const formatMoneyTHB = (n) => formatMoney(n, "THB");

// Status pill shared with PR but adapted for PC palette
const PCStatusPill = ({ status }) => {
  const m = PC_STATUS_META[status] || PC_STATUS_META.draft;
  const colorClass = {
    amber:   "bg-amber-50 text-amber-700 border-amber-200",
    rose:    "bg-rose-50 text-rose-700 border-rose-200",
    emerald: "bg-emerald-50 text-emerald-700 border-emerald-200",
    slate:   "bg-slate-100 text-slate-700 border-slate-200",
  }[m.color];
  return (
    <span className={`inline-flex items-center gap-1.5 px-2 py-0.5 rounded-md border ${colorClass} text-[11.5px] font-medium`}>
      <span className="w-1.5 h-1.5 rounded-full" style={{ background: m.dot }}/>
      <span>{m.label}</span>
    </span>
  );
};

const displayUserName = (users, userId) => {
  if (!userId) return "—";
  const u = users.find(x => x.id === userId);
  return u ? (u.full_name || u.email || userId) : userId;
};

// Resolve canManageOrdering / canManagePC client-side helper
const canManagePC = (role) =>
  role === 'procurement' || role === 'admin' || role === 'super_admin';

// =============================================================================
// PAGE 1 — PRICE COMPARISON LIST
// =============================================================================
const PriceComparisonListPage = ({ tenant, currentUser, currentRole, users, onCreate, onOpen, onEdit }) => {
  const toast = useToast();
  const [rows, setRows] = useState([]);
  const [loading, setLoading] = useState(false);
  const [q, setQ] = useState("");
  const [tab, setTab] = useState("__all__");
  const [filterCreator, setFilterCreator] = useState("__all__");
  const [filterApprover, setFilterApprover] = useState("__all__");
  const [openId, setOpenId] = useState(null);

  const reload = async () => {
    if (!tenant?.id || !window.apiClient) return;
    setLoading(true);
    try {
      const list = await window.apiClient.pcListDocuments(tenant.id);
      setRows(Array.isArray(list) ? list : []);
    } catch (e) {
      toast({ kind: "error", msg: e.message || "โหลดรายการเอกสารไม่สำเร็จ" });
    } finally { setLoading(false); }
  };
  useEffect(() => { reload(); /* eslint-disable-next-line */ }, [tenant?.id]);

  // KPI counts
  const counts = useMemo(() => {
    const c = { total: rows.length, draft: 0, pending: 0, revision: 0, approved: 0, rejected: 0, cancelled: 0 };
    rows.forEach(r => { c[r.status] = (c[r.status] || 0) + 1; });
    return c;
  }, [rows]);

  // Filtered list
  const filtered = useMemo(() => {
    const ql = q.trim().toLowerCase();
    return rows.filter(r => {
      if (tab !== "__all__" && r.status !== tab) return false;
      if (filterCreator !== "__all__" && r.created_by_user_id !== filterCreator) return false;
      if (filterApprover !== "__all__" && r.approver_id !== filterApprover) return false;
      if (!ql) return true;
      return (r.pc_number || "").toLowerCase().includes(ql)
          || (r.purpose || "").toLowerCase().includes(ql)
          || (r.item_names || "").toLowerCase().includes(ql)
          || (r.item_codes || "").toLowerCase().includes(ql)
          || (r.supplier_names || "").toLowerCase().includes(ql);
    });
  }, [rows, q, tab, filterCreator, filterApprover]);

  const exportToExcel = () => {
    const headers = ["เลขที่เอกสาร","วันที่เอกสาร","ผู้สร้าง","วัตถุประสงค์","จำนวนรายการ","ผู้อนุมัติ","สถานะ","วันที่อนุมัติ","มูลค่ารวม (THB)","อัปเดตล่าสุด"];
    const rowsCsv = filtered.map(r => [
      r.pc_number, formatDate(r.document_date),
      displayUserName(users, r.created_by_user_id),
      r.purpose || "",
      r.items_count || 0,
      displayUserName(users, r.approver_id),
      PC_STATUS_META[r.status]?.label || r.status,
      r.approved_at ? formatDate(r.approved_at.slice(0,10)) : "",
      Number(r.total_estimate_thb) || 0,
      formatDateTime(r.updated_at),
    ]);
    window.downloadCsv(`price-comparison-list-${new Date().toISOString().slice(0,10)}.csv`, headers, rowsCsv);
  };

  const tabs = [
    { id: "__all__",  label: "ทั้งหมด",      count: counts.total },
    { id: "draft",    label: "แบบร่าง",      count: counts.draft },
    { id: "pending",  label: "รออนุมัติ",   count: counts.pending },
    { id: "revision", label: "ขอแก้ไข",     count: counts.revision },
    { id: "approved", label: "อนุมัติแล้ว", count: counts.approved },
    { id: "rejected", label: "ถูกปฏิเสธ",   count: counts.rejected },
    { id: "cancelled",label: "ยกเลิก",      count: counts.cancelled },
  ];

  const handleOpen = (id) => { setOpenId(id); if (onOpen) onOpen(id); };

  return (
    <div className="p-6 max-w-[1400px] mx-auto">
      <div className="text-[11px] tracking-wider text-emerald-700 font-bold mb-1">↕ PRICE COMPARISON</div>
      <div className="flex items-start justify-between gap-3 mb-1">
        <div>
          <div className="text-[24px] font-bold text-ink-900 leading-tight">เปรียบเทียบราคา (Price Comparison)</div>
          <div className="text-[13px] text-ink-500 mt-1">
            เปรียบเทียบใบเสนอราคาจากผู้ขายหลายราย เลือกผู้ขายที่เหมาะสม และส่งขออนุมัติเพื่อบันทึกเป็นราคาที่อนุมัติแล้ว
          </div>
        </div>
        <div className="flex items-center gap-2 shrink-0">
          <Button variant="secondary" icon="fileBarChart" onClick={exportToExcel}>ส่งออก Excel</Button>
          {canManagePC(currentRole) && (
            <Button variant="primary" icon="plus" onClick={onCreate}>สร้างเอกสารใหม่</Button>
          )}
        </div>
      </div>

      {/* KPI cards */}
      <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-5 gap-3 mt-5">
        <KpiCard icon="fileText"  tone="indigo"  label="เอกสารทั้งหมด" value={counts.total}   trend="ในระบบเปรียบเทียบราคา"/>
        <KpiCard icon="clock"     tone="amber"   label="รออนุมัติ"     value={counts.pending} trend="ต้องดำเนินการ"/>
        <KpiCard icon="history"   tone="rose"    label="ขอแก้ไข"      value={counts.revision} trend="รอปรับปรุง"/>
        <KpiCard icon="check"     tone="emerald" label="อนุมัติแล้ว"  value={counts.approved} trend="พร้อมจัดซื้อ"/>
        <KpiCard icon="edit"      tone="slate"   label="แบบร่าง"      value={counts.draft}    trend="กำลังจัดทำ"/>
      </div>

      {/* Tabs */}
      <div className="flex items-center gap-2 mt-6 border-b border-ink-100">
        {tabs.map(t => (
          <button key={t.id} onClick={() => setTab(t.id)}
            className={`relative px-3 py-2.5 text-[13px] font-medium transition flex items-center gap-1.5
              ${tab === t.id ? "text-emerald-700" : "text-ink-500 hover:text-ink-800"}`}>
            {t.label}
            <span className={`inline-flex items-center justify-center min-w-[20px] h-[18px] px-1.5 text-[10.5px] font-bold rounded-md
              ${tab === t.id ? "bg-emerald-100 text-emerald-700" : "bg-ink-100 text-ink-600"}`}>{t.count}</span>
            {tab === t.id && <span className="absolute bottom-0 left-0 right-0 h-0.5 bg-emerald-600 rounded-t"/>}
          </button>
        ))}
      </div>

      {/* Search + filters */}
      <div className="flex items-center gap-2 mt-4">
        <div className="relative flex-1 max-w-[460px]">
          <Icon name="search" size={14} className="absolute left-3 top-1/2 -translate-y-1/2 text-ink-400"/>
          <input type="text" value={q} onChange={e => setQ(e.target.value)}
            placeholder="ค้นหาเลขที่เอกสาร / ผู้สร้าง / ชื่อสินค้า / รหัสสินค้า / ผู้ขาย"
            className="w-full h-9 pl-9 pr-3 text-[13px] border border-ink-200 rounded-lg bg-white focus:border-brand-500 ring-focus"/>
        </div>
        <div className="ml-auto flex items-center gap-2">
          <Select className="w-[180px]" value={filterCreator} onChange={e => setFilterCreator(e.target.value)}>
            <option value="__all__">ผู้สร้าง: ทั้งหมด</option>
            {users.map(u => <option key={u.id} value={u.id}>{u.full_name || u.email}</option>)}
          </Select>
          <Select className="w-[180px]" value={filterApprover} onChange={e => setFilterApprover(e.target.value)}>
            <option value="__all__">ผู้อนุมัติ: ทั้งหมด</option>
            {users.map(u => <option key={u.id} value={u.id}>{u.full_name || u.email}</option>)}
          </Select>
          <div className="text-[11.5px] text-ink-500 whitespace-nowrap">พบ {filtered.length} / {rows.length} รายการ</div>
        </div>
      </div>

      {/* Table */}
      <div className="mt-3 bg-white rounded-2xl border border-ink-100 overflow-hidden">
        {loading ? (
          <div className="p-10 text-center text-ink-500 text-sm">กำลังโหลด…</div>
        ) : filtered.length === 0 ? (
          <EmptyState icon="fileText" title="ยังไม่มีเอกสาร" subtitle="คลิก ‘สร้างเอกสารใหม่’ เพื่อเริ่มเปรียบเทียบราคา"/>
        ) : (
          <div className="overflow-x-auto thin-scroll">
            <table className="w-full text-[12.5px]">
              <thead className="bg-ink-50/60 text-ink-500 text-[11px] uppercase">
                <tr>
                  <th className="text-left px-4 py-2.5 font-semibold">เลขที่เอกสาร</th>
                  <th className="text-left px-3 py-2.5 font-semibold">วันที่เอกสาร</th>
                  <th className="text-left px-3 py-2.5 font-semibold">ผู้สร้าง</th>
                  <th className="text-right px-3 py-2.5 font-semibold">จำนวนรายการ</th>
                  <th className="text-left px-3 py-2.5 font-semibold">ส่งถึง (ผู้อนุมัติ)</th>
                  <th className="text-left px-3 py-2.5 font-semibold">สถานะ</th>
                  <th className="text-left px-3 py-2.5 font-semibold">วันที่อนุมัติ</th>
                  <th className="text-left px-3 py-2.5 font-semibold">อัปเดตล่าสุด</th>
                  <th className="text-center px-3 py-2.5 font-semibold">การดำเนินการ</th>
                </tr>
              </thead>
              <tbody>
                {filtered.map(r => {
                  const createdBy = users.find(u => u.id === r.created_by_user_id);
                  const approver = users.find(u => u.id === r.approver_id);
                  const purposeShort = (r.purpose || "—");
                  // Confidential detail gate — must match backend `canViewPCDetail`.
                  // Allowed: document creator · selected approver · tenant admin · super_admin.
                  const canView =
                    r.created_by_user_id === currentUser?.id ||
                    r.approver_id === currentUser?.id ||
                    currentRole === "admin" ||
                    currentRole === "super_admin";
                  return (
                    <tr key={r.id}
                      className={`border-t border-ink-100 transition ${canView ? "hover:bg-emerald-50/30 cursor-pointer" : "opacity-90"}`}
                      onClick={canView ? () => handleOpen(r.id) : undefined}>
                      <td className="px-4 py-2.5">
                        <div className="font-semibold text-emerald-700">{r.pc_number}</div>
                        <div className="text-[11px] text-ink-500 truncate max-w-[220px]">{purposeShort}</div>
                      </td>
                      <td className="px-3 py-2.5 text-ink-700 whitespace-nowrap">{formatDate(r.document_date)}</td>
                      <td className="px-3 py-2.5">
                        <div className="flex items-center gap-2">
                          <Avatar name={createdBy?.full_name || "—"} size={22}/>
                          <span className="truncate max-w-[140px]">{createdBy?.full_name || "—"}</span>
                        </div>
                      </td>
                      <td className="px-3 py-2.5 text-right text-ink-700">
                        <span className="inline-flex items-center justify-center min-w-[28px] h-6 px-2 bg-ink-100 rounded-md font-medium text-[11.5px]">{r.items_count || 0}</span>
                      </td>
                      <td className="px-3 py-2.5">
                        {approver ? (
                          <div className="flex items-center gap-2">
                            <Avatar name={approver?.full_name || "—"} size={22}/>
                            <span className="truncate max-w-[140px]">{approver?.full_name || "—"}</span>
                          </div>
                        ) : <span className="text-ink-400">—</span>}
                      </td>
                      <td className="px-3 py-2.5"><PCStatusPill status={r.status}/></td>
                      <td className="px-3 py-2.5 text-ink-700 whitespace-nowrap">{r.approved_at ? formatDate(r.approved_at.slice(0,10)) : "—"}</td>
                      <td className="px-3 py-2.5 text-ink-500 whitespace-nowrap">{formatDateTime(r.updated_at)}</td>
                      <td className="px-3 py-2.5">
                        <div className="flex items-center justify-center gap-1">
                          {canView ? (
                            <>
                              {(r.status === "draft" || r.status === "revision") && canManagePC(currentRole) && onEdit && (
                                <Tip tip={r.status === "revision" ? "แก้ไขตามที่ขอ" : "แก้ไขแบบร่าง"}>
                                  <button className="p-1.5 rounded-md text-emerald-600 hover:bg-emerald-50 hover:text-emerald-700"
                                    onClick={e => { e.stopPropagation(); onEdit(r.id); }}><Icon name="edit" size={13}/></button>
                                </Tip>
                              )}
                              <Tip tip="ดูรายละเอียด"><button className="p-1.5 rounded-md text-ink-500 hover:bg-ink-100 hover:text-emerald-700"
                                onClick={e => { e.stopPropagation(); handleOpen(r.id); }}><Icon name="eye" size={13}/></button></Tip>
                            </>
                          ) : (
                            <Tip multiline={"ข้อมูลคัดเลือกผู้ขายเป็นข้อมูลละเอียดอ่อน\nเฉพาะผู้สร้างเอกสาร · ผู้อนุมัติ · Admin · Super Admin เท่านั้นที่ดูได้"}>
                              <span className="inline-flex items-center gap-1 px-2 py-1 rounded-md bg-ink-100 text-ink-500 text-[10.5px] font-medium">
                                <Icon name="ban" size={11}/> ดูได้เฉพาะผู้เกี่ยวข้อง
                              </span>
                            </Tip>
                          )}
                        </div>
                      </td>
                    </tr>
                  );
                })}
              </tbody>
            </table>
          </div>
        )}
      </div>

      {openId && (
        <PriceComparisonDetailModal
          pcId={openId}
          tenant={tenant}
          currentUser={currentUser}
          currentRole={currentRole}
          users={users}
          onClose={() => setOpenId(null)}
          onChanged={() => { reload(); }}
        />
      )}
    </div>
  );
};

// =============================================================================
// PAGE 2 — CREATE / EDIT PRICE COMPARISON
// -----------------------------------------------------------------------------
// Layout: vendor columns (up to 4) × item rows. Each cell holds one price.
// One attachment per vendor (shared across all that vendor's quotes for this PC).
// =============================================================================
const blankVendor = () => ({
  supplier_id: null,  // link to suppliers master (optional but recommended)
  supplier_name: "", supplier_code: "", quotation_number: "", quotation_date: "",
  valid_until_date: "", currency: "THB", exchange_rate_to_thb: "",
  vat_included: 0, lead_time: "", payment_term: "", warranty_term: "", remark: "",
  attachment: null, attachment_id: null,
});

const blankItem = (vendorCount = 0) => ({
  item_master_id: null, item_code: "", item_name: "", description: "", unit: "",
  required_quantity: "", line_remark: "",
  prices: Array.from({ length: vendorCount }, () => ({ value: "", not_quoted: false })),
  selected_vendor_index: null,
  selection_reason_code: "", selection_reason_text: "",
});

// Compute THB equivalent of a price given the vendor that quoted it.
const priceThbEquiv = (priceValue, vendor) => {
  const p = Number(priceValue) || 0;
  if (!(p > 0)) return null;
  if ((vendor.currency || "THB").toUpperCase() === "THB") return p;
  const x = Number(vendor.exchange_rate_to_thb);
  return x > 0 ? p * x : null;
};

// Smaller item selector — autocomplete from Item Master
const ItemMasterPicker = ({ items, onPick, placeholder = "พิมพ์รหัสหรือชื่อสินค้า…" }) => {
  const [q, setQ] = useState("");
  const [open, setOpen] = useState(false);
  const ref = useRef(null);
  useEffect(() => {
    const close = (e) => { if (!ref.current?.contains(e.target)) setOpen(false); };
    document.addEventListener("mousedown", close);
    return () => document.removeEventListener("mousedown", close);
  }, []);
  const ql = q.trim().toLowerCase();
  const filtered = ql ? items.filter(i =>
    (i.item_code || "").toLowerCase().includes(ql) ||
    (i.name || "").toLowerCase().includes(ql)
  ).slice(0, 20) : items.slice(0, 20);
  return (
    <div ref={ref} className="relative">
      <div className="relative">
        <Icon name="search" size={14} className="absolute left-3 top-1/2 -translate-y-1/2 text-ink-400"/>
        <input value={q} onChange={e => { setQ(e.target.value); setOpen(true); }} onFocus={() => setOpen(true)}
          placeholder={placeholder}
          className="w-full h-9 pl-9 pr-3 text-[13px] border border-ink-200 rounded-lg bg-white focus:border-brand-500 ring-focus"/>
      </div>
      {open && filtered.length > 0 && (
        <div className="absolute z-30 mt-1 w-full max-h-[280px] overflow-y-auto thin-scroll bg-white border border-ink-200 rounded-lg shadow-lg">
          {filtered.map(i => (
            <button key={i.id} onClick={() => { onPick(i); setQ(""); setOpen(false); }}
              className="w-full text-left px-3 py-2 hover:bg-emerald-50 border-b border-ink-50 last:border-b-0">
              <div className="text-[12.5px] font-mono text-ink-700">{i.item_code}</div>
              <div className="text-[13px] text-ink-900 truncate">{i.name}</div>
              {i.unit && <div className="text-[11px] text-ink-500">หน่วย: {i.unit}</div>}
            </button>
          ))}
        </div>
      )}
    </div>
  );
};

// ----- Vendor details modal (edit all vendor fields except price) -----
const VendorDetailsModal = ({ open, onClose, vendor, vendorIdx, onSave, onRemove, tenant }) => {
  const toast = useToast();
  const [v, setV] = useState(vendor || blankVendor());
  useEffect(() => { if (open) setV(vendor || blankVendor()); }, [open, vendor]);
  if (!open) return null;
  const upd = (patch) => setV(prev => ({ ...prev, ...patch }));

  // When user picks from supplier master → auto-fill basic fields
  const onPickSupplier = (supId, supplier) => {
    if (!supId || !supplier) {
      upd({ supplier_id: null });
      return;
    }
    upd({
      supplier_id: supplier.id,
      supplier_name: supplier.supplier_name || v.supplier_name,
      supplier_code: supplier.supplier_code || v.supplier_code,
      currency: supplier.default_currency || v.currency || "THB",
      payment_term: supplier.payment_term || v.payment_term,
    });
  };

  // Validate required fields before allowing save. Matches the asterisks shown in the UI.
  const handleSave = () => {
    if (!(v.supplier_name || "").trim()) { toast({ kind: "warn", msg: "กรุณาระบุชื่อผู้ขาย" }); return; }
    if (!v.valid_until_date) { toast({ kind: "warn", msg: "กรุณาระบุวันยืนราคาถึง" }); return; }
    if (!v.currency) { toast({ kind: "warn", msg: "กรุณาเลือกสกุลเงิน" }); return; }
    // Cross-field sanity: quotation_date should not exceed valid_until_date
    if (v.quotation_date && v.valid_until_date && v.quotation_date > v.valid_until_date) {
      toast({ kind: "warn", msg: "วันที่เสนอราคา ต้องไม่เกิน วันยืนราคาถึง" }); return;
    }
    // Foreign currency must have exchange rate filled
    if ((v.currency || "THB") !== "THB" && !(Number(v.exchange_rate_to_thb) > 0)) {
      toast({ kind: "warn", msg: `สกุลเงิน ${v.currency} ต้องระบุอัตราแลกเปลี่ยน → THB` }); return;
    }
    onSave(v);
    onClose();
  };

  return (
    <Modal open={open} onClose={onClose} width={640}
      title={`รายละเอียดผู้ขายที่ ${vendorIdx + 1}`}
      subtitle="ข้อมูลที่กรอกที่นี่จะใช้กับสินค้าทุกรายการในเอกสารนี้"
      footer={<>
        {onRemove && <Button variant="danger" icon="trash" onClick={() => { onRemove(); onClose(); }}>ลบคอลัมน์ผู้ขาย</Button>}
        <Button variant="secondary" onClick={onClose}>ยกเลิก</Button>
        <Button variant="primary" icon="check" onClick={handleSave}>บันทึก</Button>
      </>}>
      {/* Supplier master picker at top — pre-fills basic fields */}
      <div className="mb-3 p-3 bg-emerald-50/60 border border-emerald-200 rounded-xl">
        <Field label="เลือกจากทะเบียนผู้จำหน่าย (Vendor Master)" hint="หรือกรอกเองด้านล่าง">
          {window.SupplierSearchablePicker ? (
            <window.SupplierSearchablePicker tenant={tenant} value={v.supplier_id || null} onChange={onPickSupplier}/>
          ) : (
            <div className="text-[11px] text-ink-500">โมดูลทะเบียนผู้จำหน่ายยังไม่พร้อม</div>
          )}
        </Field>
      </div>

      <div className="grid grid-cols-2 gap-3">
        <Field label="ชื่อผู้ขาย" required className="col-span-2">
          <Input value={v.supplier_name} onChange={e => upd({ supplier_name: e.target.value })} placeholder="เช่น สยาม สเตชั่นเนอรี่"/>
        </Field>
        <Field label="รหัสผู้ขาย">
          <Input value={v.supplier_code} onChange={e => upd({ supplier_code: e.target.value })} placeholder="SUP-XX"/>
        </Field>
        <Field label="เลขที่ใบเสนอราคา">
          <Input value={v.quotation_number} onChange={e => upd({ quotation_number: e.target.value })} placeholder="QT-2026-XX"/>
        </Field>
        <Field label="วันที่เสนอราคา">
          <ThaiDateInput value={v.quotation_date} onChange={x => upd({ quotation_date: x })}/>
        </Field>
        <Field label="ยืนราคาถึง" required>
          <ThaiDateInput value={v.valid_until_date} onChange={x => upd({ valid_until_date: x })}/>
        </Field>
        <Field label="สกุลเงิน" required>
          <Select value={v.currency} onChange={e => upd({ currency: e.target.value })}>
            {PC_CURRENCIES.map(c => <option key={c} value={c}>{c}</option>)}
          </Select>
        </Field>
        {v.currency !== "THB" && (
          <Field label="อัตราแลกเปลี่ยน → THB" required>
            <Input type="number" step="0.0001" min="0" value={v.exchange_rate_to_thb}
              onChange={e => upd({ exchange_rate_to_thb: e.target.value.replace(/^0+(\d)/, "$1") })} placeholder="เช่น 36.50"/>
          </Field>
        )}
        <Field label="ภาษี VAT">
          <Select value={v.vat_included ? "1" : "0"} onChange={e => upd({ vat_included: e.target.value === "1" ? 1 : 0 })}>
            <option value="0">ไม่รวม VAT</option>
            <option value="1">รวม VAT แล้ว</option>
          </Select>
        </Field>
        <Field label="ระยะเวลาส่งมอบ">
          <Input value={v.lead_time} onChange={e => upd({ lead_time: e.target.value })} placeholder="เช่น 5-7 วัน"/>
        </Field>
        <Field label="เงื่อนไขชำระเงิน">
          <Input value={v.payment_term} onChange={e => upd({ payment_term: e.target.value })} placeholder="เช่น เครดิต 30 วัน"/>
        </Field>
        <Field label="การรับประกัน" className="col-span-2">
          <Input value={v.warranty_term} onChange={e => upd({ warranty_term: e.target.value })} placeholder="เช่น 1 ปี"/>
        </Field>
        <Field label="หมายเหตุของผู้ขาย" className="col-span-2">
          <Textarea rows={2} value={v.remark} onChange={e => upd({ remark: e.target.value })} placeholder="บันทึกเพิ่มเติม เช่น เป็น approved vendor"/>
        </Field>
      </div>
    </Modal>
  );
};

// ----- Vendor column header cell (shows compact info + edit button) -----
const VendorColumnHeader = ({ vendor, vendorIdx, onEdit }) => {
  const filled = !!vendor.supplier_name;
  return (
    <th className={`px-3 py-2 border-l border-ink-100 align-top text-left min-w-[200px] ${filled ? "bg-white" : "bg-ink-50/60"}`}>
      <div className="flex items-start justify-between gap-2">
        <div className="min-w-0">
          <div className="text-[11px] text-ink-500 mb-0.5">ผู้ขายที่ {vendorIdx + 1}</div>
          <div className={`font-semibold text-[13px] truncate ${filled ? "text-ink-900" : "text-ink-400"}`}>
            {vendor.supplier_name || "— ยังไม่กรอกข้อมูล —"}
          </div>
          {filled && (
            <div className="text-[10.5px] text-ink-500 mt-0.5 space-y-0.5">
              {vendor.supplier_code && <div className="font-mono">{vendor.supplier_code} · {vendor.currency}</div>}
              {vendor.quotation_number && <div>QT: {vendor.quotation_number}</div>}
              {vendor.valid_until_date && (
                <div className="flex items-center gap-1">
                  <Icon name="calendar" size={10}/> ยืนถึง {formatDate(vendor.valid_until_date)}
                </div>
              )}
            </div>
          )}
        </div>
        <button onClick={onEdit}
          className="shrink-0 text-[11px] inline-flex items-center gap-1 px-2 py-1 rounded-md bg-emerald-50 text-emerald-700 hover:bg-emerald-100 font-medium">
          <Icon name="edit" size={10}/> {filled ? "แก้ไข" : "เพิ่มข้อมูล"}
        </button>
      </div>
    </th>
  );
};

// ----- Price cell input (with "not_quoted" toggle) -----
const PriceCellInput = ({ price, vendor, isSelected, isLowest, hasVendorInfo, onChange, onToggleNotQuoted, onSelect }) => {
  const upd = (patch) => onChange({ ...price, ...patch });
  if (price.not_quoted) {
    return (
      <td className={`px-3 py-2 border-l border-ink-100 align-top ${isSelected ? "bg-emerald-50/60" : ""}`}>
        <div className="flex flex-col items-center gap-1">
          <Badge color="slate">ไม่เสนอ</Badge>
          <button onClick={onToggleNotQuoted} className="text-[10.5px] text-emerald-700 hover:underline">
            กรอกราคา
          </button>
        </div>
      </td>
    );
  }
  return (
    <td className={`px-3 py-2 border-l border-ink-100 align-top ${isSelected ? "bg-emerald-50/60" : ""}`}>
      <div className="flex flex-col gap-1.5">
        <div className="relative">
          <input
            type="number" step="0.01" min="0"
            value={price.value}
            onChange={e => upd({ value: e.target.value.replace(/^0+(\d)/, "$1") })}
            placeholder="0.00"
            disabled={!hasVendorInfo}
            className={`w-full h-9 pl-2 pr-10 text-right font-mono text-[13px] border rounded-lg bg-white focus:border-emerald-500 transition
              ${isSelected ? "border-emerald-400 font-bold text-emerald-700" : "border-ink-200 text-ink-800"}
              ${!hasVendorInfo ? "opacity-50 cursor-not-allowed" : ""}`}
          />
          <span className="absolute right-2 top-1/2 -translate-y-1/2 text-[10.5px] text-ink-400 font-mono">{vendor.currency}</span>
        </div>
        <div className="flex items-center justify-between gap-1">
          <div className="flex items-center gap-1">
            {isLowest && <Badge color="emerald">ต่ำสุด</Badge>}
            {isSelected && <Badge color="emerald">เลือก</Badge>}
          </div>
          <div className="flex items-center gap-1">
            {!isSelected && Number(price.value) > 0 && hasVendorInfo && (
              <button onClick={onSelect}
                className="text-[10.5px] px-1.5 py-0.5 rounded-md bg-ink-100 hover:bg-emerald-100 hover:text-emerald-700 font-medium">
                เลือก
              </button>
            )}
            <Tip tip="ไม่เสนอราคา"><button onClick={onToggleNotQuoted}
              className="p-0.5 text-ink-400 hover:text-ink-700"><Icon name="ban" size={11}/></button></Tip>
          </div>
        </div>
      </div>
    </td>
  );
};

// ----- Right-most column showing the selected supplier + price for that item -----
const SelectedSupplierCell = ({ item, vendors, onClearSelection }) => {
  const selIdx = item.selected_vendor_index;
  if (selIdx == null || !vendors[selIdx]) {
    return (
      <td className="px-3 py-2 border-l border-emerald-200 bg-emerald-50/30 align-top min-w-[170px]">
        <div className="text-[11.5px] text-ink-400 italic">ยังไม่ได้เลือกผู้ขาย</div>
      </td>
    );
  }
  const v = vendors[selIdx];
  const p = item.prices[selIdx];
  const thbEq = priceThbEquiv(p?.value, v);
  // Find lowest THB across all quoted vendors
  const allEqs = item.prices.map((pp, vi) => (pp && !pp.not_quoted && pp.value !== "" ? priceThbEquiv(pp.value, vendors[vi]) : null));
  const lowest = allEqs.filter(x => x != null && x > 0).reduce((m, x) => (m == null || x < m) ? x : m, null);
  const isNotLowest = lowest != null && thbEq != null && thbEq > lowest + 0.001;
  return (
    <td className="px-3 py-2 border-l border-emerald-200 bg-emerald-50/30 align-top min-w-[170px]">
      <div className="flex items-start justify-between gap-1">
        <div className="min-w-0 flex-1">
          <div className="text-[11px] text-emerald-700 font-semibold">{v.supplier_name}</div>
          <div className="text-[14px] font-bold text-ink-900 font-mono mt-0.5">
            {p && p.value !== "" ? formatMoney(p.value, v.currency) : "—"}
          </div>
          <div className="mt-1 flex flex-wrap gap-1">
            {isNotLowest && <Badge color="amber">ไม่ใช่ราคาต่ำสุด</Badge>}
          </div>
        </div>
        <Tip tip="ยกเลิกการเลือก"><button onClick={onClearSelection}
          className="shrink-0 p-1 rounded-md text-ink-400 hover:bg-rose-50 hover:text-rose-600"><Icon name="x" size={11}/></button></Tip>
      </div>
    </td>
  );
};

// ----- Inline reason area shown below each item row -----
const ItemReasonInline = ({ item, vendors, vendorCount, onChange }) => {
  const selIdx = item.selected_vendor_index;
  if (selIdx == null) return null;
  const upd = (patch) => onChange({ ...item, ...patch });
  // Warn if selection isn't lowest
  const allEqs = item.prices.map((p, vi) => (p && !p.not_quoted && p.value !== "" ? priceThbEquiv(p.value, vendors[vi]) : null));
  const lowest = allEqs.filter(x => x != null && x > 0).reduce((m, x) => (m == null || x < m) ? x : m, null);
  const selEq = allEqs[selIdx];
  const isNotLowest = lowest != null && selEq != null && selEq > lowest + 0.001;
  const bgCls = isNotLowest ? "bg-amber-50/40 border-amber-200" : "bg-emerald-50/40 border-emerald-200";
  return (
    <tr>
      <td colSpan={vendorCount + 2} className="p-0">
        <div className={`m-0 px-4 py-3 border-l-4 ${bgCls} border-t border-ink-100`}>
          {isNotLowest && (
            <div className="flex items-start gap-2 text-amber-800 text-[12px] mb-2">
              <Icon name="warning" size={13} className="mt-0.5 shrink-0"/>
              <div>ผู้ขายที่เลือกสำหรับ <span className="font-mono font-bold">{item.item_code}</span> ไม่ใช่ราคาที่ต่ำที่สุด — โปรดระบุเหตุผลที่เลือก</div>
            </div>
          )}
          <div className="grid grid-cols-1 md:grid-cols-4 gap-2">
            <div className="md:col-span-1">
              <div className="text-[11px] text-ink-700 font-medium mb-1 flex items-center gap-1">
                <Icon name="check" size={11} className="text-emerald-700"/>
                เหตุผล <span className="text-rose-500">*</span>
              </div>
              <Select value={item.selection_reason_code} onChange={e => upd({ selection_reason_code: e.target.value })}>
                <option value="">— เลือกเหตุผล —</option>
                {PC_SELECTION_REASONS.map(r => <option key={r.code} value={r.code}>{r.label}</option>)}
              </Select>
            </div>
            <div className="md:col-span-3">
              <div className="text-[11px] text-ink-700 font-medium mb-1">คำอธิบาย <span className="text-rose-500">*</span></div>
              <Textarea rows={2} value={item.selection_reason_text}
                onChange={e => upd({ selection_reason_text: e.target.value })}
                placeholder='เช่น "คุณภาพดีกว่า — แม้ราคาสูงกว่าราคาต่ำที่สุด 6 บาท/แกลลอน"'/>
            </div>
          </div>
        </div>
      </td>
    </tr>
  );
};

// ----- Bottom: vendor attachment list (one file per vendor) -----
const VendorAttachmentSection = ({ vendors, pcId, uploadingByVendor = {}, onAttach, onRemoveAttach, onRequestSaveDraft }) => {
  const completed = vendors.filter(v => !!v.attachment).length;
  const total = vendors.filter(v => !!v.supplier_name).length;
  return (
    <div className="bg-white border border-ink-200 rounded-2xl p-5 mt-4">
      <div className="flex items-center gap-2 mb-3">
        <div className="w-7 h-7 rounded-lg bg-emerald-100 text-emerald-700 flex items-center justify-center">
          <Icon name="paperclip" size={14}/>
        </div>
        <div className="flex-1">
          <div className="text-[14px] font-bold text-ink-900">เอกสารแนบใบเสนอราคาของผู้ขาย</div>
          <div className="text-[11px] text-ink-500">
            {completed} / {total} แนบแล้ว · ไฟล์ละ ≤ 10MB · PDF / JPG / PNG / DOCX / XLSX
            {!pcId && <span className="text-amber-700"> · ⚠ ต้องบันทึกแบบร่างก่อนถึงจะแนบไฟล์ได้</span>}
          </div>
        </div>
      </div>
      <div className="space-y-2">
        {vendors.map((v, vi) => {
          const filled = !!v.supplier_name;
          if (!filled) return null;
          const att = v.attachment;
          const uploadingFilename = uploadingByVendor[vi];   // truthy when this vendor's file is in-flight
          return (
            <div key={vi} className={`flex items-center gap-3 p-3 rounded-xl border ${att ? "bg-ink-50/40 border-ink-100" : uploadingFilename ? "bg-emerald-50/40 border-emerald-200" : "bg-amber-50/30 border-amber-100"}`}>
              <div className={`w-9 h-9 rounded-lg flex items-center justify-center shrink-0 ${att ? "bg-emerald-100 text-emerald-700" : uploadingFilename ? "bg-emerald-200 text-emerald-800" : "bg-amber-100 text-amber-700"}`}>
                <Icon name="paperclip" size={14}/>
              </div>
              <div className="flex-1 min-w-0">
                <div className="text-[13px] font-semibold text-ink-900 truncate">{v.supplier_name}</div>
                <div className="text-[10.5px] text-ink-500 mt-0.5">
                  {[v.supplier_code, v.currency, v.quotation_date && formatDate(v.quotation_date)].filter(Boolean).join(" · ")}
                </div>
              </div>
              {uploadingFilename ? (
                // Show in-flight progress — encompasses the whole right side of the row
                <div className="flex items-center gap-2 shrink-0">
                  <div className="text-right">
                    <div className="text-[12px] text-emerald-700 font-medium flex items-center gap-1.5 justify-end">
                      <span className="inline-block w-3 h-3 border-2 border-emerald-600 border-t-transparent rounded-full animate-spin"/>
                      กำลังอัปโหลด…
                    </div>
                    <div className="text-[10px] text-ink-500 truncate max-w-[260px]">{uploadingFilename}</div>
                  </div>
                </div>
              ) : att ? (
                <>
                  <div className="text-right shrink-0">
                    <div className="text-[12px] text-ink-700 font-medium flex items-center gap-1.5 justify-end">
                      <Icon name="paperclip" size={11} className="text-ink-500"/>
                      <span className="truncate max-w-[200px]">{att.filename}</span>
                    </div>
                    <div className="text-[10px] text-ink-500">{att.file_size ? `${(att.file_size/1024).toFixed(0)} KB` : ""}</div>
                  </div>
                  <Tip tip="ลบไฟล์แนบ"><button onClick={() => onRemoveAttach(vi)}
                    className="shrink-0 p-1.5 rounded-md text-rose-500 hover:bg-rose-50"><Icon name="trash" size={13}/></button></Tip>
                </>
              ) : !pcId ? (
                // PC not saved yet — clicking auto-triggers saveDraft so the user can attach immediately afterwards.
                <button type="button" onClick={() => onRequestSaveDraft && onRequestSaveDraft()}
                  className="shrink-0 inline-flex items-center gap-1.5 h-8 px-3 rounded-md bg-amber-500 hover:bg-amber-600 text-white text-[12px] font-semibold">
                  <Icon name="check" size={12}/>
                  บันทึกร่างก่อนแนบไฟล์
                </button>
              ) : (
                <label className="shrink-0 inline-flex items-center gap-1.5 h-8 px-3 rounded-md bg-emerald-600 hover:bg-emerald-700 text-white text-[12px] font-semibold cursor-pointer">
                  <Icon name="paperclip" size={12}/>
                  แนบไฟล์
                  <input type="file" className="hidden"
                    accept=".pdf,.jpg,.jpeg,.png,.doc,.docx,.xls,.xlsx"
                    onChange={e => { const f = e.target.files?.[0]; if (f) onAttach(vi, f); e.target.value=""; }}/>
                </label>
              )}
            </div>
          );
        })}
        {total === 0 && (
          <div className="text-center text-[12px] text-ink-500 py-6">
            เพิ่มผู้ขายในตารางด้านบนก่อน เพื่อแนบใบเสนอราคา
          </div>
        )}
      </div>
    </div>
  );
};

// ----- Add item modal: inline list (no floating dropdown — avoids modal overflow clipping) -----
const AddItemFromMasterModal = ({ open, onClose, masterItems, existingItemMasterIds, onPick }) => {
  const [q, setQ] = useState("");
  useEffect(() => { if (open) setQ(""); }, [open]);
  const ql = q.trim().toLowerCase();
  const filtered = useMemo(() => {
    if (!Array.isArray(masterItems)) return [];
    const base = ql
      ? masterItems.filter(i =>
          (i.item_code || "").toLowerCase().includes(ql) ||
          (i.name || "").toLowerCase().includes(ql) ||
          (i.description || "").toLowerCase().includes(ql))
      : masterItems;
    return base.slice(0, 200);
  }, [masterItems, ql]);

  return (
    <Modal open={open} onClose={onClose} width={560}
      title="เลือกสินค้าจาก Item Master"
      subtitle={`พิมพ์รหัสหรือชื่อสินค้าเพื่อค้นหา · ${masterItems?.length || 0} รายการในระบบ`}
      footer={<Button variant="secondary" onClick={onClose}>ปิด</Button>}
    >
      <div className="relative mb-3">
        <Icon name="search" size={14} className="absolute left-3 top-1/2 -translate-y-1/2 text-ink-400"/>
        <input
          autoFocus
          value={q}
          onChange={e => setQ(e.target.value)}
          placeholder="พิมพ์รหัสหรือชื่อสินค้า…"
          className="w-full h-10 pl-9 pr-3 text-[13px] border border-ink-200 rounded-lg bg-white focus:border-emerald-500 ring-focus"
        />
      </div>
      <div className="max-h-[50vh] overflow-y-auto thin-scroll -mx-1 px-1">
        {filtered.length === 0 ? (
          <div className="text-center py-8 text-[12.5px] text-ink-500">
            {ql ? `ไม่พบสินค้าที่ตรงกับ “${q}”` : "ยังไม่มีสินค้าใน Item Master"}
          </div>
        ) : (
          <div className="space-y-1">
            {filtered.map(i => {
              const already = existingItemMasterIds?.has(i.id);
              return (
                <button key={i.id}
                  onClick={() => { onPick(i); onClose(); }}
                  className={`w-full text-left p-2.5 rounded-lg border transition flex items-start gap-3
                    ${already
                      ? "border-amber-200 bg-amber-50/40 hover:bg-amber-50"
                      : "border-ink-100 hover:border-emerald-300 hover:bg-emerald-50/40"}`}>
                  <div className="shrink-0 w-9 h-9 rounded-lg bg-emerald-100 text-emerald-700 flex items-center justify-center">
                    <Icon name="package" size={14}/>
                  </div>
                  <div className="flex-1 min-w-0">
                    <div className="flex items-center gap-2">
                      <span className="font-mono text-[11.5px] font-bold text-emerald-700">{i.item_code}</span>
                      <span className="text-[13px] font-semibold text-ink-900 truncate">{i.name}</span>
                      {already && <Badge color="amber">มีในเอกสารแล้ว</Badge>}
                    </div>
                    {i.description && <div className="text-[11.5px] text-ink-500 mt-0.5 line-clamp-1">{i.description}</div>}
                    <div className="flex items-center gap-3 mt-1 text-[10.5px] text-ink-500">
                      {i.unit && <span>หน่วย: {i.unit}</span>}
                      {i.category && <span>หมวด: {i.category}</span>}
                      {i.base_price ? <span>ราคาอ้างอิง: ฿{Number(i.base_price).toLocaleString()}</span> : null}
                    </div>
                  </div>
                  <Icon name="plus" size={14} className="shrink-0 text-emerald-700 mt-2"/>
                </button>
              );
            })}
          </div>
        )}
      </div>
    </Modal>
  );
};

// Searchable user picker — inline replacement for plain Select that filters as you type.
// Used for "ผู้อนุมัติ" on the Create PC form (scales to large user lists).
const SearchableUserPicker = ({ users, value, onChange, placeholder = "พิมพ์ชื่อ / อีเมล เพื่อค้นหา…" }) => {
  const [q, setQ] = useState("");
  const [open, setOpen] = useState(false);
  const wrapRef = useRef(null);
  useEffect(() => {
    const close = (e) => { if (!wrapRef.current?.contains(e.target)) setOpen(false); };
    document.addEventListener("mousedown", close);
    return () => document.removeEventListener("mousedown", close);
  }, []);

  const selected = users.find(u => u.id === value);
  const ql = q.trim().toLowerCase();
  const filtered = useMemo(() => {
    const base = ql
      ? users.filter(u =>
          (u.full_name || "").toLowerCase().includes(ql) ||
          (u.email || "").toLowerCase().includes(ql) ||
          (u.position_name || "").toLowerCase().includes(ql))
      : users;
    return base.slice(0, 100);
  }, [users, ql]);

  return (
    <div ref={wrapRef} className="relative">
      {selected && !open ? (
        // Selected pill — click to re-open and change
        <button type="button" onClick={() => { setOpen(true); setQ(""); }}
          className="w-full h-9 px-2.5 flex items-center gap-2 bg-white border border-ink-200 hover:border-brand-400 rounded-lg text-left transition">
          <Avatar name={selected.full_name} size={22}/>
          <span className="flex-1 min-w-0">
            <span className="text-[13px] text-ink-900 truncate block">{selected.full_name || selected.email}</span>
            {selected.email && selected.full_name && (
              <span className="text-[10.5px] text-ink-500 truncate block">{selected.email}</span>
            )}
          </span>
          <Tip tip="ยกเลิกการเลือก"><span role="button" tabIndex={0}
            onClick={(e) => { e.stopPropagation(); onChange(""); setQ(""); }}
            className="p-1 text-ink-400 hover:text-rose-500"><Icon name="x" size={12}/></span></Tip>
          <Icon name="chevronDown" size={13} className="text-ink-400 shrink-0"/>
        </button>
      ) : (
        <div className="relative">
          <Icon name="search" size={14} className="absolute left-3 top-1/2 -translate-y-1/2 text-ink-400"/>
          <input
            autoFocus={open}
            type="text"
            value={q}
            onChange={e => { setQ(e.target.value); setOpen(true); }}
            onFocus={() => setOpen(true)}
            placeholder={placeholder}
            className="w-full h-9 pl-9 pr-3 text-[13px] border border-ink-200 rounded-lg bg-white focus:border-brand-500 ring-focus"/>
        </div>
      )}

      {open && (
        <div className="absolute z-30 mt-1 w-full max-h-[280px] overflow-y-auto thin-scroll bg-white border border-ink-200 rounded-lg shadow-lg">
          {filtered.length === 0 ? (
            <div className="px-3 py-4 text-center text-[12px] text-ink-500">
              {ql ? `ไม่พบ "${q}"` : "ยังไม่มีผู้ใช้"}
            </div>
          ) : (
            <>
              {filtered.map(u => {
                const isSel = u.id === value;
                return (
                  <button key={u.id} type="button"
                    onClick={() => { onChange(u.id); setOpen(false); setQ(""); }}
                    className={`w-full flex items-center gap-2.5 px-3 py-2 text-left border-b border-ink-50 last:border-b-0 transition
                      ${isSel ? "bg-emerald-50" : "hover:bg-emerald-50/40"}`}>
                    <Avatar name={u.full_name} size={26}/>
                    <div className="flex-1 min-w-0">
                      <div className="text-[12.5px] font-semibold text-ink-900 truncate">{u.full_name || u.email}</div>
                      {u.email && u.full_name && (
                        <div className="text-[10.5px] text-ink-500 truncate">{u.email}{u.position_name ? " · " + u.position_name : ""}</div>
                      )}
                    </div>
                    {isSel && <Icon name="check" size={13} className="text-emerald-600 shrink-0"/>}
                  </button>
                );
              })}
              {users.length > filtered.length && (
                <div className="px-3 py-2 text-center text-[10.5px] text-ink-400 italic border-t border-ink-50">
                  แสดง {filtered.length} จาก {users.length} · พิมพ์เพื่อค้นหา
                </div>
              )}
            </>
          )}
        </div>
      )}
    </div>
  );
};

const CreatePriceComparisonPage = ({ tenant, currentUser, currentRole, users, onDone, onCancel, editPcId }) => {
  const toast = useToast();
  const [masterItems, setMasterItems] = useState([]);
  const [pcId, setPcId] = useState(editPcId || null);
  const [pcNumber, setPcNumber] = useState("");
  const [docDate, setDocDate] = useState(new Date().toISOString().slice(0, 10));
  const [approverId, setApproverId] = useState("");
  const [purpose, setPurpose] = useState("");
  const [relatedPR, setRelatedPR] = useState("");
  const [remark, setRemark] = useState("");
  const [pcStatus, setPcStatus] = useState("draft");      // tracked so we can show revision banner
  const [revisionReason, setRevisionReason] = useState(""); // approver's revision comment
  const [vendors, setVendors] = useState([blankVendor(), blankVendor()]);  // start with 2 columns
  const [items, setItems] = useState([]);
  // busyUpload state removed — replaced by per-vendor uploadingByVendor map below
  // Per-vendor upload busy state — allows other vendors to remain interactive
  // while one vendor's file is uploading. Keys are vendor indices.
  const [uploadingByVendor, setUploadingByVendor] = useState({});
  const [saving, setSaving] = useState(false);
  const [submitting, setSubmitting] = useState(false);
  const [editVendorIdx, setEditVendorIdx] = useState(null);  // open VendorDetailsModal for this idx
  const [addItemOpen, setAddItemOpen] = useState(false);

  // Load Item Master + existing PC (if editing) — reconstruct vendor columns from display_order
  useEffect(() => {
    (async () => {
      if (!tenant?.id || !window.apiClient) return;
      try {
        const mi = await window.apiClient.prListItems(tenant.id);
        setMasterItems(Array.isArray(mi) ? mi.filter(i => i.status === "active") : []);
      } catch (e) { toast({ kind:"error", msg: "โหลด Item Master ไม่สำเร็จ" }); }

      if (editPcId) {
        try {
          const pc = await window.apiClient.pcGetDocument(tenant.id, editPcId);
          setPcId(pc.id);
          setPcNumber(pc.pc_number);
          setPcStatus(pc.status || "draft");
          setRevisionReason(pc.revision_reason || "");
          setDocDate(pc.document_date);
          setApproverId(pc.approver_id || "");
          setPurpose(pc.purpose || "");
          setRelatedPR(pc.related_pr_number || "");
          setRemark(pc.remark || "");

          // Group quotes by item, then by display_order. display_order = vendor column index.
          const quotesByItem = new Map();
          (pc.supplier_quotes || []).forEach(q => {
            if (!quotesByItem.has(q.pc_item_id)) quotesByItem.set(q.pc_item_id, []);
            quotesByItem.get(q.pc_item_id).push(q);
          });
          // Determine vendor slots: union of display_order values across all items
          const slotSet = new Set();
          (pc.supplier_quotes || []).forEach(q => slotSet.add(q.display_order ?? 0));
          const slotIndices = Array.from(slotSet).sort((a, b) => a - b);
          // For each slot, first quote we find defines vendor metadata
          const slotToVendor = new Map();
          (pc.supplier_quotes || []).forEach(q => {
            if (!slotToVendor.has(q.display_order ?? 0)) {
              slotToVendor.set(q.display_order ?? 0, q);
            }
          });
          const attsByQuoteId = new Map();
          (pc.attachments || []).forEach(a => { if (a.pc_quote_id) attsByQuoteId.set(a.pc_quote_id, a); });

          const rebuiltVendors = slotIndices.map(slot => {
            const q = slotToVendor.get(slot) || {};
            const att = attsByQuoteId.get(q.id) || null;
            return {
              supplier_id: q.supplier_id || null,
              supplier_name: q.supplier_name || "",
              supplier_code: q.supplier_code || "",
              quotation_number: q.quotation_number || "",
              quotation_date: q.quotation_date || "",
              valid_until_date: q.valid_until_date || "",
              currency: q.currency || "THB",
              exchange_rate_to_thb: q.exchange_rate_to_thb ?? "",
              vat_included: q.vat_included ? 1 : 0,
              lead_time: q.lead_time || "",
              payment_term: q.payment_term || "",
              warranty_term: q.warranty_term || "",
              remark: q.remark || "",
              attachment_id: q.attachment_id || null,
              attachment: att ? { id: att.id, filename: att.filename, file_size: att.file_size } : null,
            };
          });
          // Ensure at least 2 columns visible
          while (rebuiltVendors.length < 2) rebuiltVendors.push(blankVendor());

          const rebuiltItems = (pc.items || []).map(it => {
            const itemQuotes = quotesByItem.get(it.id) || [];
            const prices = slotIndices.map(slot => {
              const q = itemQuotes.find(qq => (qq.display_order ?? 0) === slot);
              if (!q) return { value: "", not_quoted: true };
              return { value: q.quoted_price != null ? String(q.quoted_price) : "", not_quoted: false };
            });
            // Pad with empty placeholders if we added blank vendor columns
            while (prices.length < rebuiltVendors.length) prices.push({ value: "", not_quoted: false });
            let selectedVendorIndex = null;
            if (it.selected_quote_id) {
              const selQ = itemQuotes.find(qq => qq.id === it.selected_quote_id);
              if (selQ) {
                selectedVendorIndex = slotIndices.indexOf(selQ.display_order ?? 0);
                if (selectedVendorIndex < 0) selectedVendorIndex = null;
              }
            }
            return {
              item_master_id: it.item_master_id,
              item_code: it.item_code || "",
              item_name: it.item_name || "",
              description: it.description || "",
              unit: it.unit || "",
              required_quantity: it.required_quantity ?? "",
              line_remark: it.line_remark || "",
              prices,
              selected_vendor_index: selectedVendorIndex,
              selection_reason_code: it.selection_reason_code || "",
              selection_reason_text: it.selection_reason_text || "",
            };
          });
          setVendors(rebuiltVendors);
          setItems(rebuiltItems);
        } catch (e) { toast({ kind:"error", msg: "โหลดเอกสารไม่สำเร็จ" }); }
      }
    })();
    /* eslint-disable-next-line */
  }, [tenant?.id, editPcId]);

  // Lowest THB per item (used to mark "ต่ำสุด" badge)
  const itemThbEqs = items.map(it =>
    it.prices.map((p, vi) => (p && !p.not_quoted && p.value !== "" ? priceThbEquiv(p.value, vendors[vi]) : null))
  );
  const itemLowest = itemThbEqs.map(arr => arr.filter(x => x != null && x > 0).reduce((m, x) => (m == null || x < m) ? x : m, null));

  const totalEstimate = useMemo(() => {
    return items.reduce((sum, it, ii) => {
      if (it.selected_vendor_index == null) return sum;
      const p = it.prices[it.selected_vendor_index];
      const v = vendors[it.selected_vendor_index];
      if (!p || p.not_quoted || p.value === "" || !v) return sum;
      const eq = priceThbEquiv(p.value, v);
      const qty = Number(it.required_quantity) > 0 ? Number(it.required_quantity) : 1;
      return sum + ((eq != null ? eq : 0) * qty);
    }, 0);
  }, [items, vendors]);

  // -------- Vendor column actions --------
  const addVendor = () => {
    if (vendors.length >= 4) { toast({ kind:"warn", msg:"เพิ่มผู้ขายได้สูงสุด 4 ราย" }); return; }
    setVendors(prev => [...prev, blankVendor()]);
    setItems(prev => prev.map(it => ({ ...it, prices: [...it.prices, { value: "", not_quoted: false }] })));
  };
  const updateVendor = (vi, next) => setVendors(prev => prev.map((v, i) => i === vi ? next : v));
  const removeVendor = (vi) => {
    setVendors(prev => prev.filter((_, i) => i !== vi));
    setItems(prev => prev.map(it => {
      let sel = it.selected_vendor_index;
      if (sel === vi) sel = null;
      else if (sel != null && sel > vi) sel -= 1;
      return { ...it, prices: it.prices.filter((_, i) => i !== vi), selected_vendor_index: sel };
    }));
  };

  // -------- Item row actions --------
  const addItem = (masterItem) => {
    setItems(prev => [...prev, {
      ...blankItem(vendors.length),
      item_master_id: masterItem.id,
      item_code: masterItem.item_code,
      item_name: masterItem.name,
      description: masterItem.description || "",
      unit: masterItem.unit || "",
    }]);
    setAddItemOpen(false);
  };
  const removeItem = (idx) => setItems(prev => prev.filter((_, i) => i !== idx));
  const updateItem = (idx, next) => setItems(prev => prev.map((it, i) => i === idx ? next : it));
  const updatePrice = (itemIdx, vendorIdx, next) => setItems(prev => prev.map((it, i) => {
    if (i !== itemIdx) return it;
    const np = [...it.prices];
    np[vendorIdx] = next;
    return { ...it, prices: np };
  }));

  // -------- Attachment per vendor --------
  // Uploaded with no pc_quote_id; we will write attachment_id onto the vendor row,
  // and the save payload propagates that attachment_id to every quote we emit for that vendor.
  const uploadVendorAttachment = async (vi, file) => {
    if (!pcId) { toast({ kind:"warn", msg:"กรุณาบันทึกแบบร่างก่อน แล้วจึงแนบไฟล์ได้" }); return; }
    // Pre-flight client-side checks — fail fast before sending the request
    if (!file) { toast({ kind:"warn", msg:"ไม่พบไฟล์" }); return; }
    if (file.size > 10 * 1024 * 1024) {
      toast({ kind:"warn", msg:`ไฟล์ ${file.name} ใหญ่เกิน 10MB · ขนาดจริง ${(file.size/1024/1024).toFixed(1)}MB` });
      return;
    }
    const ext = (file.name.split(".").pop() || "").toLowerCase();
    if (!["pdf","jpg","jpeg","png","doc","docx","xls","xlsx"].includes(ext)) {
      toast({ kind:"warn", msg:"ชนิดไฟล์ไม่รองรับ" });
      return;
    }

    // Per-vendor busy — keeps other vendor buttons interactive
    setUploadingByVendor(prev => ({ ...prev, [vi]: file.name }));
    try {
      const res = await window.apiClient.pcUploadAttachment(tenant.id, pcId, file);
      // Functional update so we don't overwrite concurrent edits to vendor metadata
      setVendors(prev => prev.map((vendor, i) => i === vi
        ? { ...vendor, attachment: { id: res.id, filename: res.filename, file_size: res.file_size }, attachment_id: res.id }
        : vendor));
      toast({ kind:"success", msg:`อัปโหลด ${res.filename} แล้ว` });
    } catch (e) { toast({ kind:"error", msg: e.message || "อัปโหลดไม่สำเร็จ" }); }
    finally {
      setUploadingByVendor(prev => { const n = { ...prev }; delete n[vi]; return n; });
    }
  };
  const removeVendorAttachment = async (vi) => {
    // Capture the attachment id from current state, then clear it via functional update so
    // concurrent edits to other fields are not overwritten by a stale snapshot.
    const v = vendors[vi];
    if (v?.attachment?.id && pcId) {
      try { await window.apiClient.pcDeleteAttachment(tenant.id, pcId, v.attachment.id); } catch {}
    }
    setVendors(prev => prev.map((vendor, i) => i === vi ? { ...vendor, attachment: null, attachment_id: null } : vendor));
  };

  // -------- Build server payload (vendors+prices → per-item quotes array) --------
  const buildPayload = (submit = false) => ({
    submit,
    document_date: docDate,
    approver_id: approverId || null,
    purpose, related_pr_number: relatedPR, remark,
    items: items.map((it, ii) => {
      // For each vendor column with a non-empty price (and not "not_quoted"),
      // emit a quote carrying the vendor's metadata + this item's price.
      const quotes = [];
      let selectedQuoteIndex = null;
      vendors.forEach((v, vi) => {
        const p = it.prices[vi];
        const include = v.supplier_name && p && !p.not_quoted && p.value !== "";
        if (!include) return;
        if (it.selected_vendor_index === vi) selectedQuoteIndex = quotes.length;
        quotes.push({
          supplier_id: v.supplier_id || null,
          supplier_code_snapshot: v.supplier_id ? v.supplier_code : null,
          supplier_name_snapshot: v.supplier_id ? v.supplier_name : null,
          supplier_name: v.supplier_name,
          supplier_code: v.supplier_code,
          quotation_number: v.quotation_number,
          quotation_date: v.quotation_date,
          valid_until_date: v.valid_until_date,
          currency: v.currency,
          exchange_rate_to_thb: v.exchange_rate_to_thb === "" ? null : Number(v.exchange_rate_to_thb),
          vat_included: v.vat_included,
          lead_time: v.lead_time, payment_term: v.payment_term, warranty_term: v.warranty_term, remark: v.remark,
          quoted_price: Number(p.value),
          attachment_id: v.attachment_id || null,
        });
      });
      return {
        item_master_id: it.item_master_id,
        item_code: it.item_code,
        item_name: it.item_name,
        description: it.description,
        unit: it.unit,
        required_quantity: it.required_quantity === "" ? null : Number(it.required_quantity),
        line_remark: it.line_remark,
        display_order: ii,
        quotes,
        selected_quote_index: selectedQuoteIndex,
        selection_reason_code: it.selection_reason_code,
        selection_reason_text: it.selection_reason_text,
      };
    }),
  });

  const saveDraft = async () => {
    if (items.length === 0) { toast({ kind:"warn", msg:"กรุณาเพิ่มสินค้าอย่างน้อย 1 รายการ" }); return; }
    setSaving(true);
    try {
      if (pcId) {
        await window.apiClient.pcUpdateDocument(tenant.id, pcId, buildPayload(false));
        toast({ kind:"success", msg:`บันทึกแบบร่าง ${pcNumber} แล้ว` });
      } else {
        const res = await window.apiClient.pcCreateDocument(tenant.id, buildPayload(false));
        setPcId(res.id); setPcNumber(res.pc_number);
        toast({ kind:"success", msg:`บันทึกแบบร่าง ${res.pc_number} แล้ว · แนบไฟล์ใบเสนอราคาได้แล้ว` });
      }
    } catch (e) { toast({ kind:"error", msg: e.message || "บันทึกไม่สำเร็จ" }); }
    finally { setSaving(false); }
  };

  const submitForApproval = async () => {
    if (items.length === 0) { toast({ kind:"warn", msg:"ต้องมีอย่างน้อย 1 รายการ" }); return; }
    if (!approverId) { toast({ kind:"warn", msg:"กรุณาเลือกผู้อนุมัติ" }); return; }
    for (const it of items) {
      if (!it.item_code) { toast({ kind:"warn", msg:"มีรายการที่ยังไม่ได้เลือกสินค้า" }); return; }
      // Count vendors that have a price for this item
      const quoted = vendors.filter((v, vi) => v.supplier_name && it.prices[vi] && !it.prices[vi].not_quoted && it.prices[vi].value !== "");
      if (quoted.length === 0) { toast({ kind:"warn", msg:`${it.item_name} ต้องมีอย่างน้อย 1 ใบเสนอราคา` }); return; }
      if (it.selected_vendor_index == null) { toast({ kind:"warn", msg:`${it.item_name} ต้องเลือกผู้ขาย` }); return; }
      const selV = vendors[it.selected_vendor_index];
      const selP = it.prices[it.selected_vendor_index];
      if (!selV || !selV.supplier_name) { toast({ kind:"warn", msg:`${it.item_name} ผู้ขายที่เลือกยังไม่กรอกข้อมูล` }); return; }
      if (!selV.valid_until_date) { toast({ kind:"warn", msg:`${selV.supplier_name}: กรุณากรอกวันยืนราคา` }); return; }
      if (!(Number(selP.value) > 0)) { toast({ kind:"warn", msg:`${it.item_name} ราคาเสนอต้องมากกว่า 0` }); return; }
      if (!(it.selection_reason_text||"").trim()) { toast({ kind:"warn", msg:`${it.item_name} ต้องระบุเหตุผลการเลือก` }); return; }
    }

    setSubmitting(true);
    try {
      if (pcId) {
        await window.apiClient.pcUpdateDocument(tenant.id, pcId, buildPayload(false));
        await window.apiClient.pcSubmitDocument(tenant.id, pcId);
        toast({ kind:"success", msg:`ส่งขออนุมัติ ${pcNumber} สำเร็จ` });
      } else {
        const res = await window.apiClient.pcCreateDocument(tenant.id, buildPayload(true));
        toast({ kind:"success", msg:`ส่งขออนุมัติ ${res.pc_number} สำเร็จ` });
      }
      onDone();
    } catch (e) { toast({ kind:"error", msg: e.message || "ส่งขออนุมัติไม่สำเร็จ" }); }
    finally { setSubmitting(false); }
  };

  const approverList = users.filter(u => u.id !== currentUser?.id);

  return (
    <div className="p-6 max-w-[1400px] mx-auto">
      <button onClick={onCancel} className="flex items-center gap-1 text-[12.5px] text-ink-600 hover:text-ink-900 mb-3">
        <Icon name="chevronRight" size={14} className="rotate-180"/> ย้อนกลับ
      </button>
      <div className="text-[11px] tracking-wider text-emerald-700 font-bold mb-1">
        ↕ {pcId ? "รายละเอียดเอกสาร" : "สร้างเอกสารใหม่"}{pcNumber ? ` · ${pcNumber}` : ""}
      </div>
      <div className="flex items-start justify-between gap-3 mb-4">
        <div>
          <div className="text-[24px] font-bold text-ink-900 leading-tight">
            {purpose || (pcId ? "แก้ไขเปรียบเทียบราคา" : "สร้างเปรียบเทียบราคา")}
          </div>
          {pcId && (
            <div className="flex items-center gap-2 mt-1">
              <PCStatusPill status={pcStatus}/>
              <span className="text-[11.5px] text-ink-500">เลขที่เอกสาร <span className="font-mono font-semibold text-emerald-700">{pcNumber}</span></span>
            </div>
          )}
        </div>
        <div className="flex items-center gap-2 shrink-0">
          <Button variant="secondary" icon="check" onClick={saveDraft} disabled={saving || submitting}>
            {saving ? "กำลังบันทึก…" : "บันทึกแบบร่าง"}
          </Button>
          <Button variant="primary" icon="send" onClick={submitForApproval} disabled={saving || submitting}>
            {submitting ? "กำลังส่ง…" : "ส่งขออนุมัติ"}
          </Button>
        </div>
      </div>

      {/* Revision banner — shown when editing a PC that the approver sent back */}
      {pcStatus === "revision" && (
        <div className="bg-amber-50 border-2 border-amber-300 rounded-2xl p-4 mb-4">
          <div className="flex items-start gap-3">
            <div className="w-9 h-9 rounded-xl bg-amber-200 text-amber-800 flex items-center justify-center shrink-0">
              <Icon name="warning" size={16}/>
            </div>
            <div className="flex-1 min-w-0">
              <div className="text-[14px] font-bold text-amber-900">ผู้อนุมัติขอให้แก้ไขเอกสารนี้</div>
              <div className="text-[12.5px] text-amber-800 mt-0.5">
                หลังแก้ไขเสร็จ กดปุ่ม <strong>"ส่งขออนุมัติ"</strong> เพื่อส่งกลับให้ผู้อนุมัติพิจารณาอีกครั้ง
              </div>
              {revisionReason && (
                <div className="mt-2 bg-white border border-amber-200 rounded-lg p-2.5">
                  <div className="text-[10.5px] uppercase tracking-wider text-amber-700 font-semibold mb-0.5">เหตุผลที่ขอแก้ไข</div>
                  <div className="text-[12.5px] text-ink-800 italic whitespace-pre-line">"{revisionReason}"</div>
                </div>
              )}
            </div>
          </div>
        </div>
      )}

      {/* HEADER */}
      <div className="bg-white border border-ink-200 rounded-2xl p-5 mb-4">
        <div className="flex items-center gap-2 mb-3">
          <div className="w-7 h-7 rounded-lg bg-emerald-100 text-emerald-700 flex items-center justify-center">
            <Icon name="fileText" size={14}/>
          </div>
          <div>
            <div className="text-[14px] font-bold text-ink-900">ข้อมูลเอกสาร</div>
            <div className="text-[11px] text-ink-500">Header Information</div>
          </div>
          <span className="ml-auto"><PCStatusPill status="draft"/></span>
        </div>
        <div className="grid grid-cols-1 md:grid-cols-3 gap-3">
          <Field label="เลขที่เอกสาร">
            <div className="h-9 px-3 flex items-center text-[13px] font-mono text-ink-700 bg-ink-50 border border-ink-200 rounded-lg">
              {pcNumber || "สร้างอัตโนมัติ"}
            </div>
          </Field>
          <Field label="วันที่เอกสาร">
            <ThaiDateInput value={docDate} onChange={v => setDocDate(v)}/>
          </Field>
          <Field label="ผู้สร้าง">
            <div className="h-9 px-3 flex items-center gap-2 text-[13px] text-ink-700 bg-ink-50 border border-ink-200 rounded-lg">
              <Avatar name={currentUser?.full_name} size={20}/>
              <span className="truncate">{currentUser?.full_name || currentUser?.email}</span>
            </div>
          </Field>
          <Field label="วัตถุประสงค์ / Purpose" className="md:col-span-2">
            <Textarea rows={1} value={purpose} onChange={e => setPurpose(e.target.value)} placeholder="ระบุวัตถุประสงค์การจัดซื้อ"/>
          </Field>
          <Field label="เลขที่ PR ที่เกี่ยวข้อง" hint="ไม่บังคับ">
            <Input value={relatedPR} onChange={e => setRelatedPR(e.target.value)} placeholder="เช่น PR-2026-00012"/>
          </Field>
          <Field label="ผู้อนุมัติ" required hint={`${approverList.length} คน`} className="md:col-span-2">
            <SearchableUserPicker users={approverList} value={approverId} onChange={setApproverId}/>
          </Field>
          <Field label="หมายเหตุ">
            <Input value={remark} onChange={e => setRemark(e.target.value)} placeholder="หมายเหตุเพิ่มเติม"/>
          </Field>
        </div>
      </div>

      {/* COMPARISON TABLE */}
      <div className="bg-white border border-ink-200 rounded-2xl p-5">
        <div className="flex items-center gap-2 mb-3">
          <div className="w-7 h-7 rounded-lg bg-emerald-100 text-emerald-700 flex items-center justify-center">
            <Icon name="fileBarChart" size={14}/>
          </div>
          <div className="flex-1">
            <div className="text-[14px] font-bold text-ink-900">ตารางเปรียบเทียบราคา</div>
            <div className="text-[11px] text-ink-500">{items.length} สินค้า · {vendors.filter(v => v.supplier_name).length} ผู้ขาย</div>
          </div>
          <div className="flex items-center gap-2">
            {vendors.length < 4 && (
              <Button variant="soft" icon="plus" size="sm" onClick={addVendor}>เพิ่มผู้ขาย</Button>
            )}
            <Button variant="primary" icon="plus" size="sm" onClick={() => setAddItemOpen(true)}>เพิ่มสินค้า</Button>
          </div>
        </div>

        {items.length === 0 ? (
          <div className="rounded-xl border-2 border-dashed border-ink-200 bg-ink-50/40 p-10 text-center">
            <div className="w-12 h-12 mx-auto mb-2 rounded-xl bg-ink-100 text-ink-400 flex items-center justify-center">
              <Icon name="package" size={22}/>
            </div>
            <div className="text-[14px] font-semibold text-ink-700">ยังไม่มีสินค้าในเอกสาร</div>
            <div className="text-[12px] text-ink-500 mt-1">เริ่มต้นโดยกด “+ เพิ่มสินค้า” แล้วเลือกจาก Item Master</div>
            <div className="mt-3"><Button variant="primary" icon="plus" onClick={() => setAddItemOpen(true)}>+ เพิ่มสินค้า</Button></div>
          </div>
        ) : (
          <div className="overflow-x-auto thin-scroll rounded-xl border border-ink-100">
            <table className="w-full text-[12.5px] border-collapse">
              <thead className="bg-ink-50/70">
                <tr>
                  <th className="text-left px-3 py-2 font-semibold text-ink-700 min-w-[260px] sticky left-0 bg-ink-50/70 z-10">สินค้า / ผู้ขาย</th>
                  {vendors.map((v, vi) => (
                    <VendorColumnHeader key={vi} vendor={v} vendorIdx={vi} onEdit={() => setEditVendorIdx(vi)}/>
                  ))}
                  <th className="text-left px-3 py-2 font-semibold text-emerald-700 border-l border-emerald-200 bg-emerald-50/40 min-w-[170px]">ผู้ขายที่เลือก</th>
                </tr>
              </thead>
              <tbody>
                {items.map((it, ii) => {
                  const lowestEq = itemLowest[ii];
                  return (
                    <React.Fragment key={ii}>
                      <tr className="border-t border-ink-100">
                        <td className="px-3 py-2 align-top sticky left-0 bg-white z-10">
                          <div className="flex items-start justify-between gap-2">
                            <div className="min-w-0">
                              <div className="text-[10.5px] text-ink-500 font-mono">#{ii + 1} · {it.item_code}</div>
                              <div className="text-[13px] font-semibold text-ink-900 truncate">{it.item_name}</div>
                              {it.description && <div className="text-[11px] text-ink-500 truncate max-w-[260px]">{it.description}</div>}
                              <div className="text-[11px] text-ink-500 mt-0.5 flex items-center gap-2">
                                {it.unit && <span>หน่วย: {it.unit}</span>}
                                <input type="number" step="0.01" min="0" value={it.required_quantity}
                                  onChange={e => updateItem(ii, { ...it, required_quantity: e.target.value.replace(/^0+(\d)/, "$1") })}
                                  placeholder="จำนวน"
                                  className="w-[80px] h-6 px-1.5 text-[11px] border border-ink-200 rounded font-mono"/>
                              </div>
                            </div>
                            <Tip tip="ลบสินค้า"><button onClick={() => removeItem(ii)}
                              className="shrink-0 p-1 rounded-md text-rose-500 hover:bg-rose-50"><Icon name="trash" size={12}/></button></Tip>
                          </div>
                        </td>
                        {vendors.map((v, vi) => {
                          const p = it.prices[vi] || { value: "", not_quoted: false };
                          const eq = itemThbEqs[ii][vi];
                          const isLowest = lowestEq != null && eq != null && Math.abs(eq - lowestEq) < 0.001;
                          const isSelected = it.selected_vendor_index === vi;
                          return (
                            <PriceCellInput
                              key={vi}
                              price={p} vendor={v}
                              isSelected={isSelected} isLowest={isLowest}
                              hasVendorInfo={!!v.supplier_name}
                              onChange={(np) => updatePrice(ii, vi, np)}
                              onToggleNotQuoted={() => updatePrice(ii, vi, { value: "", not_quoted: !p.not_quoted })}
                              onSelect={() => updateItem(ii, { ...it, selected_vendor_index: vi })}
                            />
                          );
                        })}
                        <SelectedSupplierCell
                          item={it} vendors={vendors}
                          onClearSelection={() => updateItem(ii, { ...it, selected_vendor_index: null, selection_reason_code: "", selection_reason_text: "" })}
                        />
                      </tr>
                      <ItemReasonInline
                        item={it} vendors={vendors} vendorCount={vendors.length}
                        onChange={(next) => updateItem(ii, next)}
                      />
                    </React.Fragment>
                  );
                })}
              </tbody>
            </table>
          </div>
        )}
      </div>

      {/* Vendor attachments */}
      <VendorAttachmentSection
        vendors={vendors}
        pcId={pcId}
        uploadingByVendor={uploadingByVendor}
        onAttach={uploadVendorAttachment}
        onRemoveAttach={removeVendorAttachment}
        onRequestSaveDraft={saveDraft}
      />

      {/* Footer total + sticky actions */}
      <div className="mt-4 flex items-center justify-between bg-white border border-ink-200 rounded-2xl px-5 py-3">
        <div>
          <div className="text-[11px] text-ink-500">มูลค่าประมาณการ (รายการที่เลือกแล้ว)</div>
          <div className="text-[22px] font-bold text-ink-900 font-mono">{formatMoneyTHB(totalEstimate)}</div>
        </div>
        <div className="flex items-center gap-2">
          <Button variant="secondary" icon="check" onClick={saveDraft} disabled={saving || submitting}>
            {saving ? "กำลังบันทึก…" : "บันทึกแบบร่าง"}
          </Button>
          <Button variant="primary" icon="send" onClick={submitForApproval} disabled={saving || submitting}>
            {submitting ? "กำลังส่ง…" : "ส่งขออนุมัติ"}
          </Button>
        </div>
      </div>

      {/* Modals */}
      <VendorDetailsModal
        open={editVendorIdx != null}
        onClose={() => setEditVendorIdx(null)}
        vendor={editVendorIdx != null ? vendors[editVendorIdx] : null}
        vendorIdx={editVendorIdx ?? 0}
        tenant={tenant}
        onSave={(nv) => {
          // Functional update so we use the CURRENT vendors[idx] (e.g. attachment set by a
          // concurrent upload) — not the stale snapshot captured at modal-open time.
          setVendors(prev => prev.map((vendor, i) => i === editVendorIdx ? { ...vendor, ...nv } : vendor));
        }}
        onRemove={vendors.length > 1 ? () => removeVendor(editVendorIdx) : null}
      />
      <AddItemFromMasterModal
        open={addItemOpen}
        onClose={() => setAddItemOpen(false)}
        masterItems={masterItems}
        existingItemMasterIds={new Set(items.map(it => it.item_master_id).filter(Boolean))}
        onPick={(mi) => addItem(mi)}
      />
    </div>
  );
};

// =============================================================================
// PAGE 3 — DETAIL MODAL (read + workflow actions)
// =============================================================================
const PriceComparisonDetailModal = ({ pcId, tenant, currentUser, currentRole, users, onClose, onChanged }) => {
  const toast = useToast();
  const [pc, setPc] = useState(null);
  const [loading, setLoading] = useState(true);
  const [approveOpen, setApproveOpen] = useState(false);
  const [reviseOpen, setReviseOpen] = useState(false);
  const [rejectOpen, setRejectOpen] = useState(false);
  const [cancelOpen, setCancelOpen] = useState(false);
  const [approveComment, setApproveComment] = useState("");

  const load = async () => {
    if (!tenant?.id || !window.apiClient || !pcId) return;
    setLoading(true);
    try {
      const data = await window.apiClient.pcGetDocument(tenant.id, pcId);
      setPc(data);
    } catch (e) { toast({ kind:"error", msg: e.message || "โหลดเอกสารไม่สำเร็จ" }); }
    finally { setLoading(false); }
  };
  useEffect(() => { load(); /* eslint-disable-next-line */ }, [pcId]);

  if (!pcId) return null;

  const isApprover = pc && pc.approver_id === currentUser?.id;
  const isOwner = pc && pc.created_by_user_id === currentUser?.id;
  const isAdmin = isTenantAdminRole(currentRole);

  const doApprove = async () => {
    try {
      await window.apiClient.pcApproveDocument(tenant.id, pcId, approveComment);
      toast({ kind:"success", msg:"อนุมัติเอกสารสำเร็จ · สร้างราคาที่อนุมัติแล้วในประวัติ" });
      setApproveOpen(false); setApproveComment("");
      onChanged?.(); load();
    } catch (e) { toast({ kind:"error", msg: e.message || "อนุมัติไม่สำเร็จ" }); }
  };
  const doRevise = async (reason) => {
    try {
      await window.apiClient.pcRequestRevision(tenant.id, pcId, reason);
      toast({ kind:"success", msg:"ส่งคำขอแก้ไขกลับให้ผู้สร้างแล้ว" });
      onChanged?.(); load();
    } catch (e) { toast({ kind:"error", msg: e.message || "ส่งคำขอแก้ไขไม่สำเร็จ" }); }
  };
  const doReject = async (reason) => {
    try {
      await window.apiClient.pcRejectDocument(tenant.id, pcId, reason);
      toast({ kind:"warn", msg:"ปฏิเสธเอกสารแล้ว" });
      onChanged?.(); load();
    } catch (e) { toast({ kind:"error", msg: e.message || "ปฏิเสธไม่สำเร็จ" }); }
  };
  const doCancel = async (reason) => {
    try {
      await window.apiClient.pcCancelDocument(tenant.id, pcId, reason);
      toast({ kind:"warn", msg:"ยกเลิกเอกสารแล้ว" });
      onChanged?.(); load();
    } catch (e) { toast({ kind:"error", msg: e.message || "ยกเลิกไม่สำเร็จ" }); }
  };

  const footer = pc ? (
    <div className="flex items-center justify-between w-full">
      <div className="text-[11.5px] text-ink-500">อัปเดตล่าสุด {formatDateTime(pc.updated_at)}</div>
      <div className="flex items-center gap-2">
        {(isOwner || isAdmin) && pc.status !== "approved" && pc.status !== "cancelled" && (
          <Button variant="ghost" icon="ban" onClick={() => setCancelOpen(true)}>ยกเลิก PC</Button>
        )}
        {(isApprover || isAdmin) && pc.status === "pending" && (
          <>
            <Button variant="danger" icon="x" onClick={() => setRejectOpen(true)}>ปฏิเสธ</Button>
            <Button variant="warn" icon="history" onClick={() => setReviseOpen(true)}>ขอแก้ไข</Button>
            <Button variant="success" icon="check" onClick={() => setApproveOpen(true)}>อนุมัติ</Button>
          </>
        )}
      </div>
    </div>
  ) : null;

  return (
    <Modal open={!!pcId} onClose={onClose} width={1100}
      title={pc ? `${pc.pc_number} · ${pc.purpose || "เปรียบเทียบราคา"}` : "กำลังโหลด…"}
      subtitle={pc ? `วันที่เอกสาร ${formatDate(pc.document_date)}` : null}
      footer={footer}
    >
      {loading || !pc ? (
        <div className="py-10 text-center text-ink-500 text-sm">กำลังโหลด…</div>
      ) : (
        <PCDetailBody pc={pc} users={users} tenant={tenant}/>
      )}

      {/* Approve confirmation */}
      <Modal open={approveOpen} onClose={() => setApproveOpen(false)} title="ยืนยันการอนุมัติ"
        subtitle={`เอกสาร ${pc?.pc_number || ""} · จะสร้างราคาที่อนุมัติแล้วในประวัติ`}
        footer={<>
          <Button variant="secondary" onClick={() => setApproveOpen(false)}>ยกเลิก</Button>
          <Button variant="success" icon="check" onClick={doApprove}>ยืนยันอนุมัติ</Button>
        </>}>
        <Field label="ความเห็นการอนุมัติ" hint="ไม่บังคับ">
          <Textarea rows={3} value={approveComment} onChange={e => setApproveComment(e.target.value)}
            placeholder="เช่น อนุมัติ — ราคาเหมาะสมตามวงเงิน"/>
        </Field>
      </Modal>

      <ReasonModal open={reviseOpen} onClose={() => setReviseOpen(false)} onConfirm={doRevise}
        title="ขอแก้ไขเอกสาร" subtitle="กรุณาระบุสิ่งที่ต้องแก้ไข"
        confirmLabel="ส่งขอแก้ไข" tone="warn" placeholder="เช่น ขอให้ต่อรองราคากับเจ้าที่ 2 อีกครั้ง"/>
      <ReasonModal open={rejectOpen} onClose={() => setRejectOpen(false)} onConfirm={doReject}
        title="ปฏิเสธเอกสาร" subtitle="กรุณาระบุเหตุผล" placeholder="ระบุเหตุผลที่ปฏิเสธ"/>
      <ReasonModal open={cancelOpen} onClose={() => setCancelOpen(false)} onConfirm={doCancel}
        title="ยกเลิกเอกสาร" subtitle="กรุณาระบุเหตุผล" placeholder="เช่น โครงการถูกยกเลิก"/>
    </Modal>
  );
};

const PCDetailBody = ({ pc, users, tenant }) => {
  // Index supplier_quotes by pc_item_id
  const quotesByItem = useMemo(() => {
    const m = new Map();
    (pc.supplier_quotes || []).forEach(q => {
      if (!m.has(q.pc_item_id)) m.set(q.pc_item_id, []);
      m.get(q.pc_item_id).push(q);
    });
    return m;
  }, [pc]);

  const attsByQuote = useMemo(() => {
    const m = new Map();
    (pc.attachments || []).forEach(a => { if (a.pc_quote_id) m.set(a.pc_quote_id, a); });
    return m;
  }, [pc]);

  // Secondary index by attachment id — used for the case where the quote stores
  // attachment_id directly but the attachment row's pc_quote_id is NULL (this
  // happens for vendor-level attachments uploaded once per vendor and shared
  // across all that vendor's quotes).
  const attsById = useMemo(() => {
    const m = new Map();
    (pc.attachments || []).forEach(a => m.set(a.id, a));
    return m;
  }, [pc]);

  const totalEstimate = pc.total_estimate_thb || 0;
  const totalItems = (pc.items || []).length;
  const selectedCount = (pc.items || []).filter(it => it.selected_quote_id).length;

  return (
    <div className="space-y-4">
      {/* Header info */}
      <div className="grid grid-cols-1 lg:grid-cols-3 gap-4">
        <div className="lg:col-span-2 bg-white border border-ink-100 rounded-xl p-4">
          <div className="flex items-center gap-2 mb-3">
            <div className="w-7 h-7 rounded-lg bg-emerald-100 text-emerald-700 flex items-center justify-center">
              <Icon name="fileText" size={14}/>
            </div>
            <div className="text-[14px] font-bold text-ink-900">ข้อมูลเอกสาร</div>
            <PCStatusPill status={pc.status}/>
          </div>
          <div className="grid grid-cols-2 md:grid-cols-3 gap-3 text-[12.5px]">
            <div><div className="text-[11px] text-ink-500">เลขที่เอกสาร</div><div className="font-mono font-semibold text-emerald-700">{pc.pc_number}</div></div>
            <div><div className="text-[11px] text-ink-500">วันที่เอกสาร</div><div>{formatDate(pc.document_date)}</div></div>
            <div><div className="text-[11px] text-ink-500">ผู้สร้าง</div><div>{displayUserName(users, pc.created_by_user_id)}</div></div>
            <div><div className="text-[11px] text-ink-500">ส่งถึง (ผู้อนุมัติ)</div><div>{displayUserName(users, pc.approver_id)}</div></div>
            <div><div className="text-[11px] text-ink-500">เลขที่ PR ที่เกี่ยวข้อง</div><div className="font-mono">{pc.related_pr_number || "—"}</div></div>
            <div><div className="text-[11px] text-ink-500">รายการสินค้า / เลือกแล้ว</div><div>{selectedCount} / {totalItems}</div></div>
            <div className="md:col-span-3"><div className="text-[11px] text-ink-500">วัตถุประสงค์ / Purpose</div><div className="whitespace-pre-line">{pc.purpose || "—"}</div></div>
            {pc.remark && <div className="md:col-span-3"><div className="text-[11px] text-ink-500">หมายเหตุ</div><div className="whitespace-pre-line">{pc.remark}</div></div>}
          </div>
        </div>
        <div className="bg-white border border-ink-100 rounded-xl p-4 flex flex-col">
          <div className="text-[11px] text-ink-500">มูลค่าประมาณการรวม</div>
          <div className="text-[26px] font-bold text-ink-900 font-mono">{formatMoneyTHB(totalEstimate)}</div>
          <div className="mt-2 text-[11.5px] text-ink-600 flex justify-between"><span>จำนวนสินค้า</span><span className="font-semibold">{totalItems} รายการ</span></div>
          <div className="text-[11.5px] text-ink-600 flex justify-between"><span>เลือกผู้ขายแล้ว</span><span className="font-semibold">{selectedCount} / {totalItems}</span></div>
          {pc.status === "approved" && <div className="text-[11px] text-emerald-700 mt-2">อนุมัติเมื่อ {formatDateTime(pc.approved_at)} โดย {displayUserName(users, pc.approved_by_user_id)}</div>}
          {pc.status === "rejected" && <div className="text-[11px] text-rose-700 mt-2">ปฏิเสธ: {pc.rejected_reason || "—"}</div>}
          {pc.status === "revision" && <div className="text-[11px] text-amber-700 mt-2">ขอแก้ไข: {pc.revision_reason || "—"}</div>}
          {pc.status === "cancelled" && <div className="text-[11px] text-slate-600 mt-2">ยกเลิก: {pc.cancelled_reason || "—"}</div>}
        </div>
      </div>

      {/* Items + supplier comparison */}
      <div className="bg-white border border-ink-100 rounded-xl p-4">
        <div className="flex items-center gap-2 mb-3">
          <Icon name="package" size={14} className="text-emerald-700"/>
          <div className="text-[14px] font-bold text-ink-900">รายการสินค้าและการเปรียบเทียบ</div>
          <div className="text-[11.5px] text-ink-500">{totalItems} รายการ</div>
        </div>
        <div className="space-y-3">
          {(pc.items || []).map((it, idx) => (
            <PCItemComparisonRow key={it.id} item={it} idx={idx} quotes={quotesByItem.get(it.id) || []}
              attsByQuote={attsByQuote} attsById={attsById} pc={pc} tenant={tenant}/>
          ))}
        </div>
      </div>

      {/* Audit log */}
      <div className="bg-white border border-ink-100 rounded-xl p-4">
        <div className="flex items-center gap-2 mb-3">
          <Icon name="history" size={14} className="text-emerald-700"/>
          <div className="text-[14px] font-bold text-ink-900">ประวัติการดำเนินการ</div>
        </div>
        <div className="space-y-2">
          {(pc.audit_log || []).length === 0 && <div className="text-[12px] text-ink-500">ไม่มีรายการ</div>}
          {(pc.audit_log || []).map(a => {
            const actMeta = {
              created:            { color:"slate",   label:"สร้างเอกสาร" },
              edited:             { color:"slate",   label:"แก้ไข" },
              submitted:          { color:"amber",   label:"ส่งขออนุมัติ" },
              resubmitted:        { color:"amber",   label:"ส่งอีกครั้ง" },
              approved:           { color:"emerald", label:"อนุมัติแล้ว" },
              rejected:           { color:"rose",    label:"ปฏิเสธ" },
              revision_requested: { color:"amber",   label:"ขอแก้ไข" },
              cancelled:          { color:"slate",   label:"ยกเลิก" },
              comment:            { color:"slate",   label:"ความเห็น" },
            }[a.action] || { color:"slate", label: a.action };
            return (
              <div key={a.id} className="flex items-start gap-2 py-2 border-b border-ink-50 last:border-b-0">
                <Badge color={actMeta.color}>{actMeta.label}</Badge>
                <div className="flex-1 min-w-0">
                  <div className="text-[12px] text-ink-700">{a.comment || "—"}</div>
                  <div className="text-[11px] text-ink-500">
                    {formatDateTime(a.performed_at)} · {a.performed_by_name || displayUserName(users, a.performed_by)}
                  </div>
                </div>
              </div>
            );
          })}
        </div>
      </div>
    </div>
  );
};

const PCItemComparisonRow = ({ item, idx, quotes, attsByQuote, attsById, pc, tenant }) => {
  const selected = quotes.find(q => q.id === item.selected_quote_id);

  // Lowest THB price among quotes (for hint)
  const lowestThb = quotes.map(q => Number(q.price_thb_equivalent != null ? q.price_thb_equivalent : (q.currency === "THB" ? q.quoted_price : null)))
    .filter(v => v != null && v > 0)
    .reduce((min, v) => (min == null || v < min) ? v : min, null);

  const reasonMeta = PC_SELECTION_REASONS.find(r => r.code === item.selection_reason_code);

  return (
    <div className="rounded-xl border border-ink-200 overflow-hidden">
      <div className="bg-ink-50/60 px-4 py-2 flex items-center gap-3">
        <span className="font-mono text-[12px] font-bold text-emerald-700">#{idx + 1}</span>
        <span className="font-mono text-[12px] font-bold text-emerald-700">{item.item_code || "—"}</span>
        <span className="text-[13px] font-semibold text-ink-900 truncate">{item.item_name}</span>
        {item.unit && <span className="text-[11px] text-ink-500">หน่วย: {item.unit}</span>}
        {item.required_quantity != null && <span className="text-[11px] text-ink-500">จำนวน {item.required_quantity}</span>}
        <span className="ml-auto text-[11px] text-ink-500">{quotes.length} ใบเสนอราคา · {lowestThb != null ? `ต่ำสุด ${formatMoneyTHB(lowestThb)}` : ""}</span>
      </div>
      <div className="overflow-x-auto thin-scroll">
        <table className="w-full text-[12.5px]">
          <thead className="bg-white text-ink-500 text-[11px]">
            <tr className="border-b border-ink-100">
              <th className="text-left px-3 py-2 font-semibold w-[140px]">คุณสมบัติ</th>
              {quotes.map((q, qi) => {
                const isSel = item.selected_quote_id === q.id;
                const thb = q.price_thb_equivalent != null ? q.price_thb_equivalent : (q.currency === "THB" ? q.quoted_price : null);
                const isLowest = lowestThb != null && thb != null && Math.abs(thb - lowestThb) < 0.001;
                return (
                  <th key={q.id} className={`text-left px-3 py-2 font-semibold ${isSel ? "bg-emerald-50" : ""}`}>
                    <div className="flex items-center gap-1.5">
                      <span className="text-ink-800">{q.supplier_name}</span>
                      {isLowest && <Badge color="emerald">ต่ำสุด</Badge>}
                      {isSel && <Badge color="emerald">ที่เลือก</Badge>}
                    </div>
                  </th>
                );
              })}
            </tr>
          </thead>
          <tbody>
            <PCRow label="ราคาเสนอ" cells={quotes.map(q => (
              <span className={`font-mono font-bold ${item.selected_quote_id === q.id ? "text-emerald-700" : "text-ink-800"}`}>
                {formatMoney(q.quoted_price, q.currency)} <span className="text-[10px] text-ink-500">{q.currency}</span>
              </span>
            ))} item={item} quotes={quotes}/>
            <PCRow label="เทียบ THB" cells={quotes.map(q => {
              const thb = q.price_thb_equivalent != null ? q.price_thb_equivalent : (q.currency === "THB" ? q.quoted_price : null);
              return thb != null ? formatMoneyTHB(thb) : <span className="text-ink-400">—</span>;
            })} item={item} quotes={quotes}/>
            <PCRow label="รหัสผู้ขาย" cells={quotes.map(q => q.supplier_code || "—")} item={item} quotes={quotes}/>
            <PCRow label="เลขที่ใบเสนอราคา" cells={quotes.map(q => q.quotation_number || "—")} item={item} quotes={quotes}/>
            <PCRow label="วันที่เสนอราคา" cells={quotes.map(q => formatDate(q.quotation_date))} item={item} quotes={quotes}/>
            <PCRow label="ภาษี (VAT)" cells={quotes.map(q => q.vat_included ? "รวม VAT แล้ว" : "ไม่รวม VAT")} item={item} quotes={quotes}/>
            <PCRow label="ยืนราคาถึง" cells={quotes.map(q => formatDate(q.valid_until_date))} item={item} quotes={quotes}/>
            <PCRow label="ระยะเวลาส่งมอบ" cells={quotes.map(q => q.lead_time || "—")} item={item} quotes={quotes}/>
            <PCRow label="เงื่อนไขชำระเงิน" cells={quotes.map(q => q.payment_term || "—")} item={item} quotes={quotes}/>
            <PCRow label="การรับประกัน" cells={quotes.map(q => q.warranty_term || "—")} item={item} quotes={quotes}/>
            <PCRow label="ไฟล์แนบ" cells={quotes.map(q => {
              const att = attsByQuote.get(q.id)
                       || (q.attachment_id && attsById ? attsById.get(q.attachment_id) : null);
              if (!att) return <span className="text-ink-400">—</span>;
              return (
                <PCAttachmentLink att={att} tenant={tenant} pcId={pc.id}/>
              );
            })} item={item} quotes={quotes}/>
          </tbody>
        </table>
      </div>
      {/* Selection footer */}
      {selected && (
        <div className="bg-emerald-50/40 border-t border-emerald-100 px-4 py-2.5">
          <div className="flex items-start gap-2 text-[12.5px]">
            <Icon name="check" size={14} className="text-emerald-700 mt-0.5"/>
            <div className="flex-1">
              <div className="font-semibold text-emerald-800">ผู้ขายที่เลือก: {selected.supplier_name}</div>
              <div className="grid grid-cols-2 md:grid-cols-4 gap-2 mt-1 text-[12px]">
                <div><span className="text-ink-500">ราคาที่เลือก:</span> <span className="font-mono font-semibold">{formatMoney(selected.quoted_price, selected.currency)}</span></div>
                <div><span className="text-ink-500">ยืนราคาถึง:</span> {formatDate(selected.valid_until_date)}</div>
                <div className="md:col-span-2"><span className="text-ink-500">เหตุผลในการเลือก:</span> {reasonMeta ? reasonMeta.label : "—"}</div>
              </div>
              {item.selection_reason_text && (
                <div className="mt-1 italic text-ink-700">“{item.selection_reason_text}”</div>
              )}
            </div>
          </div>
        </div>
      )}
    </div>
  );
};

const PCRow = ({ label, cells, item, quotes }) => (
  <tr className="border-b border-ink-100 last:border-b-0">
    <td className="px-3 py-2 text-ink-500 align-top">{label}</td>
    {cells.map((c, qi) => {
      const isSel = item.selected_quote_id === quotes[qi].id;
      return <td key={qi} className={`px-3 py-2 align-top ${isSel ? "bg-emerald-50/60" : ""}`}>{c}</td>;
    })}
  </tr>
);

// Preview-first attachment viewer. PDFs and images open in a lightbox modal
// (no download); other types fall back to download since browsers can't render
// docx / xlsx inline. A download icon is always shown next to the preview link.
const PCAttachmentLink = ({ att, tenant, pcId }) => {
  const toast = useToast();
  const [previewUrl, setPreviewUrl] = useState(null);   // object URL for the modal
  const [busy, setBusy] = useState(false);
  const ext = (att.filename || "").toLowerCase().split(".").pop() || "";
  const isImage = ["jpg","jpeg","png","gif","webp"].includes(ext);
  const isPdf = ext === "pdf";
  const canPreview = isImage || isPdf;

  const fetchBlob = async () => {
    return await window.apiClient.pcDownloadAttachmentBlob(tenant.id, pcId, att.id);
  };

  const preview = async () => {
    if (busy) return;
    setBusy(true);
    try {
      const blob = await fetchBlob();
      const url = URL.createObjectURL(blob);
      setPreviewUrl(url);
    } catch (e) {
      toast({ kind: "error", msg: e.message || "เปิดดูไฟล์ไม่สำเร็จ" });
    } finally { setBusy(false); }
  };

  const download = async (e) => {
    if (e) e.stopPropagation();
    if (busy) return;
    setBusy(true);
    try {
      const blob = await fetchBlob();
      const url = URL.createObjectURL(blob);
      const a = document.createElement("a"); a.href = url; a.download = att.filename; a.click();
      setTimeout(() => URL.revokeObjectURL(url), 1000);
    } catch (e) {
      toast({ kind: "error", msg: e.message || "ดาวน์โหลดไม่สำเร็จ" });
    } finally { setBusy(false); }
  };

  const closePreview = () => {
    if (previewUrl) URL.revokeObjectURL(previewUrl);
    setPreviewUrl(null);
  };

  // ESC closes preview
  useEffect(() => {
    if (!previewUrl) return;
    const onKey = (e) => { if (e.key === "Escape") closePreview(); };
    document.addEventListener("keydown", onKey);
    return () => document.removeEventListener("keydown", onKey);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [previewUrl]);

  return (
    <>
      <span className="inline-flex items-center gap-2 text-[12px]">
        <button onClick={canPreview ? preview : download} disabled={busy}
          className="inline-flex items-center gap-1 text-emerald-700 hover:underline">
          <Icon name={canPreview ? "eye" : "paperclip"} size={11}/>
          {busy ? "กำลังโหลด…" : (canPreview ? "ดูตัวอย่าง" : "ดาวน์โหลด")}
        </button>
        {canPreview && (
          <Tip tip="ดาวน์โหลดไฟล์"><button onClick={download} disabled={busy}
            className="text-ink-400 hover:text-emerald-700"><Icon name="arrowDown" size={11}/></button></Tip>
        )}
      </span>

      {/* Lightbox preview modal */}
      {previewUrl && (
        <div className="fixed inset-0 z-[110] flex items-center justify-center p-4" style={{ background: "rgba(20,24,55,.75)" }} onClick={closePreview}>
          {/* Floating close button (always on top, always visible) */}
          <button onClick={closePreview}
            className="fixed top-4 right-4 z-[120] w-10 h-10 rounded-full bg-ink-900/90 hover:bg-rose-600 text-white flex items-center justify-center shadow-2xl border-2 border-white/30 transition"
            aria-label="ปิด preview"
            title="ปิด (ESC)">
            <Icon name="x" size={18}/>
          </button>
          {/* Floating download button (next to close) */}
          <button onClick={(e) => { e.stopPropagation(); download(); }}
            className="fixed top-4 right-16 z-[120] h-10 px-3 rounded-full bg-emerald-600/90 hover:bg-emerald-700 text-white flex items-center gap-1.5 shadow-2xl border-2 border-white/30 text-[12.5px] font-semibold transition"
            title="ดาวน์โหลดไฟล์">
            <Icon name="arrowDown" size={14}/> ดาวน์โหลด
          </button>

          <div className="relative bg-white rounded-2xl shadow-2xl pop overflow-hidden flex flex-col" style={{ width: "92vw", height: "92vh" }} onClick={e => e.stopPropagation()}>
            {/* Header (filename, decorative — actions are in floating buttons above) */}
            <div className="flex items-center gap-2 px-4 py-2.5 border-b border-ink-100 bg-white pr-32 shrink-0">
              <Icon name="paperclip" size={13} className="text-emerald-700"/>
              <div className="text-[13px] font-semibold text-ink-900 truncate flex-1">{att.filename}</div>
            </div>
            {/* Content */}
            <div className="flex-1 bg-ink-100 flex items-center justify-center overflow-hidden">
              {isImage ? (
                <img src={previewUrl} alt={att.filename} className="max-w-full max-h-full object-contain"/>
              ) : (
                <iframe src={previewUrl} title={att.filename} className="w-full h-full" style={{ border: 0 }}/>
              )}
            </div>
          </div>
        </div>
      )}
    </>
  );
};

// =============================================================================
// PAGE 4 — PRICE VALIDITY (approved price history)
// =============================================================================
const PriceValidityPage = ({ tenant, currentUser, currentRole, users }) => {
  const toast = useToast();
  const [rows, setRows] = useState([]);
  const [loading, setLoading] = useState(false);
  const [q, setQ] = useState("");
  const [filterSupplier, setFilterSupplier] = useState("__all__");
  const [filterCurrency, setFilterCurrency] = useState("__all__");
  const [filterStatus, setFilterStatus] = useState("__all__");
  const [openIpvId, setOpenIpvId] = useState(null);
  const [expanded, setExpanded] = useState(new Set());

  const reload = async () => {
    if (!tenant?.id || !window.apiClient) return;
    setLoading(true);
    try {
      const list = await window.apiClient.pcListValidity(tenant.id);
      setRows(Array.isArray(list) ? list : []);
    } catch (e) {
      toast({ kind:"error", msg: e.message || "โหลดประวัติราคาไม่สำเร็จ" });
    } finally { setLoading(false); }
  };
  useEffect(() => { reload(); /* eslint-disable-next-line */ }, [tenant?.id]);

  // Group rows by item_code → array of validity records (sorted by approved_at DESC)
  const grouped = useMemo(() => {
    const m = new Map();
    rows.forEach(r => {
      const key = r.item_code || `__no_code__${r.item_name}`;
      if (!m.has(key)) m.set(key, []);
      m.get(key).push(r);
    });
    // Sort each group by approved_at DESC
    m.forEach((arr) => arr.sort((a, b) => (b.approved_at || "").localeCompare(a.approved_at || "")));
    return m;
  }, [rows]);

  const counts = useMemo(() => {
    const items = new Set(rows.map(r => r.item_code || r.item_name).filter(Boolean)).size;
    const total = rows.length;
    const active = rows.filter(r => r.status === "active").length;
    const expired = rows.filter(r => r.status === "expired").length;
    return { items, total, active, expired };
  }, [rows]);

  // Supplier list for filter
  const suppliers = useMemo(() => Array.from(new Set(rows.map(r => r.supplier_name).filter(Boolean))).sort(), [rows]);
  const currencies = useMemo(() => Array.from(new Set(rows.map(r => r.currency).filter(Boolean))).sort(), [rows]);

  // Apply filters to grouped data
  const filteredGroups = useMemo(() => {
    const ql = q.trim().toLowerCase();
    const out = [];
    grouped.forEach((arr, key) => {
      const filteredArr = arr.filter(r => {
        if (filterSupplier !== "__all__" && r.supplier_name !== filterSupplier) return false;
        if (filterCurrency !== "__all__" && r.currency !== filterCurrency) return false;
        if (filterStatus !== "__all__" && r.status !== filterStatus) return false;
        if (!ql) return true;
        return (r.item_code || "").toLowerCase().includes(ql)
            || (r.item_name || "").toLowerCase().includes(ql)
            || (r.supplier_name || "").toLowerCase().includes(ql)
            || (r.source_pc_number || "").toLowerCase().includes(ql);
      });
      if (filteredArr.length > 0) out.push({ key, latest: filteredArr[0], all: filteredArr, total: arr.length });
    });
    return out;
  }, [grouped, q, filterSupplier, filterCurrency, filterStatus]);

  const toggleExpand = (key) => {
    const next = new Set(expanded);
    if (next.has(key)) next.delete(key); else next.add(key);
    setExpanded(next);
  };

  const exportExcel = () => {
    const headers = ["รหัสสินค้า","ชื่อสินค้า","หน่วย","ผู้ขาย","ราคาอนุมัติ","สกุล","THB เทียบเท่า","ยืนราคาถึง","สถานะ","เลขที่เอกสาร","วันที่อนุมัติ","ผู้อนุมัติ"];
    const csvRows = rows.map(r => [
      r.item_code, r.item_name, r.unit || "", r.supplier_name,
      r.approved_price, r.currency,
      r.price_thb_equivalent || (r.currency === "THB" ? r.approved_price : ""),
      formatDate(r.valid_until_date), r.status,
      r.source_pc_number,
      r.approved_at ? formatDateTime(r.approved_at) : "",
      displayUserName(users, r.approved_by_user_id),
    ]);
    window.downloadCsv(`price-validity-${new Date().toISOString().slice(0,10)}.csv`, headers, csvRows);
  };

  return (
    <div className="p-6 max-w-[1400px] mx-auto">
      <div className="text-[11px] tracking-wider text-emerald-700 font-bold mb-1">↕ PRICE VALIDITY · ราคาที่อนุมัติแล้ว</div>
      <div className="flex items-start justify-between gap-3 mb-1">
        <div>
          <div className="text-[24px] font-bold text-ink-900 leading-tight">ราคาที่อนุมัติแล้ว (Price Validity)</div>
          <div className="text-[13px] text-ink-500 mt-1">
            ราคาที่ได้รับอนุมัติจากเอกสารเปรียบเทียบราคา จัดกลุ่มตามรหัสสินค้า พร้อมประวัติราคาย้อนหลังของแต่ละสินค้า — ราคาเดิมจะถูกเก็บไว้ ไม่ถูกเขียนทับ
          </div>
        </div>
        <Button variant="secondary" icon="fileBarChart" onClick={exportExcel}>ส่งออก Excel</Button>
      </div>

      <div className="grid grid-cols-2 md:grid-cols-4 gap-3 mt-5">
        <KpiCard icon="package" tone="indigo"  label="สินค้าที่มีราคาอนุมัติ" value={counts.items}   trend="จำนวนรหัสสินค้า"/>
        <KpiCard icon="history" tone="violet"  label="รายการราคาทั้งหมด"     value={counts.total}   trend="รวมทุกรอบที่อนุมัติ"/>
        <KpiCard icon="check"   tone="emerald" label="ยังมีผล (Active)"     value={counts.active}  trend="ราคายังใช้ได้"/>
        <KpiCard icon="clock"   tone="slate"   label="หมดอายุ (Expired)"     value={counts.expired} trend="เกินวันที่ยืนราคา"/>
      </div>

      {/* Filters */}
      <div className="mt-5 flex items-center gap-2">
        <div className="relative flex-1 max-w-[460px]">
          <Icon name="search" size={14} className="absolute left-3 top-1/2 -translate-y-1/2 text-ink-400"/>
          <input type="text" value={q} onChange={e => setQ(e.target.value)}
            placeholder="ค้นหารหัสสินค้า / ชื่อสินค้า / ผู้ขาย / เลขที่เอกสาร"
            className="w-full h-9 pl-9 pr-3 text-[13px] border border-ink-200 rounded-lg bg-white focus:border-brand-500 ring-focus"/>
        </div>
        <Select className="w-[170px]" value={filterSupplier} onChange={e => setFilterSupplier(e.target.value)}>
          <option value="__all__">ผู้ขาย: ทั้งหมด</option>
          {suppliers.map(s => <option key={s} value={s}>{s}</option>)}
        </Select>
        <Select className="w-[150px]" value={filterCurrency} onChange={e => setFilterCurrency(e.target.value)}>
          <option value="__all__">สกุลเงิน: ทั้งหมด</option>
          {currencies.map(c => <option key={c} value={c}>{c}</option>)}
        </Select>
        <Select className="w-[150px]" value={filterStatus} onChange={e => setFilterStatus(e.target.value)}>
          <option value="__all__">สถานะ: ทั้งหมด</option>
          <option value="active">ยังมีผล</option>
          <option value="expired">หมดอายุ</option>
        </Select>
        <div className="ml-auto text-[11.5px] text-ink-500 whitespace-nowrap">
          {filteredGroups.length} สินค้า · {filteredGroups.reduce((s, g) => s + g.all.length, 0)} รายการราคา
        </div>
      </div>

      {/* List */}
      <div className="mt-3 bg-white rounded-2xl border border-ink-100 overflow-hidden">
        {loading ? (
          <div className="p-10 text-center text-ink-500 text-sm">กำลังโหลด…</div>
        ) : filteredGroups.length === 0 ? (
          <EmptyState icon="package" title="ยังไม่มีราคาที่อนุมัติ" subtitle="ราคาอนุมัติจะปรากฏที่นี่หลังจากเอกสารเปรียบเทียบราคาได้รับการอนุมัติ"/>
        ) : (
          <div className="overflow-x-auto thin-scroll">
            <table className="w-full text-[12.5px]">
              <thead className="bg-ink-50/60 text-ink-500 text-[11px] uppercase">
                <tr>
                  <th className="w-8"></th>
                  <th className="text-left px-4 py-2.5 font-semibold">สินค้า</th>
                  <th className="text-left px-3 py-2.5 font-semibold">ผู้ขายปัจจุบัน</th>
                  <th className="text-right px-3 py-2.5 font-semibold">ราคาอนุมัติ</th>
                  <th className="text-left px-3 py-2.5 font-semibold">ยืนราคาถึง</th>
                  <th className="text-left px-3 py-2.5 font-semibold">สถานะ</th>
                  <th className="text-center px-3 py-2.5 font-semibold">ประวัติ</th>
                  <th className="text-left px-3 py-2.5 font-semibold">เอกสารต้นทาง</th>
                </tr>
              </thead>
              <tbody>
                {filteredGroups.map(g => {
                  const latest = g.latest;
                  const isOpen = expanded.has(g.key);
                  return (
                    <React.Fragment key={g.key}>
                      <tr className="border-t border-ink-100 hover:bg-emerald-50/30 transition cursor-pointer" onClick={() => toggleExpand(g.key)}>
                        <td className="px-3 py-2.5 text-ink-400">
                          <Icon name="chevronRight" size={12} className={`transition-transform ${isOpen ? "rotate-90" : ""}`}/>
                        </td>
                        <td className="px-4 py-2.5">
                          <div className="font-mono text-[11.5px] font-bold text-emerald-700">{latest.item_code || "—"}</div>
                          <div className="text-[13px] text-ink-900">{latest.item_name}</div>
                          {latest.unit && <div className="text-[10.5px] text-ink-500">หน่วย: {latest.unit}</div>}
                        </td>
                        <td className="px-3 py-2.5 text-ink-700">{latest.supplier_name}</td>
                        <td className="px-3 py-2.5 text-right font-mono font-bold text-ink-900">
                          {formatMoney(latest.approved_price, latest.currency)}
                          <div className="text-[10px] text-ink-500 font-normal">{latest.unit ? `THB/${latest.unit}` : latest.currency}</div>
                        </td>
                        <td className="px-3 py-2.5 text-ink-700 whitespace-nowrap">{formatDate(latest.valid_until_date)}</td>
                        <td className="px-3 py-2.5">
                          <Badge color={latest.status === "active" ? "emerald" : "slate"}>
                            {latest.status === "active" ? "● ยังมีผล" : "● หมดอายุ"}
                          </Badge>
                        </td>
                        <td className="px-3 py-2.5 text-center">
                          <span className="inline-flex items-center justify-center min-w-[34px] h-6 px-2 bg-ink-100 rounded-md font-medium text-[11px]">{g.total} รอบ</span>
                        </td>
                        <td className="px-3 py-2.5">
                          <button onClick={(e) => { e.stopPropagation(); setOpenIpvId(latest.id); }}
                            className="text-emerald-700 hover:underline text-[12px] font-mono">{latest.source_pc_number}</button>
                        </td>
                      </tr>
                      {isOpen && g.all.map((r, i) => (
                        <tr key={r.id} className="border-t border-emerald-100 bg-emerald-50/20">
                          <td></td>
                          <td className="px-4 py-2 text-[11.5px] text-ink-500" colSpan={2}>
                            <Icon name="clock" size={11} className="inline mr-1"/>
                            <span>อนุมัติ {r.approved_at ? formatDate(r.approved_at.slice(0,10)) : "—"}</span>
                            <span className="ml-2">· โดย {displayUserName(users, r.approved_by_user_id)}</span>
                            <span className="ml-2">· {r.supplier_name}</span>
                          </td>
                          <td className="px-3 py-2 text-right font-mono text-[12px]">{formatMoney(r.approved_price, r.currency)}</td>
                          <td className="px-3 py-2 text-ink-700 whitespace-nowrap">{formatDate(r.valid_until_date)}</td>
                          <td className="px-3 py-2"><Badge color={r.status === "active" ? "emerald" : "slate"}>{r.status === "active" ? "ยังมีผล" : "หมดอายุ"}</Badge></td>
                          <td></td>
                          <td className="px-3 py-2">
                            <button onClick={() => setOpenIpvId(r.id)} className="text-emerald-700 hover:underline text-[12px] font-mono">{r.source_pc_number}</button>
                          </td>
                        </tr>
                      ))}
                      {isOpen && (
                        <tr className="border-t border-emerald-100 bg-emerald-50/40">
                          <td></td>
                          <td className="px-4 py-2 text-[11px] text-emerald-700 font-semibold" colSpan={7}>
                            <Icon name="history" size={11} className="inline mr-1"/>
                            ดูประวัติทั้งหมด ({g.total} รอบ) · แสดง {g.all.length} รอบล่าสุด จากทั้งหมด {g.total} รอบ
                          </td>
                        </tr>
                      )}
                    </React.Fragment>
                  );
                })}
              </tbody>
            </table>
          </div>
        )}
      </div>

      {/* Drawer: history detail for a single IPV row */}
      {openIpvId && (
        <PriceValidityDetailDrawer
          ipvId={openIpvId}
          tenant={tenant}
          users={users}
          onClose={() => setOpenIpvId(null)}
        />
      )}
    </div>
  );
};

const PriceValidityDetailDrawer = ({ ipvId, tenant, users, onClose }) => {
  const toast = useToast();
  const [ipv, setIpv] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    (async () => {
      if (!ipvId || !tenant?.id) return;
      setLoading(true);
      try {
        const d = await window.apiClient.pcGetValidity(tenant.id, ipvId);
        setIpv(d);
      } catch (e) { toast({ kind:"error", msg: e.message || "โหลดรายละเอียดไม่สำเร็จ" }); }
      finally { setLoading(false); }
    })();
    /* eslint-disable-next-line */
  }, [ipvId]);

  const otherQuotes = useMemo(() => {
    if (!ipv?.source_pc) return [];
    return (ipv.source_pc.supplier_quotes || []).filter(q => q.pc_item_id === ipv.source_pc_item_id && q.id !== ipv.source_quote_id);
  }, [ipv]);


  return (
    <Drawer open={!!ipvId} onClose={onClose} width={620}
      title={ipv ? `${ipv.item_code || "—"} · ${ipv.item_name}` : "กำลังโหลด…"}
      subtitle={ipv ? `จาก ${ipv.source_pc_number}` : null}
    >
      {loading || !ipv ? (
        <div className="p-6 text-center text-ink-500 text-sm">กำลังโหลด…</div>
      ) : (
        <div className="p-4 space-y-4">
          {/* Status header */}
          <div className={`rounded-xl p-4 ${ipv.status === "active" ? "bg-emerald-50 border border-emerald-100" : "bg-slate-100 border border-slate-200"}`}>
            <div className="flex items-center gap-2 mb-2">
              <Icon name="check" size={14} className={ipv.status === "active" ? "text-emerald-700" : "text-slate-500"}/>
              <span className="text-[13px] font-bold">{ipv.status === "active" ? "ราคายังมีผล (Active)" : "ราคาหมดอายุ (Expired)"}</span>
            </div>
            <div className="text-[26px] font-bold text-ink-900 font-mono">{formatMoney(ipv.approved_price, ipv.currency)}</div>
            <div className="text-[11px] text-ink-500 mt-0.5">
              {ipv.unit ? `ต่อ ${ipv.unit}` : ipv.currency} {ipv.price_thb_equivalent != null && ipv.currency !== "THB" && `· เทียบเท่า ${formatMoneyTHB(ipv.price_thb_equivalent)}`}
            </div>
            <div className="mt-2 text-[12px] text-ink-700">ยืนราคาถึง: <span className="font-semibold">{formatDate(ipv.valid_until_date)}</span></div>
          </div>

          <div className="grid grid-cols-2 gap-3 text-[12.5px]">
            <div><div className="text-[11px] text-ink-500">ผู้ขาย</div><div className="font-semibold">{ipv.supplier_name}</div></div>
            <div><div className="text-[11px] text-ink-500">รหัสผู้ขาย</div><div>{ipv.supplier_code || "—"}</div></div>
            <div><div className="text-[11px] text-ink-500">รหัสสินค้า</div><div className="font-mono">{ipv.item_code || "—"}</div></div>
            <div><div className="text-[11px] text-ink-500">หน่วย</div><div>{ipv.unit || "—"}</div></div>
            <div><div className="text-[11px] text-ink-500">สกุลเงิน</div><div>{ipv.currency}</div></div>
            {ipv.exchange_rate_to_thb && <div><div className="text-[11px] text-ink-500">อัตราแลกเปลี่ยน → THB</div><div className="font-mono">{ipv.exchange_rate_to_thb}</div></div>}
            <div><div className="text-[11px] text-ink-500">เลขที่เอกสารต้นทาง</div><div className="font-mono text-emerald-700">{ipv.source_pc_number}</div></div>
            <div><div className="text-[11px] text-ink-500">วันที่อนุมัติ</div><div>{formatDateTime(ipv.approved_at)}</div></div>
            <div className="col-span-2"><div className="text-[11px] text-ink-500">อนุมัติโดย</div><div>{displayUserName(users, ipv.approved_by_user_id)}</div></div>
          </div>

          {/* Reason */}
          <div className="bg-white border border-ink-100 rounded-xl p-3">
            <div className="text-[11px] text-ink-500 mb-1">เหตุผลในการเลือกผู้ขายนี้</div>
            <div className="text-[12.5px] font-semibold text-ink-800">
              {(PC_SELECTION_REASONS.find(r => r.code === ipv.selection_reason_code)?.label) || "—"}
            </div>
            {ipv.selection_reason_text && <div className="text-[12px] text-ink-700 italic mt-1">“{ipv.selection_reason_text}”</div>}
          </div>

          {/* Other quotes from same comparison */}
          {otherQuotes.length > 0 && (
            <div className="bg-white border border-ink-100 rounded-xl p-3">
              <div className="text-[12px] font-semibold text-ink-800 mb-2">ใบเสนอราคาอื่นจากเอกสารเดียวกัน</div>
              <div className="space-y-1.5">
                {otherQuotes.map(q => {
                  const thb = q.price_thb_equivalent != null ? q.price_thb_equivalent : (q.currency === "THB" ? q.quoted_price : null);
                  return (
                    <div key={q.id} className="flex items-center justify-between bg-ink-50/60 rounded-md px-3 py-2">
                      <div>
                        <div className="text-[12.5px] font-semibold text-ink-800">{q.supplier_name}</div>
                        <div className="text-[10.5px] text-ink-500">{q.quotation_number || "—"} · ยืนถึง {formatDate(q.valid_until_date)}</div>
                      </div>
                      <div className="text-right">
                        <div className="font-mono text-[13px] text-ink-700">{formatMoney(q.quoted_price, q.currency)}</div>
                        {thb != null && q.currency !== "THB" && <div className="text-[10px] text-ink-500">≈ {formatMoneyTHB(thb)}</div>}
                      </div>
                    </div>
                  );
                })}
              </div>
            </div>
          )}

          {/* Attachment(s) for the selected quote */}
          {ipv.source_pc && (ipv.source_pc.attachments || []).filter(a => a.pc_quote_id === ipv.source_quote_id).map(a => (
            <div key={a.id} className="bg-white border border-ink-100 rounded-xl p-3">
              <div className="text-[12px] font-semibold text-ink-800 mb-2">ไฟล์แนบใบเสนอราคาที่เลือก</div>
              <PCAttachmentLink att={a} tenant={tenant} pcId={ipv.source_pc_id}/>
            </div>
          ))}

          {/* If user lacks PC detail permission, source_pc comes back null → show notice */}
          {ipv.source_pc_restricted && (
            <div className="bg-amber-50 border border-amber-200 rounded-xl p-3">
              <div className="flex items-start gap-2 text-[12px] text-amber-800">
                <Icon name="ban" size={13} className="mt-0.5 shrink-0"/>
                <div>
                  <div className="font-semibold">ข้อมูลคัดเลือกผู้ขาย / ใบเสนอราคาอื่น / ไฟล์แนบ ถูกซ่อน</div>
                  <div className="mt-0.5 leading-snug">
                    เฉพาะผู้สร้างเอกสาร · ผู้อนุมัติ · Admin · Super Admin ที่ดูได้
                  </div>
                </div>
              </div>
            </div>
          )}
        </div>
      )}
    </Drawer>
  );
};

// =============================================================================
// EXPORT
// =============================================================================
window.PriceComparisonListPage = PriceComparisonListPage;
window.CreatePriceComparisonPage = CreatePriceComparisonPage;
window.PriceComparisonDetailModal = PriceComparisonDetailModal;
window.PriceValidityPage = PriceValidityPage;
window.PCStatusPill = PCStatusPill;
