/* global React, I, UI, SSSData */
const { useState: useStateN, useMemo: useMemoN, useEffect: useEffectN } = React;
const { Btn, Badge, Card, Drawer, Workflow } = window.UI;
const { fmtM, fmt0, bahtText } = window.UI;

// =============== เก็บเอกสารที่สร้างจริง (persist + apply เข้า SSSData) ===============
const NDC_KEY = "sss-created-docs";
const NDC_TOMB_KEY = "sss-doc-tombstones"; // เอกสารที่ถูกลบ (sync) — กันกลับมาจาก seed/ของเก่า
function ndcLoad() { try { return JSON.parse(localStorage.getItem(NDC_KEY) || "{}"); } catch { return {}; } }
function ndcTombLoad() { try { return JSON.parse(localStorage.getItem(NDC_TOMB_KEY) || "[]"); } catch { return []; } }
// listKey ของเอกสารทุกชนิด (ใช้สแกนหาเอกสารที่ผูกกัน)
const NDC_DOC_LISTS = ["Quotations", "Invoices", "DeliveryNotes", "TaxInvoices", "BillingNotes", "Receipts", "PurchaseOrders", "PaymentVouchers", "ReceiptVouchers"];
const NDC_LIST_LABEL = { Quotations: "ใบเสนอราคา", Invoices: "ใบแจ้งหนี้", DeliveryNotes: "ใบส่งสินค้า", TaxInvoices: "ใบกำกับภาษี", BillingNotes: "ใบวางบิล", Receipts: "ใบเสร็จรับเงิน", PurchaseOrders: "ใบสั่งซื้อ", PaymentVouchers: "ใบสำคัญจ่าย", ReceiptVouchers: "ใบสำคัญรับเงิน" };
window.DocCreateStore = {
  // เลขรันถัดไป — รูปแบบ AAYYYYMMDD00n (AA=อักษรย่อ, YYYYMMDD=วันที่ออกเอกสาร, 00n=ลำดับในวันนั้น)
  // เช่น TX20260630001 · นับลำดับใหม่ทุกวันต่อชนิดเอกสาร · สแกนรายการในระบบ+ที่เพิ่งสร้าง กันเลขซ้ำ
  // forDate (ถ้าส่งมา) = วันที่จากช่อง "วันที่ออก" — เลขจะรันตามวันนั้น ไม่ใช่วันปัจจุบัน
  nextNo(listKey, prefix, forDate) {
    const d = (forDate instanceof Date && !isNaN(forDate)) ? forDate : new Date();
    const ymd = `${d.getFullYear()}${String(d.getMonth() + 1).padStart(2, "0")}${String(d.getDate()).padStart(2, "0")}`;
    const stem = prefix + ymd;                                  // เช่น TX20260630
    const re = new RegExp("^" + stem + "(\\d+)$");              // จับลำดับของวันนี้
    const used = new Set();
    let max = 0;
    const scan = (arr) => (arr || []).forEach(r => {
      const no = r && r.no; if (!no) return;
      used.add(no);
      const m = re.exec(no); if (m) max = Math.max(max, parseInt(m[1], 10));
    });
    scan(window.SSSData && window.SSSData[listKey]);
    try { const created = JSON.parse(localStorage.getItem(NDC_KEY) || "{}"); scan(created[listKey]); } catch {}
    // เลขของเอกสารที่ถูกลบ (tombstone) ห้ามใช้ซ้ำ — ไม่งั้นเอกสารใหม่จะโดน tombstone ลบทิ้งทันทีตอน sync
    ndcTombLoad().forEach(key => {
      const sep = key.indexOf("::"); if (sep < 0) return;
      if (key.slice(0, sep) !== listKey) return;
      const no = key.slice(sep + 2);
      used.add(no);
      const m = re.exec(no); if (m) max = Math.max(max, parseInt(m[1], 10));
    });
    let n = max + 1, no;
    do { no = stem + String(n).padStart(3, "0"); n++; } while (used.has(no));
    return no;
  },
  add(listKey, row) {
    // สร้าง/บันทึกเอกสารเลขนี้ = ตั้งใจให้มีอยู่ — ปลด tombstone ถ้าเลขนี้เคยถูกลบ (กันโดนลบซ้ำตอน sync)
    try {
      const tomb = ndcTombLoad();
      const tk = listKey + "::" + row.no;
      if (tomb.includes(tk)) localStorage.setItem(NDC_TOMB_KEY, JSON.stringify(tomb.filter(k => k !== tk)));
    } catch {}
    const m = ndcLoad();
    m[listKey] = (m[listKey] || []).filter(r => r.no !== row.no);
    m[listKey].unshift(row);
    const ok = window.lsSetSafe ? window.lsSetSafe(NDC_KEY, JSON.stringify(m)) : (() => { try { localStorage.setItem(NDC_KEY, JSON.stringify(m)); return true; } catch (e) { return false; } })();
    if (!ok) window.toast && window.toast("⚠️ พื้นที่จัดเก็บเต็ม — บันทึกเอกสารไม่สำเร็จ ลองลบรูปเก่าก่อน");
    const list = window.SSSData[listKey] = window.SSSData[listKey] || [];
    const idx = list.findIndex(r => r.no === row.no);
    if (idx >= 0) list[idx] = { ...list[idx], ...row }; else list.unshift(row);
    window.dispatchEvent(new Event("sss-docs-changed"));
  },
  apply() {
    const m = ndcLoad();
    const tomb = new Set(ndcTombLoad());
    let added = 0;
    Object.entries(m).forEach(([lk, rows]) => {
      const list = window.SSSData[lk] = window.SSSData[lk] || [];
      rows.forEach(row => { if (!tomb.has(lk + "::" + row.no) && !list.some(r => r.no === row.no)) { list.unshift(row); added++; } });
    });
    return added;
  },
  // ลบเอกสารที่ถูก tombstone ออกจาก SSSData (กัน seed/ของเก่ากลับมา)
  applyTombstones() {
    const tomb = ndcTombLoad();
    let removed = 0;
    tomb.forEach(key => {
      const sep = key.indexOf("::"); if (sep < 0) return;
      const lk = key.slice(0, sep), no = key.slice(sep + 2);
      const list = window.SSSData[lk];
      if (Array.isArray(list)) {
        const i = list.findIndex(r => (r.no || r.id) === no);
        if (i >= 0) { list.splice(i, 1); removed++; }
      }
    });
    return removed;
  },
  // อัปเดตเอกสารที่มีอยู่ (เช่น แนบสลิป/คอมเมนต์) — merge เข้า row เดิม + sync
  patch(listKey, no, patch) {
    const list = window.SSSData[listKey] || [];
    const cur = list.find(r => r.no === no) || { no };
    this.add(listKey, { ...cur, ...patch });
  },
  // หาเอกสารที่ "ออกต่อจาก" เอกสารนี้ (ผูกกัน) — ต้องยกเลิกก่อนจึงลบได้ · ข้ามที่ยกเลิกแล้ว
  findLinks(no) {
    const out = [];
    NDC_DOC_LISTS.forEach(lk => {
      (window.SSSData[lk] || []).forEach(r => {
        if (r.no === no || r.status === "ยกเลิกแล้ว") return;
        const refStr = typeof r.ref === "string" ? r.ref : "";
        const refsIt = r.fromDoc === no || r.ref === no
          || (Array.isArray(r.invoices) && r.invoices.includes(no))
          || (refStr && refStr.split(/[,\s]+/).includes(no));
        if (refsIt) out.push({ listKey: lk, no: r.no, label: NDC_LIST_LABEL[lk] || lk });
      });
    });
    return out;
  },
  // ลบเอกสาร (tombstone + ออกจาก array + created-docs) — sync ข้ามเครื่อง
  remove(listKey, no) {
    // เก็บสำเนาไว้ในถังกู้คืน (3 วัน) ก่อนลบจริง
    try {
      const cur = (window.SSSData[listKey] || []).find(r => (r.no || r.id) === no)
        || ((ndcLoad()[listKey] || []).find(r => r.no === no));
      if (window.RecycleBin) window.RecycleBin.capture(listKey, no, cur || null);
    } catch (e) {}
    const t = new Set(ndcTombLoad()); t.add(listKey + "::" + no);
    try { localStorage.setItem(NDC_TOMB_KEY, JSON.stringify([...t])); } catch {}
    const m = ndcLoad();
    if (m[listKey]) { m[listKey] = m[listKey].filter(r => r.no !== no); try { localStorage.setItem(NDC_KEY, JSON.stringify(m)); } catch {} }
    const list = window.SSSData[listKey];
    if (Array.isArray(list)) { const i = list.findIndex(r => (r.no || r.id) === no); if (i >= 0) list.splice(i, 1); }
    window.dispatchEvent(new Event("sss-docs-changed"));
  }
};

// =============== ถังกู้คืนเอกสาร (เก็บที่ลบไว้ 3 วัน) ===============
const NDC_BIN_KEY = "sss-recycle-bin";
const NDC_BIN_TTL = 3 * 24 * 60 * 60 * 1000; // 3 วัน
function ndcBinLoad() { try { return JSON.parse(localStorage.getItem(NDC_BIN_KEY) || "[]"); } catch { return []; } }
function ndcBinSave(list) { try { localStorage.setItem(NDC_BIN_KEY, JSON.stringify(list)); } catch {} }
function ndcBinActive() {
  const now = Date.now();
  const all = ndcBinLoad();
  const live = all.filter(e => now - (e.deletedAt || 0) < NDC_BIN_TTL);
  if (live.length !== all.length) ndcBinSave(live); // ล้างที่หมดอายุ (เกิน 3 วัน)
  return live;
}
window.RecycleBin = {
  TTL_DAYS: 3,
  capture(listKey, no, row) {
    const list = ndcBinActive().filter(e => !(e.listKey === listKey && e.no === no));
    list.unshift({
      id: listKey + "::" + no + "::" + Date.now(),
      listKey, no, label: NDC_LIST_LABEL[listKey] || listKey, row: row || null,
      payee: row ? (row.customer || row.vendor || row.payee || "") : "",
      amount: row ? (row.amount || row.grand || 0) : 0,
      date: row ? (row.date || "") : "",
      deletedAt: Date.now(), deletedBy: (window.actorName ? window.actorName() : "")
    });
    ndcBinSave(list);
    window.dispatchEvent(new Event("sss-recycle-changed"));
  },
  list() { return ndcBinActive().sort((a, b) => b.deletedAt - a.deletedAt); },
  restore(id) {
    const e = ndcBinActive().find(x => x.id === id);
    if (!e) return null;
    // ปลด tombstone เพื่อให้เอกสารกลับมาได้ (ทุกเครื่องผ่าน sync)
    const t = ndcTombLoad().filter(k => k !== e.listKey + "::" + e.no);
    try { localStorage.setItem(NDC_TOMB_KEY, JSON.stringify(t)); } catch {}
    if (e.row) window.DocCreateStore.add(e.listKey, e.row);
    else { window.DocCreateStore.apply(); window.dispatchEvent(new Event("sss-docs-changed")); }
    ndcBinSave(ndcBinActive().filter(x => x.id !== id));
    window.dispatchEvent(new Event("sss-recycle-changed"));
    window.dispatchEvent(new Event("sss-docs-changed"));
    return e;
  },
  purge(id) {
    ndcBinSave(ndcBinActive().filter(x => x.id !== id));
    window.dispatchEvent(new Event("sss-recycle-changed"));
  },
  // เอกสารที่ถูกลบก่อนมีถังกู้คืน (มีแค่ tombstone — ตัวจริงอาจยังอยู่บนคลาวด์)
  tombstones() {
    const binKeys = new Set(ndcBinActive().map(e => e.listKey + "::" + e.no));
    return ndcTombLoad().filter(k => k.indexOf("::") > 0 && !binKeys.has(k)).map(k => {
      const sep = k.indexOf("::");
      const lk = k.slice(0, sep), no = k.slice(sep + 2);
      return { key: k, listKey: lk, no, label: NDC_LIST_LABEL[lk] || lk };
    });
  },
  // กู้จากคลาวด์: ปลด tombstone แล้วดึงข้อมูลกลับจาก Supabase (ถ้ายังมีอยู่)
  restoreTombstone(key) {
    const t = ndcTombLoad().filter(k => k !== key);
    try { localStorage.setItem(NDC_TOMB_KEY, JSON.stringify(t)); } catch {}
    window.DocCreateStore.apply();
    if (window.SSSSync && window.SSSSync.load) { try { window.SSSSync.load(); } catch (e) {} }
    window.dispatchEvent(new Event("sss-recycle-changed"));
    window.dispatchEvent(new Event("sss-docs-changed"));
  },
  forgetTombstone(key) {
    const t = ndcTombLoad().filter(k => k !== key);
    try { localStorage.setItem(NDC_TOMB_KEY, JSON.stringify(t)); } catch {}
    window.dispatchEvent(new Event("sss-recycle-changed"));
  }
};
window.DocCreateStore.applyTombstones();
window.DocCreateStore.apply();
// 🛡️ Safety net: หลัง sync/โหลด server อาจไม่มีเอกสารที่เพิ่งสร้าง (timing/แก้พร้อมกัน)
// → กู้เอกสารที่บันทึกไว้กลับเข้า array เสมอ แล้ว push ขึ้น server (กันเอกสารหาย) · เคารพ tombstone
(function () {
  let _t = null;
  const reApply = () => {
    window.DocCreateStore.applyTombstones();
    const added = window.DocCreateStore.apply();
    if (added > 0) {
      clearTimeout(_t);
      _t = setTimeout(() => window.dispatchEvent(new Event("sss-docs-changed")), 300);
    }
  };
  window.addEventListener("sss-db-loaded", reApply);
  window.addEventListener("sss-data-synced", reApply);
})();
const NDC_ROUTE_BY_LIST = { Quotations: "quotations", Invoices: "invoices", DeliveryNotes: "delivery-notes", TaxInvoices: "tax-invoices", BillingNotes: "billing-notes", Receipts: "receipts", PurchaseOrders: "purchase-orders", PaymentVouchers: "payment-vouchers", ReceiptVouchers: "receipt-vouchers" };
// ยกเลิกเอกสาร — ตั้งสถานะ "ยกเลิกแล้ว" (เก็บเลขเอกสารไว้ ไม่ลบ) · sync ทุกเครื่อง
window.cancelDocFlow = async function (listKey, no, label, onDone) {
  const list = window.SSSData[listKey] || [];
  const cur = list.find(r => (r.no || r.id) === no);
  if (cur && cur.status === "ยกเลิกแล้ว") { window.toast && window.toast(`${no} ถูกยกเลิกอยู่แล้ว`); return false; }
  const ok = await window.confirmDialog({
    title: `ยกเลิก ${label || "เอกสาร"} ${no}?`,
    message: "เอกสารจะถูกทำเครื่องหมาย <b>ยกเลิกแล้ว</b> แต่ยังเก็บไว้ในระบบ (เลขเอกสารไม่หาย) · ซิงค์ทุกเครื่อง",
    confirmText: "ยกเลิกเอกสาร", danger: true
  });
  if (!ok) return false;
  window.DocCreateStore.patch(listKey, no, { status: "ยกเลิกแล้ว" });
  window.logActivity && window.logActivity("ยกเลิกเอกสาร", `${label || ""} ${no}`, "update");
  window.toast && window.toast(`ยกเลิก ${no} แล้ว`);
  onDone && onDone();
  return true;
};
// ลบเอกสาร — กันถ้ามีเอกสารอื่นออกต่อจากเอกสารนี้ (ต้องยกเลิกอันที่ผูกก่อน)
window.deleteDocFlow = async function (listKey, no, label, navigate) {
  const links = window.DocCreateStore.findLinks(no);
  if (links.length) {
    await window.confirmDialog({
      title: "ลบไม่ได้ — มีเอกสารที่ออกต่อจากเอกสารนี้",
      message: `ต้องยกเลิก/ลบเอกสารที่ผูกกับ <b>${no}</b> ต่อไปนี้ก่อน แล้วจึงลบเอกสารนี้ได้:`,
      bullets: links.map(l => `${l.label} ${l.no}`),
      confirmText: "เข้าใจแล้ว"
    });
    return false;
  }
  const ok = await window.confirmDialog({
    title: `ลบ ${label || "เอกสาร"} ${no}?`,
    message: "เอกสารจะถูกย้ายไป <b>ถังกู้คืน</b> เก็บไว้ 3 วัน — กู้คืนได้ที่เมนู \"กู้คืนเอกสาร\" · ซิงค์ทุกเครื่อง",
    confirmText: "ลบเอกสาร", danger: true
  });
  if (!ok) return false;
  window.DocCreateStore.remove(listKey, no);
  window.logActivity && window.logActivity("ลบเอกสาร", `${label || ""} ${no}`, "delete");
  window.toast && window.toast(`ลบ ${no} แล้ว`);
  if (navigate) navigate(NDC_ROUTE_BY_LIST[listKey] || "dashboard");
  return true;
};
const NDC_DEFAULT_STATUS = { quotation: "ร่าง", invoice: "ค้างชำระ", "delivery-note": "กำลังจัดส่ง", "tax-invoice": "เต็มรูป", "billing-note": "ส่งแล้ว", receipt: "ออกแล้ว", "purchase-order": "รออนุมัติ", "payment-voucher": "รอจ่าย" };
const NDC_ROUTE = { quotation: "quotations", invoice: "invoices", "delivery-note": "delivery-notes", "tax-invoice": "tax-invoices", "billing-note": "billing-notes", receipt: "receipts", "purchase-order": "purchase-orders", "payment-voucher": "payment-vouchers" };

// =============== หมายเหตุเอกสารสำเร็จรูป (ตั้งค่าได้) ===============
const NDN_KEY = "sss-doc-note-presets";
const NDN_DEFAULTS = [
  "• ราคาข้างต้นเป็นราคาก่อนภาษีมูลค่าเพิ่ม",
  "• ระยะเวลาผลิตประมาณ 25-30 วันทำการ หลังได้รับ PO + เงินมัดจำ 50%",
  "• ชำระมัดจำ 50% ส่วนที่เหลือก่อนส่งงาน",
  "• ยืนราคา 30 วันนับจากวันที่ในเอกสาร",
  "• กรุณาชำระเงินภายในกำหนด · โอนเข้าบัญชีบริษัทเท่านั้น",
  "• สินค้ารับประกัน 1 ปี ตามเงื่อนไขบริษัท",
  "• แบบ 3D ที่ส่งให้ลูกค้าถือเป็นทรัพย์สินของบริษัทจนกว่าจะชำระครบ"
];
window.DocNotePresets = {
  load() { try { return JSON.parse(localStorage.getItem(NDN_KEY) || "null") || NDN_DEFAULTS; } catch { return NDN_DEFAULTS; } },
  save(list) { try { localStorage.setItem(NDN_KEY, JSON.stringify(list)); } catch {} window.dispatchEvent(new Event("sss-note-presets-changed")); },
  DEFAULTS: NDN_DEFAULTS
};

// =============== Document type config ===============
const DOC_TYPES = {
  quotation: {
    title: "ใบเสนอราคา",
    titleEn: "QUOTATION",
    prefix: "QT",
    next: "QO-2569-0118",
    party: "customer",
    partyLabel: "ลูกค้า",
    showDue: false,
    showValidity: true,
    showVAT: true,
    showSign: ["ผู้เสนอราคา", "ลูกค้าอนุมัติ"],
    listKey: "Quotations",
    note: "• ราคาข้างต้นเป็นราคาก่อนภาษีมูลค่าเพิ่ม\n• ระยะเวลาผลิตประมาณ 25-30 วันทำการ\n• ชำระมัดจำ 50% ส่วนที่เหลือก่อนส่งงาน"
  },
  "delivery-note": {
    title: "ใบส่งสินค้า",
    titleEn: "DELIVERY NOTE",
    prefix: "DN",
    next: "DN-2569-0189",
    party: "customer",
    partyLabel: "ผู้รับสินค้า",
    showDue: false,
    showVAT: false,
    showQty: true,
    showSign: ["ผู้ส่งของ", "ผู้รับของ", "วันที่/เวลารับ"],
    listKey: "DeliveryNotes",
    note: "โปรดตรวจรับสินค้าให้ครบถ้วนก่อนเซ็นรับ หากชำรุดเสียหายแจ้งทันที"
  },
  invoice: {
    title: "ใบส่งสินค้า / ใบแจ้งหนี้",
    titleEn: "DELIVERY NOTE / INVOICE",
    prefix: "INV",
    next: "INV-2569-0205",
    party: "customer",
    partyLabel: "ลูกค้า",
    showDue: true,
    showVAT: true,
    showSign: ["ผู้วางบิล", "ผู้รับวางบิล"],
    listKey: "Invoices",
    note: "กรุณาชำระเงินภายในกำหนด · โอนเข้าบัญชี ธ.ไทยพาณิชย์ 4211849819"
  },
  "tax-invoice": {
    title: "ใบกำกับภาษี",
    titleEn: "TAX INVOICE",
    prefix: "TX",
    next: "TX-2569-0205",
    party: "customer",
    partyLabel: "ลูกค้า (ผู้ซื้อ)",
    showDue: false,
    showVAT: true,
    showFullVAT: true,
    showSign: ["ผู้มีอำนาจลงนาม"],
    listKey: "TaxInvoices",
    note: "เอกสารฉบับนี้เป็นใบกำกับภาษีตามประมวลรัษฎากรมาตรา 86"
  },
  "billing-note": {
    title: "ใบวางบิล",
    titleEn: "BILLING NOTE",
    prefix: "BN",
    next: "BN-2569-0045",
    party: "customer",
    partyLabel: "ลูกค้า",
    showDue: true,
    showVAT: false,
    showSign: ["ผู้วางบิล", "ผู้รับวางบิล"],
    listKey: "BillingNotes",
    note: "กรุณานัดวันเก็บเงิน · ทุกวันอังคาร-พฤหัส 9.00-16.00 น."
  },
  receipt: {
    title: "ใบเสร็จรับเงิน",
    titleEn: "OFFICIAL RECEIPT",
    prefix: "RE",
    next: "RC-2569-0157",
    party: "customer",
    partyLabel: "ได้รับเงินจาก",
    showDue: false,
    showVAT: true,
    showStamp: true,
    showMethod: true,
    showSign: ["ผู้รับเงิน", "ตราประทับ"],
    listKey: "Receipts",
    note: "ได้รับเงินครบถ้วนเรียบร้อยแล้ว · ขอบคุณที่ใช้บริการ"
  },
  "purchase-order": {
    title: "ใบสั่งซื้อ",
    titleEn: "PURCHASE ORDER",
    prefix: "PO",
    next: "PO-2569-0099",
    party: "vendor",
    partyLabel: "ผู้ขาย",
    showDue: false,
    showExpected: true,
    showVAT: true,
    showSign: ["ผู้สั่งซื้อ", "ผู้รับใบสั่งซื้อ"],
    listKey: "PurchaseOrders",
    note: "กรุณายืนยันคำสั่งซื้อภายใน 24 ชม. · ระบุวันส่งสินค้าให้ชัดเจน"
  },
  "payment-voucher": {
    title: "ใบสำคัญจ่าย",
    titleEn: "PAYMENT VOUCHER",
    prefix: "PV",
    next: "PV-2569-0079",
    party: "vendor",
    partyLabel: "จ่ายให้",
    showDue: true,
    dueLabel: "กำหนดจ่าย",
    showMethod: true,
    showVAT: true,   // จ่ายผู้ขายที่จด VAT — เลือกบวก/รวมภาษีได้ (ค่าเริ่มต้นไม่คิด VAT)
    showSign: ["ผู้จัดทำ", "ผู้อนุมัติ", "ผู้รับเงิน"],
    listKey: "PaymentVouchers",
    note: "เอกสารฉบับนี้เป็นหลักฐานการจ่ายเงิน"
  }
};

// =============== Searchable party combobox ===============
function PartyCombo({ list, value, onChange }) {
  const [open, setOpen] = useStateN(false);
  const [q, setQ] = useStateN("");
  const sel = list.find(p => p.id === value);
  const ql = q.trim().toLowerCase();
  const filtered = ql ? list.filter(p => (p.id + " " + p.name + " " + (p.contact || "") + " " + (p.phone || "") + " " + (p.taxId || "")).toLowerCase().includes(ql)) : list;
  return (
    <div style={{position: "relative"}}>
      <div style={{position: "relative"}}>
        <input className="input" style={{paddingRight: 30}}
          value={open ? q : (sel ? `${sel.id} — ${sel.name}` : "")}
          placeholder="พิมพ์ค้นหาชื่อ / รหัส / เบอร์โทร…"
          onFocus={() => { setOpen(true); setQ(""); }}
          onChange={e => { setQ(e.target.value); setOpen(true); }}
          onBlur={() => setTimeout(() => setOpen(false), 150)}
          onKeyDown={e => {
            if (e.key === "Enter" && filtered.length > 0) { onChange(filtered[0].id); setOpen(false); e.target.blur(); }
            if (e.key === "Escape") { setOpen(false); e.target.blur(); }
          }}
        ></input>
        <span style={{position: "absolute", right: 10, top: "50%", transform: "translateY(-50%)", color: "var(--ink-3)", pointerEvents: "none", display: "flex"}}>
          {open ? <I.search size={13}/> : <I.chevronDown size={13}/>}
        </span>
      </div>
      {open && (
        <div style={{position: "absolute", top: "100%", left: 0, right: 0, zIndex: 70, background: "var(--surface)", border: "1px solid var(--line)", borderRadius: 8, boxShadow: "var(--shadow-lg)", maxHeight: 280, overflowY: "auto", marginTop: 4}}>
          {filtered.map(p => (
            <div key={p.id}
              onMouseDown={(e) => { e.preventDefault(); onChange(p.id); setOpen(false); }}
              style={{padding: "8px 12px", cursor: "pointer", borderBottom: "1px solid var(--line-soft)", background: p.id === value ? "var(--surface-2)" : "transparent"}}
              onMouseEnter={e => e.currentTarget.style.background = "var(--surface-2)"}
              onMouseLeave={e => e.currentTarget.style.background = p.id === value ? "var(--surface-2)" : "transparent"}>
              <div style={{fontSize: 12.5, fontWeight: 500}}><span className="mono" style={{fontSize: 11, color: "var(--ink-3)"}}>{p.id}</span> — {p.name}</div>
              <div style={{fontSize: 11, color: "var(--ink-3)", marginTop: 1}}>{p.contact}{p.phone ? <span className="mono"> · {p.phone}</span> : null}</div>
            </div>
          ))}
          {filtered.length === 0 && <div style={{padding: 14, fontSize: 12, color: "var(--ink-3)", textAlign: "center"}}>ไม่พบ "{q}"</div>}
        </div>
      )}
    </div>
  );
}

// เลือกรายการส่งมอบจากโปรเจกต์ (ติ๊กได้หลายรายการ) มาเป็นรายการเอกสาร
function ProjItemsModal({ items, onAdd, onClose }) {
  const { Btn } = window.UI;
  const [sel, setSel] = useStateN(() => items.map((_, i) => i)); // ติ๊กทั้งหมดไว้ก่อน
  const toggle = (i) => setSel(s => s.includes(i) ? s.filter(x => x !== i) : [...s, i]);
  return (
    <div className="scrim" style={{display: "flex", alignItems: "center", justifyContent: "center", padding: 18, zIndex: 240}} onClick={onClose}>
      <div style={{background: "var(--surface)", borderRadius: 14, width: 460, maxWidth: "96vw", maxHeight: "90vh", display: "flex", flexDirection: "column", overflow: "hidden"}} onClick={e => e.stopPropagation()}>
        <div style={{padding: "14px 18px", borderBottom: "1px solid var(--line)", display: "flex", alignItems: "center", gap: 10}}>
          <div style={{fontSize: 14.5, fontWeight: 700, flex: 1}}>เพิ่มรายการจากโปรเจกต์</div>
          <button className="icon-btn" onClick={onClose}><I.close size={14}/></button>
        </div>
        <div style={{padding: 12, overflow: "auto"}}>
          {items.map((it, i) => (
            <label key={i} style={{display: "flex", alignItems: "center", gap: 10, padding: "9px 10px", borderRadius: 8, cursor: "pointer", border: "1px solid var(--line)", marginBottom: 6, background: sel.includes(i) ? "color-mix(in oklab, var(--accent) 8%, var(--surface))" : "var(--surface)"}}>
              <input type="checkbox" checked={sel.includes(i)} onChange={() => toggle(i)} style={{width: 17, height: 17, accentColor: "var(--accent)"}}/>
              <span style={{flex: 1, fontSize: 13}}>{it.name}</span>
              <span style={{fontSize: 12, color: "var(--ink-3)"}}>{it.qty} {it.unit}</span>
            </label>
          ))}
        </div>
        <div style={{padding: "12px 18px", borderTop: "1px solid var(--line)", background: "var(--surface-2)", display: "flex", gap: 8, justifyContent: "flex-end"}}>
          <Btn kind="ghost" onClick={onClose}>ยกเลิก</Btn>
          <Btn kind="primary" icon={I.check} onClick={() => onAdd(sel.map(i => items[i]))}>เพิ่ม {sel.length} รายการ</Btn>
        </div>
      </div>
    </div>
  );
}

// =============== Main create-doc drawer ===============
function NewDocDrawer({ docType, onClose, prefill }) {
  const D = window.SSSData;
  const cfg = DOC_TYPES[docType] || DOC_TYPES.quotation;
  const partyList = cfg.party === "customer" ? D.Customers : D.Vendors;

  // โหมดแก้ไข: โหลดค่าที่บันทึกไว้จริงของเอกสารมาตั้งต้น "ทุกช่อง" — เลขที่/วันที่/วิธีจ่าย/VAT ฯลฯ
  // ไม่รีเซ็ตเป็นค่า default (แก้เฉพาะจุดที่ต้องการได้เลย)
  const _editRow = (prefill && prefill.editing && prefill.docNo)
    ? (((window.SSSData[cfg.listKey] || []).find(r => r.no === prefill.docNo))
      || ((ndcLoad()[cfg.listKey] || []).find(r => r.no === prefill.docNo)) || null)
    : null;

  const _thMonths = ["ม.ค.", "ก.พ.", "มี.ค.", "เม.ย.", "พ.ค.", "มิ.ย.", "ก.ค.", "ส.ค.", "ก.ย.", "ต.ค.", "พ.ย.", "ธ.ค."];
  const _thDate = (dt) => `${dt.getDate()} ${_thMonths[dt.getMonth()]} ${dt.getFullYear() + 543}`;
  // แปลงวันที่ไทย "2 ก.ค. 2569" → Date (รับทั้ง พ.ศ. และ ค.ศ.)
  const _parseThaiDate = (s) => {
    const m = String(s || "").trim().match(/^(\d{1,2})\s+(\S+)\s+(\d{4})$/);
    if (!m) return null;
    const mi = _thMonths.indexOf(m[2]);
    if (mi < 0) return null;
    let y = parseInt(m[3], 10);
    if (y > 2400) y -= 543;
    const d = new Date(y, mi, parseInt(m[1], 10), 12);
    return isNaN(d.getTime()) ? null : d;
  };
  const _now = new Date(); _now.setHours(12, 0, 0, 0);
  const _due = new Date(_now); _due.setDate(_due.getDate() + 30);
  const today = _thDate(_now);
  const [docNo, setDocNo] = useStateN(prefill && prefill.docNo ? prefill.docNo : (window.DocCreateStore.nextNo(cfg.listKey, cfg.prefix) || cfg.next));
  // โหมดแก้ไข: ใช้วันที่เดิมของเอกสาร (ส่งมาทาง prefill.date หรือจาก row ที่บันทึกไว้) — ไม่รีเซ็ตเป็นวันนี้
  const [date, setDate] = useStateN((prefill && prefill.date) || (_editRow && _editRow.date) || today);
  // เลขเอกสารรันตามช่อง "วันที่ออก" — เปลี่ยนวันที่ → รันเลขใหม่ของวันนั้น (001,002,...)
  // ข้ามรอบแรกตอนเปิดฟอร์ม (เลขตั้งต้นถูกต้องอยู่แล้ว / โหมดแก้ไขคงเลขเดิมจนกว่าจะแก้วันที่)
  const _dateInit = React.useRef(true);
  useEffectN(() => {
    if (_dateInit.current) { _dateInit.current = false; return; }
    const d = _parseThaiDate(date);
    if (!d) return;   // วันที่ยังพิมพ์ไม่ครบ/อ่านไม่ออก — คงเลขเดิมไว้ก่อน
    const ymd = `${d.getFullYear()}${String(d.getMonth() + 1).padStart(2, "0")}${String(d.getDate()).padStart(2, "0")}`;
    if (docNo && docNo.indexOf(cfg.prefix + ymd) === 0) return;   // เลขเป็นของวันนั้นอยู่แล้ว
    setDocNo(window.DocCreateStore.nextNo(cfg.listKey, cfg.prefix, d) || cfg.next);
  }, [date]);
  const [dueDate, setDueDate] = useStateN(_editRow && _editRow.due ? _editRow.due : (docType === "payment-voucher" ? today : _thDate(_due)));
  const [creditDays, setCreditDays] = useStateN(docType === "payment-voucher" ? 0 : 30);
  const applyCredit = (days) => {
    setCreditDays(days);
    const base = _parseThaiDate(date) || new Date();
    const d2 = new Date(base); d2.setDate(d2.getDate() + days);
    setDueDate(_thDate(d2));
  };
  const [validity, setValidity] = useStateN((_editRow && _editRow.validity) || "30 วัน");
  const _partyName0 = (prefill && prefill.partyName) || (_editRow && (_editRow.customer || _editRow.vendor || _editRow.payee)) || "";
  const [partyId, setPartyId] = useStateN(() => _partyName0 ? ((partyList.find(p => p.name === _partyName0) || partyList[0] || {}).id || "") : ((partyList[0] || {}).id || ""));
  const [project, setProject] = useStateN((prefill && prefill.project) || (_editRow && _editRow.project) || "");
  const _lines0 = (prefill && prefill.lines && prefill.lines.length) ? prefill.lines : (_editRow && Array.isArray(_editRow.lines) && _editRow.lines.length ? _editRow.lines : null);
  const [lines, setLines] = useStateN(() => _lines0
    ? _lines0.map((l, i) => ({ id: i + 1, sku: l.sku || "", name: l.name || "", qty: l.qty || 1, unit: l.unit || "ชิ้น", price: l.price || 0, img: l.img || "" }))
    : [{ id: 1, sku: "", name: "", qty: 1, unit: "ชิ้น", price: 0 }]
  );
  const [discount, setDiscount] = useStateN(() => prefill && prefill.discount != null ? prefill.discount : (_editRow && _editRow.discount != null ? _editRow.discount : 0));
  // ใบสำคัญจ่าย: ค่าเริ่มต้นไม่คิด VAT (เลือกติ๊กเองได้) · เอกสารขาย: ค่าเริ่มต้น 7%
  const [vatPct, setVatPct] = useStateN(() => prefill && prefill.vatPct != null ? prefill.vatPct
    : (_editRow && _editRow.vatPct != null ? _editRow.vatPct : (cfg.showVAT ? (docType === "payment-voucher" ? 0 : 7) : 0)));
  // รวมภาษี: true = ราคาที่กรอก "รวม VAT แล้ว" (ถอด VAT ออกมาแสดง) · false = ราคาก่อนภาษี (บวก VAT เพิ่ม)
  const [vatIncl, setVatIncl] = useStateN(() => prefill && prefill.vatIncl != null ? !!prefill.vatIncl
    : (_editRow && _editRow.vatIncl != null ? !!_editRow.vatIncl : false));
  // หัก ณ ที่จ่าย (เอกสารขาย): ลูกค้าหักภาษี ณ ที่จ่ายจากยอดก่อน VAT — ค่าเริ่มต้นไม่หัก เลือกติ๊กได้ (3% ค่าบริการ)
  const [whtPct, setWhtPct] = useStateN(() => prefill && prefill.whtPct != null ? prefill.whtPct
    : (_editRow && _editRow.whtPct != null ? _editRow.whtPct : 0));
  const [notes, setNotes] = useStateN((prefill && prefill.notes) || (_editRow && _editRow.notes) || cfg.note);
  // ใบเสร็จ: รายละเอียดใต้ชื่อลูกค้า ("รายละเอียด : ...")
  const [docDetail, setDocDetail] = useStateN((prefill && prefill.detail) || (_editRow && _editRow.detail) || "");
  // แนบลายเซ็นผู้ออกเอกสาร + ตราประทับบริษัท (ส่งไฟล์ออนไลน์ได้เลย)
  const [signStamp, setSignStamp] = useStateN(() => !!((prefill && prefill.signStamp) || (_editRow && _editRow.signStamp)));
  // วิธีรับ/จ่ายเงินจริง — อิงจากบัญชีธนาคารที่มีจริง + เช็ค/หักบัญชี
const PAY_METHODS = (() => {
    const accs = (window.CompanyCashStore && window.CompanyCashStore.ACCOUNTS) || [];
    const out = [];
    accs.forEach(a => out.push(a.kind === "เงินสด" ? `💵 เงินสด` : `🏦 โอน · ${a.short}`));
    out.push("🧾 เช็ค", "🔁 หักบัญชีอัตโนมัติ");
    return out;
  })();
  const [method, setMethod] = useStateN(() => (prefill && prefill.method) || (_editRow && _editRow.method) || (PAY_METHODS[0] || "💵 เงินสด"));
  const [taxType, setTaxType] = useStateN((_editRow && _editRow.type) || "เต็มรูป"); // ใบกำกับภาษี: เต็มรูป / อย่างย่อ
  const [expected, setExpected] = useStateN("29 พ.ค. 2569");
  const [showNewParty, setShowNewParty] = useStateN(false);
  const [showPrint, setShowPrint] = useStateN(false);
  const [fullView, setFullView] = useStateN(false); // ดูเอกสารเต็มจอ
  // ใบเสนอราคาแบบมีรูปในรายการ (ติ๊กเพื่อแนบรูปต่อรายการ)
  const canImages = docType === "quotation";
  const [withImages, setWithImages] = useStateN(() => !!((prefill && prefill.withImages) || (_editRow && _editRow.withImages)));

  const pickLineImg = async (id, file) => {
    if (!file) return;
    const dataUrl = window.DeliveryStore && window.DeliveryStore.resizeImage
      ? await window.DeliveryStore.resizeImage(file, 500) : null;
    if (dataUrl) updLine(id, { img: dataUrl });
    else window.toast && window.toast("อ่านรูปไม่สำเร็จ ลองใหม่");
  };

  const party = partyList.find(p => p.id === partyId);
  // โปรเจกต์ของลูกค้ารายนี้ — ให้เลือกผูกเอกสารกับงาน (เฉพาะเอกสารฝั่งขาย)
  const allProjects = [...(window.ProjX && window.ProjX.upLoad ? window.ProjX.upLoad() : []), ...((D.Projects) || [])];
  const custProjects = cfg.party === "customer" && party ? allProjects.filter(p => p.customer === party.name) : [];
  // ตัวเลือกโปรเจกต์: ของลูกค้ารายนี้ก่อน — ถ้าไม่มี ให้เลือกจากทุกโปรเจกต์ได้ (กันงงว่าทำไมไม่โผล่)
  const projOptions = cfg.party === "customer" ? (custProjects.length ? custProjects : allProjects) : [];
  // รายการส่งมอบ + หมายเหตุ ของโปรเจกต์ที่ผูก (ดึงมาเป็นรายการเอกสารได้)
  const projExtra = project && window.ProjX ? window.ProjX.pxGet(project) : {};
  const projItems = (projExtra.workOrders || []).flatMap(wo => (wo.items || []).map(it => ({ name: it.name, qty: it.qty || 1, unit: it.unit || "ชิ้น" })));
  const projNote = (projExtra.workOrders || []).map(wo => (wo.detail || "").trim()).filter(Boolean).join("\n");
  const [showProjPick, setShowProjPick] = useStateN(false);
  const addProjItems = (picked) => {
    setLines(ls => {
      const kept = ls.filter(l => (l.name || "").trim() || (Number(l.price) || 0) > 0);
      const add = picked.map((it, i) => ({ id: Date.now() + i, sku: "", name: it.name, qty: it.qty, unit: it.unit, price: 0 }));
      return [...kept, ...add, { id: Date.now() + 9999, sku: "", name: "", qty: 1, unit: "ชิ้น", price: 0 }]; // เว้นบรรทัดให้กรอกเอง
    });
    if (projNote) setNotes(n => (n && n.trim()) ? (n + "\n" + projNote) : projNote);
    setShowProjPick(false);
    window.toast && window.toast(`เพิ่ม ${picked.length} รายการจากโปรเจกต์แล้ว`);
  };

  // ✅ ใบวางบิล: ออกจากใบแจ้งหนี้เท่านั้น — เลือกรายการเอกสารแทนสินค้า
  const isBilling = docType === "billing-note";
  const [selInv, setSelInv] = useStateN([]);
  const [printCopies, setPrintCopies] = useStateN(true);
  const custInvoices = isBilling ? (D.Invoices || []).filter(i => i.customer === (party && party.name) && i.status !== "ชำระแล้ว") : [];
  const billLines = selInv.map((no, i) => { const inv = (D.Invoices || []).find(x => x.no === no); const owed = inv ? (inv.amount - (inv.paid || 0)) : 0; return { id: i + 1, sku: "", name: `ใบแจ้งหนี้ ${no}${inv ? " · " + inv.date : ""}`, qty: 1, unit: "ใบ", price: owed, _inv: inv }; });
  const effLines = isBilling ? billLines : lines;
  const toggleInv = (no) => setSelInv(s => s.includes(no) ? s.filter(x => x !== no) : [...s, no]);

  const rawSubtotal = useMemoN(() => effLines.reduce((s, l) => s + (Number(l.qty) || 0) * (Number(l.price) || 0), 0), [effLines]);
  const _pct = Number(vatPct) || 0;
  const vatActive = cfg.showVAT && _pct > 0;
  const inclusive = vatActive && vatIncl;              // ราคาที่กรอกรวม VAT แล้ว → ถอดออก
  // subtotal = ราคา "ก่อนภาษี" ของรายการ (โหมดรวมภาษี = ถอด VAT ออกจากที่กรอก)
  const subtotal = inclusive ? rawSubtotal / (1 + _pct / 100) : rawSubtotal;
  const afterDisc = subtotal - Number(discount || 0); // ฐานภาษีหลังหักส่วนลด (ก่อน VAT)
  const vat = vatActive ? afterDisc * _pct / 100 : 0;
  const grand = afterDisc + vat;                       // ราคารวมภาษี
  // หัก ณ ที่จ่าย: คิดจากฐานก่อน VAT (หลังหักส่วนลด) — ยอดเอกสารคงเดิม แต่ยอดรับชำระจริงลดลง
  const _whtPct = Number(whtPct) || 0;
  const whtActive = cfg.party === "customer" && _whtPct > 0;
  const wht = whtActive ? afterDisc * _whtPct / 100 : 0;
  const netPay = grand - wht;                          // ยอดรับชำระสุทธิหลังถูกหัก ณ ที่จ่าย

  const addLine = () => setLines(ls => [...ls, { id: Date.now(), sku: "", name: "", qty: 1, unit: "ชิ้น", price: 0 }]);
  const delLine = (id) => setLines(ls => ls.length > 1 ? ls.filter(l => l.id !== id) : ls);
  const updLine = (id, patch) => setLines(ls => ls.map(l => l.id === id ? { ...l, ...patch } : l));
  const pickProduct = (id, sku) => {
    if (!sku) { updLine(id, { sku: "", name: "" }); return; }
    const p = D.Products.find(x => x.sku === sku);
    if (p) updLine(id, { sku: p.sku, name: p.name, unit: p.unit, price: cfg.party === "vendor" ? p.cost : p.price });
  };

  // เพิ่มลูกค้า/ผู้ขายใหม่จากในฟอร์มออกเอกสารเลย — บันทึกเข้าฐาน CRM จริง + เลือกใช้ทันที
  const addNewParty = (c) => {
    if (cfg.party === "customer") {
      const maxN = Math.max(0, ...(D.Customers || []).map(x => parseInt((String(x.id || "").split("-")[1]) || 0, 10)));
      const entry = { owe: 0, lastOrder: "—", ...c, id: "C-" + String(maxN + 1).padStart(4, "0"), tags: c.tags ? [c.tags] : [] };
      D.Customers.unshift(entry);
      window.dispatchEvent(new Event("sss-customers-changed"));
      setPartyId(entry.id);
      window.logActivity && window.logActivity("เพิ่มลูกค้า", `${entry.name} (${entry.id}) · จากฟอร์มออกเอกสาร`, "create");
      window.toast && window.toast(`เพิ่มลูกค้า ${entry.name} และเลือกใช้ในเอกสารนี้แล้ว`);
    } else {
      const maxN = Math.max(0, ...(D.Vendors || []).map(x => parseInt((String(x.id || "").split("-")[1]) || 0, 10)));
      const entry = { balance: 0, ...c, id: "V-" + String(maxN + 1).padStart(4, "0") };
      D.Vendors.unshift(entry);
      window.dispatchEvent(new Event("sss-vendors-changed"));
      setPartyId(entry.id);
      window.logActivity && window.logActivity("เพิ่มผู้ขาย", `${entry.name} (${entry.id}) · จากฟอร์มออกเอกสาร`, "create");
      window.toast && window.toast(`เพิ่มผู้ขาย ${entry.name} และเลือกใช้ในเอกสารนี้แล้ว`);
    }
    setShowNewParty(false);
  };

  const save = () => {
    if (isBilling && selInv.length === 0) { window.toast && window.toast("เลือกใบแจ้งหนี้ที่จะนำมาวางบิลอย่างน้อย 1 ใบ"); return; }
    if (!party) { window.toast && window.toast("เลือก" + cfg.partyLabel + "ก่อน"); return; }
    // ✅ สร้างเอกสารจริง + บันทึกถาวร
    const row = { no: docNo, date, amount: Math.round(grand), status: NDC_DEFAULT_STATUS[docType] || "ออกแล้ว" };
    if (cfg.party === "customer") row.customer = party.name;
    else if (docType === "payment-voucher") row.payee = party.name;
    else row.vendor = party.name;
    // snapshot ข้อมูลคู่ค้าติดเอกสาร — ที่อยู่/เลขภาษี/ผู้ติดต่อ/โทร แสดงครบแม้ CRM ถูกแก้/ลบภายหลัง
    row.partySnap = {};
    ["name", "address", "taxId", "contact", "phone", "credit", "bank", "bankAcc", "accName"].forEach(k => {
      if (party[k] !== undefined && party[k] !== null && party[k] !== "") row.partySnap[k] = party[k];
    });
    if (cfg.showDue) row.due = dueDate;
    if (cfg.showValidity) row.validity = validity;
    if (docType === "invoice") row.paid = 0;
    if (docType === "tax-invoice") { row.type = taxType; row.base = Math.round(afterDisc); row.vat = Math.round(vat); }
    if (cfg.showMethod) row.method = method;
    if (isBilling) row.invoices = selInv.slice();
    if (prefill && prefill.fromDoc) row.fromDoc = prefill.fromDoc; // ผูกกับเอกสารต้นทาง (ใช้ตอนลบ)
    if (cfg.party === "customer" && project) { row.project = project; row.projectName = (projOptions.find(p => p.code === project) || {}).name || (prefill && prefill.projectName) || ""; }
    // ผู้ออกเอกสาร = user ที่ล็อกอินจริง (ไม่ใช่ dummy) — เก็บไว้กับเอกสารเพื่อแสดงภายหลัง
    if (!prefill || !prefill.editing || !row.issuedBy) {
      row.issuedBy = (window.Session && window.Session.get() && window.Session.get().name)
        || (D.Company.currentUser && D.Company.currentUser.name) || "";
    }
    // เก็บรายการ + รูป (ใบเสนอราคา) เพื่อพิมพ์ซ้ำภายหลัง
    if (!isBilling) {
      row.lines = lines.map(l => ({ sku: l.sku, name: l.name, qty: l.qty, unit: l.unit, price: l.price, ...(l.img ? { img: l.img } : {}) }));
      row.vatPct = Number(vatPct) || 0;
      row.vatIncl = !!vatIncl;
      row.discount = Number(discount) || 0;
      row.base = Math.round(afterDisc * 100) / 100;   // ราคารวมก่อนภาษี (หลังหักส่วนลด)
      row.vat = Math.round(vat * 100) / 100;          // มูลค่าภาษี
      if (canImages) row.withImages = withImages;
    }
    // หัก ณ ที่จ่าย — เก็บ % และยอดหักติดเอกสาร (พิมพ์ซ้ำ/แก้ไขได้ค่าเดิม)
    if (cfg.party === "customer") {
      row.whtPct = _whtPct;
      row.wht = Math.round(wht * 100) / 100;
    }
    row.notes = notes;   // เก็บหมายเหตุติดเอกสาร — เปิดแก้ไขแล้วได้ข้อความเดิม
    if (docDetail.trim()) row.detail = docDetail.trim(); else if (docType === "receipt") delete row.detail;
    row.signStamp = !!signStamp;
    // โหมดแก้ไข: คงค่าที่ฟอร์มไม่ได้สร้างใหม่ (ลิงก์ต้นทาง/ยอดชำระ/ผู้ออกเอกสารเดิม)
    if (prefill && prefill.editing && _editRow) {
      ["fromDoc", "paid", "invoices", "accountId", "partySnap"].forEach(k => {
        if (row[k] === undefined && _editRow[k] !== undefined) row[k] = _editRow[k];
      });
      if (_editRow.issuedBy) row.issuedBy = _editRow.issuedBy;
    }
    // ── ใบเสร็จรับเงิน: บันทึกเงินเข้าบัญชีที่รับชำระอัตโนมัติ ──
    if (docType === "receipt" && window.CompanyCashStore) {
      try {
        const accs = window.CompanyCashStore.ACCOUNTS || [];
        const acc = accs.find(a => (a.kind === "เงินสด" && /เงินสด/.test(method)) || (a.short && method.includes(a.short))) || accs[0];
        if (acc) {
          row.accountId = acc.id;
          // ลบรายการเดิมของใบนี้ (กรณีแก้ไข) แล้วบันทึกใหม่ — ยอดบัญชีตรงเสมอ ไม่ซ้ำ
          (window.CompanyCashStore.load().movements || [])
            .filter(m => m.ref === docNo && m.source === "ใบเสร็จ")
            .forEach(m => window.CompanyCashStore.removeMovement(m.id));
          // เงินเข้าจริง = ยอดหลังถูกหัก ณ ที่จ่าย (ลูกค้าหักไว้แล้วออกหนังสือรับรองให้)
          window.CompanyCashStore.addMovement({ accountId: acc.id, direction: "in", amount: Math.round(netPay), desc: `รับเงิน ${docNo} · ${party.name}${whtActive ? ` (หลังหัก ณ ที่จ่าย ${_whtPct}%)` : ""}`, ref: docNo, source: "ใบเสร็จ" });
          window.toast && window.toast(`เงินเข้า ${acc.name} ฿${fmtM(netPay)} อัตโนมัติแล้ว${whtActive ? ` (หัก ณ ที่จ่าย ${_whtPct}% = ฿${fmtM(wht)})` : ""}`);
        }
      } catch {}
    }
    // แก้ไขเอกสารแล้วเลขเปลี่ยน (เปลี่ยนวันที่ออก) → เก็บกวาดเลขเดิม กันเป็นเอกสารซ้ำ 2 ใบ
    if (prefill && prefill.editing && prefill.docNo && prefill.docNo !== docNo) {
      try { window.DocCreateStore.remove(cfg.listKey, prefill.docNo); } catch {}
    }
    window.DocCreateStore.add(cfg.listKey, row);
    // ออกใบเสร็จ → ปิดยอดต้นทาง (ใบแจ้งหนี้/ใบส่งของ) เป็น "ชำระแล้ว" ตลอดสาย
    if (docType === "receipt" && prefill && prefill.fromDoc && window.DocStatusStore) {
      const PFX_TYPE = { INV: "invoice", DN: "delivery-note", TX: "tax-invoice", BN: "billing-note" };
      const findByNo = (n) => { for (const lk of ["Invoices", "DeliveryNotes", "TaxInvoices", "BillingNotes"]) { const r = (D[lk] || []).find(x => x.no === n); if (r) return r; } return null; };
      let cur = prefill.fromDoc, guard = 0;
      while (cur && guard++ < 6) {
        const pfx = (String(cur).match(/^([A-Z]{2,3})/) || [])[1];
        const t = PFX_TYPE[pfx];
        if (t) { try { window.DocStatusStore.set(t, cur, "ชำระแล้ว"); } catch (e) {} }
        const src = findByNo(cur);
        cur = src && src.fromDoc;
      }
      window.dispatchEvent(new Event("sss-doc-status-changed"));
    }
    window.logActivity && window.logActivity(prefill && prefill.editing ? "แก้ไขเอกสาร" : "สร้างเอกสาร", `${cfg.title} ${docNo} · ฿${fmtM(grand)}`, "create");
    window.toast && window.toast(`บันทึก ${cfg.title} ${docNo} แล้ว${isBilling && printCopies ? " · แนบสำเนาใบแจ้งหนี้แล้ว" : ""} — เปิดดูได้ในรายการ`);
    onClose && onClose();
    const route = NDC_ROUTE[docType];
    if (route) setTimeout(() => { window.location.hash = "#/" + route; }, 250);
  };
  const saveAndPrint = () => {
    if (isBilling && selInv.length === 0) { window.toast && window.toast("เลือกใบแจ้งหนี้ที่จะนำมาวางบิลอย่างน้อย 1 ใบ"); return; }
    setShowPrint(true);
    if (isBilling && printCopies) window.__billCopies = selInv.slice();
    setTimeout(() => window.printDocPaperWithOptions(), 250);
  };
  // เตือนถ้ายังไม่ได้บันทึก
  const isDirty = () => (isBilling ? selInv.length > 0 : lines.some(l => l.name || (Number(l.qty) || 0) > 1 || (Number(l.price) || 0) > 0)) || (Number(discount) || 0) > 0 || notes !== cfg.note;
  const requestClose = async () => {
    if (isDirty()) {
      const ok = await window.confirmDialog({
        title: "ยังไม่ได้บันทึกเอกสาร",
        message: `มีการแก้ไขใน <b>${cfg.title} ${docNo}</b> ที่ยังไม่ได้บันทึก — ออกโดยไม่บันทึก?`,
        confirmText: "ออกโดยไม่บันทึก", cancelText: "กลับไปแก้ต่อ"
      });
      if (!ok) return;
    }
    onClose && onClose();
  };

  return (
    <>
      <div className="scrim" onClick={requestClose}/>
      <div className="drawer wide newdoc-drawer" style={{width: "min(1480px, 96vw)"}}>
        <div className="drawer-head">
          <I.plus size={16}/>
          <h3>{prefill && prefill.editing ? "แก้ไข" : "สร้าง"} {cfg.title}</h3>
          {prefill && prefill.fromDoc && <span className="badge info" style={{marginLeft: 8}}>📋 สร้างจาก {prefill.fromDoc} — ตรวจสอบแล้วกดบันทึก</span>}
          {prefill && prefill.editing && <span className="badge warning" style={{marginLeft: 8}}>✏️ แก้ไขเอกสาร {prefill.docNo}</span>}
          <span className="mono muted" style={{fontSize: 12}}>{docNo}</span>
          <span className="spacer"/>
          <button className="icon-btn" onClick={onClose}><I.close size={14}/></button>
        </div>
        <div className="drawer-body" style={{padding: 0, display: "grid", gridTemplateColumns: "minmax(380px, 440px) 1fr"}}>
          {/* LEFT: Form */}
          <div style={{padding: 18, borderRight: "1px solid var(--line)", overflowY: "auto"}}>
            <div className="h2 mb">ข้อมูลเอกสาร</div>
            <div className="field-row cols-2 mb">
              <div className="field"><label>เลขที่</label><input className="input mono" value={docNo} onChange={e=>setDocNo(e.target.value)}/></div>
              <div className="field"><label>วันที่ออก</label><input className="input" value={date} onChange={e=>setDate(e.target.value)}/></div>
            </div>
            {cfg.showDue && (
              <div className="field-row cols-2 mb">
                <div className="field"><label>{cfg.dueLabel || "ครบกำหนดชำระ"}</label><input className="input" value={dueDate} onChange={e=>setDueDate(e.target.value)}/></div>
                {docType === "payment-voucher"
                  ? <div className="field"><label>เครดิต (กำหนดจ่ายนับจากวันที่ออก)</label>
                      <div className="payseg" style={{display: "flex", width: "100%"}}>
                        {[0, 7, 15, 30].map(dn => (
                          <button key={dn} type="button" className={"payseg-btn" + (creditDays === dn ? " active" : "")} style={{flex: 1}} onClick={() => applyCredit(dn)}>{dn === 0 ? "จ่ายทันที" : dn + " วัน"}</button>
                        ))}
                      </div>
                    </div>
                  : <div className="field"><label>เครดิต</label><div className="input" style={{display: "flex", alignItems: "center", color: "var(--ink-3)"}}>{party?.credit || 30} วัน (อัตโนมัติ)</div></div>}
              </div>
            )}
            {cfg.showValidity && (
              <div className="field-row cols-2 mb">
                <div className="field"><label>ยืนราคา</label>
                  <select className="input" value={validity} onChange={e=>setValidity(e.target.value)}>
                    <option>7 วัน</option><option>15 วัน</option><option>30 วัน</option><option>60 วัน</option>
                  </select>
                </div>
                <div></div>
              </div>
            )}
            {cfg.showExpected && (
              <div className="field-row cols-2 mb">
                <div className="field"><label>นัดรับสินค้า</label><input className="input" value={expected} onChange={e=>setExpected(e.target.value)}/></div>
                <div></div>
              </div>
            )}
            {cfg.showMethod && (
              <div className="field-row cols-2 mb">
                <div className="field"><label>วิธีรับ/จ่ายเงิน</label>
                  <select className="input" value={method} onChange={e=>setMethod(e.target.value)}>
                    {PAY_METHODS.map(m => <option key={m} value={m}>{m}</option>)}
                  </select>
                </div>
                {docType === "tax-invoice"
                  ? <div className="field"><label>ประเภทใบกำกับภาษี</label>
                      <div className="payseg" style={{display: "flex"}}>
                        {["เต็มรูป", "อย่างย่อ"].map(tt => (
                          <button key={tt} type="button" className={"payseg-btn" + (taxType === tt ? " active" : "")} style={{flex: 1}} onClick={() => setTaxType(tt)}>{tt === "เต็มรูป" ? "เต็มรูปแบบ" : "อย่างย่อ"}</button>
                        ))}
                      </div>
                    </div>
                  : <div></div>}
              </div>
            )}

            <div className="h2 mt-lg mb" style={{display: "flex", alignItems: "center"}}>
              <span>{cfg.partyLabel}</span>
              <span className="spacer"/>
              <Btn size="sm" kind="ghost" icon={I.plus} onClick={() => setShowNewParty(true)}>เพิ่ม{cfg.party === "customer" ? "ลูกค้า" : "ผู้ขาย"}ใหม่</Btn>
            </div>
            {partyList.length === 0 && (
              <div style={{padding: "9px 12px", background: "var(--warning-bg, #fef3c7)", color: "var(--warning, #b45309)", border: "1px solid var(--warning, #f59e0b)", borderRadius: 8, fontSize: 12.5, marginBottom: 10}}>
                ⚠️ ยังไม่มี{cfg.partyLabel}ในระบบ — ไปเพิ่มที่เมนู {cfg.party === "vendor" ? "Vendor/ผู้ขาย" : "ลูกค้า (CRM)"} ก่อน แล้วกลับมาออกเอกสาร
              </div>
            )}
            <div className="field mb">
              <PartyCombo list={partyList} value={partyId} onChange={setPartyId}/>
            </div>
            {cfg.party === "customer" && projOptions.length > 0 && (
              <div className="field mb">
                <label>ผูกกับโปรเจกต์ (เลือกเพื่อดึงรายการ/หมายเหตุของงานมาใส่)</label>
                <select className="input mono" value={project} onChange={e => setProject(e.target.value)}>
                  <option value="">— ไม่ผูกโปรเจกต์ —</option>
                  {projOptions.map(p => <option key={p.code} value={p.code}>{p.code} · {p.name}{custProjects.length ? "" : ` · ${p.customer}`}</option>)}
                </select>
              </div>
            )}
            {party && (
              <div style={{padding: 10, background: "var(--surface-2)", borderRadius: 6, fontSize: 12, color: "var(--ink-3)", marginBottom: 12, lineHeight: 1.6}}>
                <div>{party.contact} · <span className="mono">{party.phone}</span></div>
                <div>เลขผู้เสียภาษี: <span className="mono">{party.taxId}</span></div>
                {party.credit && <div>เครดิต: {party.credit} วัน</div>}
              </div>
            )}
            {docType === "receipt" && (
              <div className="field" style={{marginBottom: 12}}>
                <label>รายละเอียด (แสดงใต้ชื่อลูกค้าในเอกสาร)</label>
                <input className="input" value={docDetail} onChange={e => setDocDetail(e.target.value)} placeholder="เช่น ค่ามัดจำงานแร็คท้ายเวฟ งวดที่ 1"/>
              </div>
            )}
            <label style={{display: "flex", alignItems: "center", gap: 8, fontSize: 12.5, cursor: "pointer", padding: "8px 12px", background: signStamp ? "var(--surface-2)" : "transparent", border: signStamp ? "1.5px solid var(--accent)" : "1px solid var(--line)", borderRadius: 8, marginBottom: 12}}>
              <input type="checkbox" checked={signStamp} onChange={e => setSignStamp(e.target.checked)}/>
              <span>🖋 แนบลายเซ็นผู้ออกเอกสาร + ตราประทับบริษัท <span style={{fontSize: 11, color: "var(--ink-4)"}}>(ส่งไฟล์ให้ลูกค้าออนไลน์ได้เลย · ตั้งค่ารูปที่หน้าผู้ใช้/ตั้งค่า)</span></span>
            </label>

            <div className="h2 mt-lg mb" style={{display: "flex", alignItems: "center"}}>
              <span>{isBilling ? "เอกสารที่นำมาวางบิล" : "รายการสินค้า/บริการ"}</span>
              <span className="spacer"/>
              {!isBilling && project && <Btn size="sm" kind="ghost" icon={I.cube3d} onClick={() => projItems.length ? setShowProjPick(true) : (window.toast && window.toast("โปรเจกต์นี้ยังไม่มีรายการส่งมอบในใบสั่งงาน"))}>เพิ่มจากโปรเจค</Btn>}
              {!isBilling && <Btn size="sm" icon={I.plus} onClick={addLine}>เพิ่มรายการ</Btn>}
            </div>
            {showProjPick && <ProjItemsModal items={projItems} onAdd={addProjItems} onClose={() => setShowProjPick(false)}/>}
            {canImages && (
              <label style={{display: "flex", alignItems: "center", gap: 8, fontSize: 12.5, cursor: "pointer", padding: "8px 12px", background: withImages ? "var(--surface-2)" : "transparent", border: withImages ? "1.5px solid var(--accent)" : "1px solid var(--line)", borderRadius: 8, marginBottom: 8}}>
                <input type="checkbox" checked={withImages} onChange={e => setWithImages(e.target.checked)}/>
                <span>🖼️ ใส่รูปภาพในรายการ — ออกใบเสนอราคาแบบมีรูปสินค้าต่อรายการ</span>
              </label>
            )}
            {isBilling ? (
              <div style={{display: "flex", flexDirection: "column", gap: 8}}>
                <div style={{fontSize: 12, color: "var(--ink-3)"}}>ใบวางบิลออกจาก"ใบส่งสินค้า/ใบแจ้งหนี้"ที่ยังไม่ชำระของลูกค้ารายนี้ — เลือกได้หลายใบ</div>
                {custInvoices.length === 0 && <div style={{padding: 16, textAlign: "center", color: "var(--ink-3)", fontSize: 12.5, border: "1px dashed var(--line)", borderRadius: 8}}>ลูกค้ารายนี้ไม่มีใบแจ้งหนี้ค้างชำระ — เลือกลูกค้ารายอื่น</div>}
                {custInvoices.map(inv => {
                  const owed = inv.amount - (inv.paid || 0);
                  const on = selInv.includes(inv.no);
                  return (
                    <label key={inv.no} style={{display: "flex", alignItems: "center", gap: 10, padding: "9px 12px", border: on ? "1.5px solid var(--accent)" : "1px solid var(--line)", borderRadius: 8, cursor: "pointer", background: on ? "var(--surface-2)" : "var(--surface)"}}>
                      <input type="checkbox" checked={on} onChange={() => toggleInv(inv.no)}></input>
                      <div style={{flex: 1, minWidth: 0}}>
                        <div style={{fontSize: 12.5, fontWeight: 600}}><span className="mono">{inv.no}</span> · {inv.date}</div>
                        <div style={{fontSize: 11, color: "var(--ink-3)"}}>ครบกำหนด {inv.due || "—"} · สถานะ {inv.status}</div>
                      </div>
                      <span className="amount" style={{fontWeight: 600, fontSize: 12.5}}>฿{fmtM(owed)}</span>
                    </label>
                  );
                })}
                <label style={{display: "flex", alignItems: "center", gap: 8, marginTop: 4, fontSize: 12.5, cursor: "pointer", padding: "8px 12px", background: "var(--surface-2)", borderRadius: 8}}>
                  <input type="checkbox" checked={printCopies} onChange={e => setPrintCopies(e.target.checked)}></input>
                  📎 พิมพ์สำเนาใบแจ้งหนี้ที่นำมาวางบิลแนบท้ายใบวางบิล ({selInv.length} ใบ)
                </label>
              </div>
            ) : (
            <div style={{display: "flex", flexDirection: "column", gap: 8}}>
              {lines.map((l, i) => (
                <div key={l.id} style={{padding: 10, border: "1px solid var(--line)", borderRadius: 8, background: "var(--surface)"}}>
                  <div style={{display: "flex", alignItems: "center", gap: 8, marginBottom: 8}}>
                    <span style={{fontSize: 11, color: "var(--ink-3)", width: 18}}>#{i + 1}</span>
                    <select className="input" style={{height: 30, flex: 1}} value={l.sku} onChange={e=>pickProduct(l.id, e.target.value)}>
                      <option value="">— เลือกจากคลัง หรือกรอกเอง —</option>
                      {D.Products.map(p => <option key={p.sku} value={p.sku}>{p.sku} — {p.name}</option>)}
                    </select>
                    <button className="icon-btn" onClick={() => delLine(l.id)} title="ลบ"><I.trash size={12}/></button>
                  </div>
                  <window.UI.ProductAutocomplete value={l.name} placeholder="ชื่อรายการ — พิมพ์เพื่อค้นหาสินค้า"
                    style={{marginBottom: 6}}
                    onChange={v => updLine(l.id, { name: v })}
                    onPick={p => updLine(l.id, { sku: p.sku, name: p.name, unit: p.unit, price: cfg.party === "vendor" ? p.cost : p.price })}/>
                  <div style={{display: "grid", gridTemplateColumns: "1fr 1fr 1.2fr 1fr", gap: 6}}>
                    <input className="input" type="number" placeholder="จำนวน" value={l.qty} onChange={e=>updLine(l.id, { qty: e.target.value })}/>
                    <input className="input" placeholder="หน่วย" value={l.unit} onChange={e=>updLine(l.id, { unit: e.target.value })}/>
                    <input className="input" type="number" placeholder="ราคา/หน่วย" value={l.price} onChange={e=>updLine(l.id, { price: e.target.value })}/>
                    <div className="input amount" style={{display: "flex", alignItems: "center", justifyContent: "flex-end", background: "var(--surface-2)"}}>฿{fmtM((Number(l.qty)||0) * (Number(l.price)||0))}</div>
                  </div>
                  {canImages && withImages && (
                    <div style={{display: "flex", alignItems: "center", gap: 8, marginTop: 8}}>
                      {l.img
                        ? <img src={l.img} alt="" style={{width: 46, height: 46, objectFit: "cover", borderRadius: 6, border: "1px solid var(--line)"}}/>
                        : <div style={{width: 46, height: 46, borderRadius: 6, border: "1px dashed var(--line-strong)", display: "grid", placeItems: "center", color: "var(--ink-3)"}}><I.camera size={16}/></div>}
                      <label className="btn" style={{cursor: "pointer", fontSize: 12}}>
                        {l.img ? "เปลี่ยนรูป" : "แนบรูป"}
                        <input type="file" accept="image/*" style={{display: "none"}} onChange={e => pickLineImg(l.id, e.target.files && e.target.files[0])}/>
                      </label>
                      {l.img && <button className="icon-btn" title="ลบรูป" onClick={() => updLine(l.id, { img: "" })}><I.trash size={12}/></button>}
                    </div>
                  )}
                </div>
              ))}
            </div>
            )}

            {docType !== "delivery-note" && <><div className="h2 mt-lg mb">สรุปยอด</div>
            <div style={{display: "flex", flexDirection: "column", gap: 6, padding: 12, background: "var(--surface-2)", borderRadius: 8}}>
              {(cfg.showVAT || cfg.party === "customer") && (
                <div style={{display: "flex", justifyContent: "space-between", alignItems: "center", paddingBottom: 6, borderBottom: "1px dashed var(--line)"}}>
                  <label style={{display: "flex", alignItems: "center", gap: 7, cursor: "pointer", fontSize: 13}}>
                    <input type="checkbox" checked={vatIncl} onChange={e => { setVatIncl(e.target.checked); if (e.target.checked && (Number(vatPct)||0) === 0) setVatPct(7); }}></input>
                    <span className="muted">รวมภาษี <span style={{fontSize: 11, color: "var(--ink-4)"}}>(ราคาที่กรอกรวม VAT แล้ว)</span></span>
                  </label>
                  <span style={{fontSize: 11, color: "var(--ink-3)"}}>{vatIncl ? "ถอด VAT ออกมาแสดง" : "ราคาก่อนภาษี"}</span>
                </div>
              )}
              <div style={{display: "flex", justifyContent: "space-between"}}><span className="muted">{vatIncl ? "ราคารวมก่อนภาษี" : "รวมเป็นเงิน"}</span><span className="amount">฿{fmtM(subtotal)}</span></div>
              <div style={{display: "flex", justifyContent: "space-between", alignItems: "center"}}>
                <span className="muted">ส่วนลด</span>
                <input className="input" type="number" value={discount} onChange={e=>setDiscount(e.target.value)} style={{width: 110, textAlign: "right", height: 26}}/>
              </div>
              {(cfg.showVAT || cfg.party === "customer") && (
                <div style={{display: "flex", justifyContent: "space-between", alignItems: "center"}}>
                  <label style={{display: "flex", alignItems: "center", gap: 7, cursor: "pointer", fontSize: 13}}>
                    <input type="checkbox" checked={(Number(vatPct) || 0) > 0} onChange={e => setVatPct(e.target.checked ? 7 : 0)}></input>
                    <span className="muted">คิดภาษีมูลค่าเพิ่ม (VAT 7%)</span>
                  </label>
                  <span className="amount">{(Number(vatPct) || 0) > 0 ? `฿${fmtM(vat)}` : "— ไม่คิด VAT"}</span>
                </div>
              )}
              {cfg.party === "customer" && (
                <div style={{display: "flex", justifyContent: "space-between", alignItems: "center"}}>
                  <label style={{display: "flex", alignItems: "center", gap: 7, cursor: "pointer", fontSize: 13}}>
                    <input type="checkbox" checked={_whtPct > 0} onChange={e => setWhtPct(e.target.checked ? 3 : 0)}></input>
                    <span className="muted">หัก ณ ที่จ่าย</span>
                    {_whtPct > 0 && (
                      <select className="input" value={_whtPct} onChange={e => setWhtPct(Number(e.target.value))} style={{height: 26, width: 74, padding: "0 6px", fontSize: 12}}>
                        {[1, 2, 3, 5].map(p => <option key={p} value={p}>{p}%</option>)}
                      </select>
                    )}
                  </label>
                  <span className="amount" style={_whtPct > 0 ? {color: "var(--danger, #dc2626)"} : {}}>{_whtPct > 0 ? `−฿${fmtM(wht)}` : "— ไม่หัก"}</span>
                </div>
              )}
              <div style={{borderTop: "1px solid var(--line)", paddingTop: 6, marginTop: 4, display: "flex", justifyContent: "space-between", fontWeight: 600, fontSize: 15}}>
                <span>ยอดสุทธิ (รวมภาษี)</span>
                <span className="amount">฿{fmtM(grand)}</span>
              </div>
              {whtActive && (
                <div style={{display: "flex", justifyContent: "space-between", fontWeight: 600, fontSize: 13.5, color: "var(--accent)"}}>
                  <span>ยอดรับชำระหลังหัก ณ ที่จ่าย {_whtPct}%</span>
                  <span className="amount">฿{fmtM(netPay)}</span>
                </div>
              )}
            </div></>}

            <div className="field mt-lg">
              <label>หมายเหตุ</label>
              <div style={{display: "flex", flexWrap: "wrap", gap: 6, marginBottom: 8}}>
                {(window.DocNotePresets ? window.DocNotePresets.load() : []).map((p, i) => {
                  const on = notes.includes(p);
                  return (
                    <button key={i} type="button" onClick={() => setNotes(cur => on ? cur.split("\n").filter(l => l !== p).join("\n") : (cur ? cur + "\n" + p : p))}
                      style={{textAlign: "left", fontSize: 11.5, padding: "6px 10px", borderRadius: 8, cursor: "pointer", fontFamily: "var(--font)", maxWidth: "100%",
                        border: on ? "1.5px solid var(--accent)" : "1px solid var(--line)", background: on ? "var(--surface-2)" : "var(--surface)", color: on ? "var(--ink-1)" : "var(--ink-2)"}}>
                      {on ? "✓ " : "+ "}{p.replace(/^•\s*/, "")}
                    </button>
                  );
                })}
              </div>
              <textarea className="textarea" value={notes} onChange={e=>setNotes(e.target.value)} style={{minHeight: 80}} placeholder="เลือกจากการ์ดด้านบน หรือพิมพ์เอง"/>
              <div style={{fontSize: 10.5, color: "var(--ink-3)", marginTop: 4}}>ปรับรายการหมายเหตุได้ที่ ตั้งค่า → ตั้งค่าหมายเหตุเอกสาร</div>
            </div>
          </div>

          {/* RIGHT: Live preview */}
          <div style={{padding: 18, overflowY: "auto", background: "var(--surface-2)"}} className="newdoc-preview">
            <div className="h2 mb muted" style={{display: "flex", alignItems: "center", gap: 6}}>
              <I.doc size={14}/> ตัวอย่างเอกสาร (พิมพ์ได้)
              <span className="spacer" style={{flex: 1}}/>
              <Btn size="sm" kind="ghost" icon={I.search} onClick={() => setFullView(true)}>ดูเต็มจอ</Btn>
            </div>
            <PrintablePaper
              cfg={cfg}
              docType={docType}
              docNo={docNo}
              docDetail={docType === "receipt" ? docDetail : ""}
              signStamp={signStamp}
              date={date}
              dueDate={dueDate}
              validity={validity}
              expected={expected}
              method={method}
              party={party}
              lines={effLines.filter(l => l.name || l.qty > 0)}
              subtotal={subtotal}
              discount={Number(discount) || 0}
              vat={vat}
              vatPct={vatPct}
              wht={wht}
              whtPct={_whtPct}
              grand={grand}
              notes={notes}
              withImages={canImages && withImages}
            />
          </div>
        </div>
        <div className="drawer-foot">
          <Btn kind="ghost" onClick={requestClose}>ยกเลิก</Btn>
          <Btn kind="ghost" onClick={save}>บันทึกร่าง</Btn>
          <Btn icon={I.print} onClick={saveAndPrint}>บันทึก + พิมพ์</Btn>
          <Btn kind="primary" icon={I.check} onClick={save}>บันทึก</Btn>
        </div>
      </div>

      {showNewParty && cfg.party === "customer" && window.NewCustomerDrawer && (
        <window.NewCustomerDrawer onClose={() => setShowNewParty(false)} onSave={addNewParty}/>
      )}
      {showNewParty && cfg.party !== "customer" && window.NewVendorDrawer && (
        <window.NewVendorDrawer onClose={() => setShowNewParty(false)} onSave={addNewParty}/>
      )}
      {fullView && (
        <div style={{ position: "fixed", inset: 0, zIndex: 300, background: "rgba(15,12,30,0.72)", display: "flex", flexDirection: "column" }} onClick={() => setFullView(false)}>
          <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", padding: "10px 16px", color: "#fff", flexShrink: 0 }} onClick={e => e.stopPropagation()}>
            <span style={{ fontSize: 14, fontWeight: 600 }}>{cfg.title} · {docNo}</span>
            <div style={{ display: "flex", gap: 8 }}>
              <Btn size="sm" kind="ghost" icon={I.print} onClick={() => window.printDocPaperWithOptions()} style={{ color: "#fff", borderColor: "rgba(255,255,255,0.4)" }}>พิมพ์</Btn>
              <button className="icon-btn" onClick={() => setFullView(false)} style={{ background: "rgba(255,255,255,0.15)", color: "#fff" }}><I.close size={16} /></button>
            </div>
          </div>
          <div style={{ flex: 1, overflow: "auto", padding: "0 16px 24px", display: "flex", justifyContent: "center" }} onClick={e => e.stopPropagation()}>
            <div style={{ width: 794, maxWidth: "100%", background: "#fff", borderRadius: 8, boxShadow: "0 24px 70px rgba(0,0,0,0.5)", alignSelf: "flex-start" }}>
              <PrintablePaper cfg={cfg} docType={docType} docNo={docNo} date={date} dueDate={dueDate} validity={validity}
                expected={expected} method={method} party={party} lines={effLines.filter(l => l.name || l.qty > 0)}
                subtotal={subtotal} discount={Number(discount) || 0} vat={vat} vatPct={vatPct} wht={wht} whtPct={_whtPct} grand={grand} notes={notes} docDetail={docType === "receipt" ? docDetail : ""} signStamp={signStamp}
                withImages={canImages && withImages} />
            </div>
          </div>
        </div>
      )}
    </>
  );
}

// =============== Printable paper (used in preview + standalone print) ===============
function PrintablePaper({ cfg, docType, docNo, date, dueDate, validity, expected, method, party, lines, subtotal, discount, vat, vatPct, wht, whtPct, grand, notes, withImages, issuedBy, docDetail, signStamp }) {
  const showImg = !!withImages;
  const hidePrice = docType === "delivery-note"; // ใบส่งสินค้า — ไม่แสดงราคา/ยอดเงิน
  const C = window.SSSData.Company;
  const issuer = issuedBy || (window.Session && window.Session.get() && window.Session.get().name) || (C.currentUser && C.currentUser.name) || "";
  // ลายเซ็นผู้ออกเอกสาร + ตราประทับ (เมื่อติ๊กแนบ) — จากคลังรูปที่ตั้งค่าไว้
  const _signs = (window.lsSyncGet && window.lsSyncGet("sss-user-signs")) || {};
  const _assets = (window.lsSyncGet && window.lsSyncGet("sss-doc-sign-assets")) || {};
  const sigImg = signStamp ? (_signs[issuer] || "") : "";
  const stampImg = signStamp ? (_assets.stamp || "") : "";
  const afterDisc = subtotal - discount;
  const color = window.getDocColor(docType);
  const colorSoft = window.hexA(color, 0.07);
  const colorLine = window.hexA(color, 0.2);

  return (
    <div className="doc-paper print-target" style={{fontSize: 11.5, padding: 0, position: "relative", overflow: "hidden"}}>
      {/* Top color band */}
      <div style={{height: 6, background: color}}/>

      <div style={{padding: "26px 36px 32px"}}>
        {/* Header */}
        <div style={{display: "grid", gridTemplateColumns: "1fr auto", gap: 24, marginBottom: 24, alignItems: "flex-start"}}>
          <div style={{display: "flex", gap: 20, alignItems: "flex-start"}}>
            <div style={{background: color, borderRadius: 6, alignSelf: "stretch", minHeight: 56, maxHeight: 80, display: "flex", alignItems: "center", justifyContent: "center", padding: "8px 16px", flexShrink: 0}}><img src={window.docLogo ? window.docLogo() : "assets/logo-ink.png"} alt={C.name} style={{width: 132, maxHeight: "100%", objectFit: "contain", display: "block"}}/></div>
            <div style={{paddingTop: 2}}>
              <div style={{fontWeight: 700, fontSize: 13.5, lineHeight: 1.25, color: "#0c0a09"}}>{C.name}</div>
              <div style={{fontSize: 10.5, color: "#78716c", marginTop: 1}}>{C.nameEn}</div>
              <div style={{fontSize: 10.5, color: "#57534e", marginTop: 6, lineHeight: 1.5, maxWidth: 360}}>{C.address}</div>
              <div style={{display: "flex", gap: 14, fontSize: 10.5, color: "#57534e", marginTop: 3}}>
                <span>โทร {C.phone}</span>
                <span>{C.email}</span>
              </div>
              <div style={{fontSize: 10.5, color: "#57534e", marginTop: 1}}>เลขประจำตัวผู้เสียภาษี <b style={{color: "#0c0a09"}}>{C.taxId}</b> (สำนักงานใหญ่)</div>
            </div>
          </div>
          <div style={{textAlign: "right", minWidth: 200}}>
            <h1 style={{margin: 0, fontSize: 28, fontWeight: 700, color, letterSpacing: "-0.02em", lineHeight: 1, textTransform: "none"}}>{cfg.title}</h1>
            <div style={{fontSize: 9.5, color: "#a8a29e", letterSpacing: "0.12em", marginTop: 4, fontWeight: 500}}>{cfg.titleEn}</div>
            <div style={{marginTop: 14, display: "inline-block", textAlign: "left", background: colorSoft, padding: "10px 14px", borderRadius: 4, minWidth: 180}}>
              <div style={{display: "flex", justifyContent: "space-between", gap: 14, fontSize: 11}}><span style={{color: "#78716c"}}>เลขที่</span><b className="mono" style={{color: "#0c0a09"}}>{docNo}</b></div>
              <div style={{display: "flex", justifyContent: "space-between", gap: 14, fontSize: 11, marginTop: 3}}><span style={{color: "#78716c"}}>วันที่</span><b style={{color: "#0c0a09"}}>{date}</b></div>
              {cfg.showDue && <div style={{display: "flex", justifyContent: "space-between", gap: 14, fontSize: 11, marginTop: 3}}><span style={{color: "#78716c"}}>ครบกำหนด</span><b style={{color: "#0c0a09"}}>{dueDate}</b></div>}
              {cfg.showValidity && <div style={{display: "flex", justifyContent: "space-between", gap: 14, fontSize: 11, marginTop: 3}}><span style={{color: "#78716c"}}>ยืนราคา</span><b style={{color: "#0c0a09"}}>{validity}</b></div>}
              {cfg.showExpected && <div style={{display: "flex", justifyContent: "space-between", gap: 14, fontSize: 11, marginTop: 3}}><span style={{color: "#78716c"}}>นัดรับ</span><b style={{color: "#0c0a09"}}>{expected}</b></div>}
            </div>
          </div>
        </div>

        {/* Party info — 2 column boxes */}
        <div style={{display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12, marginBottom: 16}}>
          <div style={{border: `1px solid ${colorLine}`, borderRadius: 5, overflow: "hidden"}}>
            <div style={{background: color, color: "white", padding: "5px 12px", fontSize: 10, fontWeight: 600, letterSpacing: "0.06em", textTransform: "uppercase"}}>{cfg.partyLabel}</div>
            <div style={{padding: "10px 12px", fontSize: 11.5}}>
              <div style={{fontWeight: 600, fontSize: 12.5, color: "#0c0a09"}}>{party?.name || "—"}</div>
              {docDetail && <div style={{fontSize: 11.5, color: "#44403c", marginTop: 2}}>รายละเอียด : {docDetail}</div>}
              {party && (
                <div style={{color: "#57534e", marginTop: 3, lineHeight: 1.6}}>
                  {party.address && <div>{party.address}</div>}
                  <div>เลขผู้เสียภาษี <span className="mono" style={{color: "#0c0a09"}}>{party.taxId || "—"}</span></div>
                  {party.contact && <div>ผู้ติดต่อ: {party.contact}</div>}
                  {party.phone && party.phone !== "—" && <div className="mono">{party.phone}</div>}
                  {party.bankAcc && <div style={{marginTop: 4, paddingTop: 4, borderTop: "1px dashed #e7e5e4"}}>บัญชีรับโอน: <b style={{color: "#0c0a09"}}>{party.bank || ""} {party.bankAcc}</b>{party.accName ? ` (${party.accName})` : ""}</div>}
                </div>
              )}
            </div>
          </div>
          <div style={{border: "1px solid #e7e5e4", borderRadius: 5, overflow: "hidden"}}>
            <div style={{background: "#f7f6f4", color: "#44403c", padding: "5px 12px", fontSize: 10, fontWeight: 600, letterSpacing: "0.06em", textTransform: "uppercase"}}>เงื่อนไข · อ้างอิง</div>
            <div style={{padding: "10px 12px", fontSize: 11.5, color: "#44403c", lineHeight: 1.7}}>
              {cfg.showDue && <div>เครดิต: <b>{party?.credit || 30} วัน</b> · ครบกำหนด {dueDate}</div>}
              {cfg.showMethod && <div>วิธี: <b>{method}</b></div>}
              {cfg.showValidity && <div>ยืนราคา: <b>{validity}</b></div>}
              <div>ผู้ออกเอกสาร: <b>{issuer}</b></div>
              <div>โครงการ/อ้างอิง: <span className="mono">{docNo}</span></div>
            </div>
          </div>
        </div>

        {/* Line items table */}
        <table style={{borderCollapse: "separate", borderSpacing: 0, width: "100%"}}>
          <thead>
            <tr>
              <th style={{width: 30, background: color, color: "white", padding: "10px 6px", fontSize: 10.5, fontWeight: 600, textAlign: "center", borderTopLeftRadius: 4}}>#</th>
              {showImg && <th style={{width: 60, background: color, color: "white", padding: "10px 6px", fontSize: 10.5, fontWeight: 600, textAlign: "center"}}>รูป</th>}
              <th style={{background: color, color: "white", padding: "10px 12px", fontSize: 10.5, fontWeight: 600, textAlign: "left"}}>คำอธิบายรายการ</th>
              <th style={{width: 55, background: color, color: "white", padding: "10px 6px", fontSize: 10.5, fontWeight: 600, textAlign: "right"}}>จำนวน</th>
              <th style={{width: 55, background: color, color: "white", padding: "10px 6px", fontSize: 10.5, fontWeight: 600, textAlign: "left", ...(hidePrice ? {borderTopRightRadius: 4} : {})}}>หน่วย</th>
              {!hidePrice && <th style={{width: 85, background: color, color: "white", padding: "10px 8px", fontSize: 10.5, fontWeight: 600, textAlign: "right"}}>ราคา/หน่วย</th>}
              {!hidePrice && <th style={{width: 100, background: color, color: "white", padding: "10px 12px", fontSize: 10.5, fontWeight: 600, textAlign: "right", borderTopRightRadius: 4}}>มูลค่า</th>}
            </tr>
          </thead>
          <tbody>
            {lines.length === 0 && (
              <tr><td colSpan={(showImg ? 5 : 4) + (hidePrice ? 0 : 2)} style={{padding: 26, textAlign: "center", color: "#a8a29e", fontStyle: "italic", borderBottom: "1px solid #e7e5e4"}}>(ยังไม่มีรายการ)</td></tr>
            )}
            {lines.map((l, i) => (
              <tr key={l.id} style={{background: i % 2 === 0 ? "white" : "#fafaf9"}}>
                <td style={{padding: "10px 6px", borderBottom: "1px solid #f1efeb", textAlign: "center", fontSize: 11, color: "#78716c"}}>{i + 1}</td>
                {showImg && (
                  <td style={{padding: "6px", borderBottom: "1px solid #f1efeb", textAlign: "center"}}>
                    {l.img
                      ? <img src={l.img} alt="" style={{width: 50, height: 50, objectFit: "cover", borderRadius: 4, border: "1px solid #e7e5e4", display: "inline-block"}}/>
                      : <div style={{width: 50, height: 50, borderRadius: 4, border: "1px dashed #d6d3d1", display: "inline-block"}}/>}
                  </td>
                )}
                <td style={{padding: "10px 12px", borderBottom: "1px solid #f1efeb"}}>
                  <div style={{fontWeight: 500, fontSize: 11.5, color: "#0c0a09"}}>{l.name || "—"}</div>
                  {l.sku && <div className="mono" style={{fontSize: 9.5, color: "#78716c", marginTop: 1}}>{l.sku}</div>}
                </td>
                <td style={{padding: "10px 6px", borderBottom: "1px solid #f1efeb", textAlign: "right", fontSize: 11}}>{l.qty}</td>
                <td style={{padding: "10px 6px", borderBottom: "1px solid #f1efeb", fontSize: 11, color: "#57534e"}}>{l.unit}</td>
                {!hidePrice && <td style={{padding: "10px 8px", borderBottom: "1px solid #f1efeb", textAlign: "right", fontSize: 11}}>{fmtM(Number(l.price) || 0)}</td>}
                {!hidePrice && <td style={{padding: "10px 12px", borderBottom: "1px solid #f1efeb", textAlign: "right", fontSize: 11, fontWeight: 600, color: "#0c0a09"}}>{fmtM((Number(l.qty)||0) * (Number(l.price)||0))}</td>}
              </tr>
            ))}
          </tbody>
        </table>

        {/* Totals + notes */}
        <div style={{display: "grid", gridTemplateColumns: hidePrice ? "1fr" : "1.1fr 1fr", gap: 24, marginTop: 18}}>
          <div style={{fontSize: 10.5, color: "#57534e", lineHeight: 1.6, whiteSpace: "pre-line"}}>
            <div style={{textTransform: "uppercase", fontSize: 9.5, fontWeight: 700, color: "#44403c", letterSpacing: "0.08em", marginBottom: 6, paddingBottom: 4, borderBottom: `2px solid ${color}`, display: "inline-block"}}>หมายเหตุ</div>
            <div style={{marginTop: 4}}>{notes}</div>
            {cfg.showMethod && cfg.party === "customer" && (
              <div style={{marginTop: 12, padding: "8px 12px", background: "#fefdf9", border: "1px solid #f1efeb", borderRadius: 4, fontSize: 10.5}}>
                <b style={{color: "#0c0a09"}}>ช่องทางชำระเงิน:</b><br/>{C.bank}
              </div>
            )}
          </div>
          {!hidePrice && <div>
            <div style={{padding: "10px 14px", background: "#f7f6f4", borderRadius: "5px 5px 0 0", border: "1px solid #e7e5e4"}}>
              <div style={{display: "flex", justifyContent: "space-between", padding: "3px 0", fontSize: 11.5, color: "#57534e"}}><span>ราคารวมก่อนภาษี</span><span style={{color: "#0c0a09"}}>{fmtM(subtotal)}</span></div>
              {discount > 0 && <div style={{display: "flex", justifyContent: "space-between", padding: "3px 0", fontSize: 11.5, color: "#57534e"}}><span>ส่วนลด</span><span style={{color: "#0c0a09"}}>−{fmtM(discount)}</span></div>}
              {discount > 0 && <div style={{display: "flex", justifyContent: "space-between", padding: "3px 0", fontSize: 11.5, color: "#57534e"}}><span>หลังหักส่วนลด</span><span style={{color: "#0c0a09"}}>{fmtM(afterDisc)}</span></div>}
              {cfg.showVAT && <div style={{display: "flex", justifyContent: "space-between", padding: "3px 0", fontSize: 11.5, color: "#57534e"}}><span>ภาษีมูลค่าเพิ่ม {vatPct}%</span><span style={{color: "#0c0a09"}}>{fmtM(vat)}</span></div>}
            </div>
            <div style={{display: "flex", justifyContent: "space-between", alignItems: "center", padding: "14px 14px", background: color, color: "white", borderRadius: "0 0 5px 5px"}}>
              <span style={{fontSize: 12.5, fontWeight: 500}}>จำนวนเงินรวมทั้งสิ้น</span>
              <span style={{fontSize: 17, fontWeight: 700, fontVariantNumeric: "tabular-nums"}}>฿ {fmtM(grand)}</span>
            </div>
            <div style={{padding: "6px 14px", fontSize: 10.5, color: "#57534e", fontStyle: "italic", textAlign: "right"}}>
              ({bahtText(grand)})
            </div>
            {Number(wht) > 0 && (
              <div style={{marginTop: 4, border: "1px solid #e7e5e4", borderRadius: 5, overflow: "hidden"}}>
                <div style={{display: "flex", justifyContent: "space-between", padding: "5px 14px", fontSize: 11, color: "#57534e", background: "#fbfaf8"}}>
                  <span>หัก ภาษี ณ ที่จ่าย {Number(whtPct) || 0}% (จากยอดก่อน VAT)</span>
                  <span style={{color: "#b91c1c"}}>−{fmtM(wht)}</span>
                </div>
                <div style={{display: "flex", justifyContent: "space-between", padding: "6px 14px", fontSize: 12, fontWeight: 700, color: "#0c0a09", borderTop: "1px solid #e7e5e4"}}>
                  <span>ยอดชำระสุทธิ</span>
                  <span>฿ {fmtM(grand - wht)}</span>
                </div>
              </div>
            )}
            {cfg.showStamp && (
              <div style={{marginTop: 8, textAlign: "center"}}>
                <span style={{display: "inline-block", padding: "6px 22px", border: `2.5px solid ${color}`, color, fontWeight: 700, fontSize: 12, letterSpacing: "0.14em", borderRadius: 4, transform: "rotate(-3deg)"}}>PAID · ชำระแล้ว</span>
              </div>
            )}
          </div>}
        </div>

        {/* Signatures */}
        {cfg.party === "customer" ? (() => {
          const custRole = { "delivery-note": "ผู้รับสินค้า", "invoice": "ผู้รับสินค้า / บริการ", "tax-invoice": "ผู้รับสินค้า / บริการ", "quotation": "ผู้อนุมัติสั่งซื้อ", "billing-note": "ผู้รับวางบิล", "receipt": "ผู้จ่ายเงิน" }[docType] || "ผู้รับเอกสาร";
          const block = (orgName, role, img) => (
            <div style={{textAlign: "center"}}>
              <div style={{fontSize: 10, color: "#57534e", marginBottom: 2}}>ในนาม</div>
              <div style={{fontWeight: 600, color: "#0c0a09", fontSize: 11, minHeight: 28, lineHeight: 1.3}}>{orgName || "—"}</div>
              <div style={{height: 44, display: "flex", alignItems: "flex-end", justifyContent: "center"}}>
                {img ? <img src={img} alt="ลายเซ็น" style={{maxHeight: 42, maxWidth: "72%", objectFit: "contain"}}/> : null}
              </div>
              <div style={{borderTop: "1px dashed #57534e", margin: "0 18px 0", paddingTop: 6}}>
                <div style={{fontWeight: 600, color: "#0c0a09"}}>{role}</div>
                <div style={{color: "#78716c", fontSize: 10}}>วันที่ {img ? date : "____________"}</div>
              </div>
            </div>
          );
          return (
            // ซ้าย: ในนามลูกค้า + วันที่ · กลาง: พื้นที่ปั๊มตราบริษัท · ขวา: ในนามบริษัท + วันที่ (แบบใบกำกับภาษี)
            <div style={{display: "grid", gridTemplateColumns: "1fr 130px 1fr", gap: 18, marginTop: 40, fontSize: 10.5, alignItems: "start"}}>
              {block(party?.name, custRole)}
              <div style={{display: "flex", alignItems: "center", justifyContent: "center", paddingTop: 26}}>
                {stampImg
                  ? <img src={stampImg} alt="ตราประทับ" style={{width: 104, height: 104, objectFit: "contain"}}/>
                  : <div style={{width: 104, height: 104, border: "1.5px dashed #d6d3d1", borderRadius: "50%", display: "grid", placeItems: "center", textAlign: "center", color: "#c9c5c1", fontSize: 9, lineHeight: 1.5}}>
                      <div>ตราประทับ<div>บริษัท</div></div>
                    </div>}
              </div>
              {block(C.name, docType === "receipt" ? "ผู้รับเงิน / มีอำนาจลงนาม" : "ผู้อนุมัติ / มีอำนาจลงนาม", sigImg)}
            </div>
          );
        })() : (
          <div style={{display: "grid", gridTemplateColumns: `repeat(${cfg.showSign.length + (stampImg ? 1 : 0)}, 1fr)`, gap: 30, marginTop: 44, fontSize: 10.5}}>
            {cfg.showSign.map((s, i) => (
              <div key={i} style={{textAlign: "center"}}>
                <div style={{height: 38, display: "flex", alignItems: "flex-end", justifyContent: "center"}}>
                  {i === 0 && sigImg ? <img src={sigImg} alt="ลายเซ็น" style={{maxHeight: 36, maxWidth: "72%", objectFit: "contain"}}/> : null}
                </div>
                <div style={{borderTop: "1px dashed #57534e", paddingTop: 6}}>
                  <div style={{fontWeight: 600, color: "#0c0a09"}}>{s}</div>
                  <div style={{color: "#78716c", fontSize: 10}}>วันที่ {i === 0 && sigImg ? date : "____________"}</div>
                </div>
              </div>
            ))}
            {stampImg && (
              <div style={{textAlign: "center"}}>
                <img src={stampImg} alt="ตราประทับ" style={{width: 84, height: 84, objectFit: "contain"}}/>
                <div style={{color: "#78716c", fontSize: 10, marginTop: 2}}>ตราประทับบริษัท</div>
              </div>
            )}
          </div>
        )}

        {/* Footer */}
        <div style={{marginTop: 28, paddingTop: 10, borderTop: `2px solid ${color}`, fontSize: 9.5, color: "#78716c", display: "flex", justifyContent: "space-between", alignItems: "center"}}>
          <span><b style={{color: "#44403c"}}>{C.name}</b> · {C.website}</span>
          <span>{docNo} · ออกโดยระบบ SSS</span>
        </div>
      </div>
    </div>
  );
}

// =============== Global helper ===============
function NewDocHost() {
  const [docType, setDocType] = useStateN(null);
  const [prefill, setPrefill] = useStateN(null);
  useEffectN(() => {
    window.openNewDoc = (type, pf) => { setPrefill(pf || null); setDocType(type); };
  }, []);
  if (!docType) return null;
  return <NewDocDrawer key={(prefill && prefill.docNo) || docType} docType={docType} prefill={prefill} onClose={() => { setDocType(null); setPrefill(null); }}/>;
}

window.NewDocHost = NewDocHost;
window.DOC_TYPES = DOC_TYPES;
window.NewDocPrintablePaper = PrintablePaper;

// =============== หน้ากู้คืนเอกสาร (ถังกู้คืน 3 วัน) ===============
function RecoveryPage({ navigate }) {
  const { Btn, Badge, Card, PageHead } = window.UI;
  const fmt0 = window.UI.fmt0;
  const [, force] = useStateN(0);
  useEffectN(() => {
    const h = () => force(n => n + 1);
    window.addEventListener("sss-recycle-changed", h);
    window.addEventListener("sss-data-synced", h);
    return () => { window.removeEventListener("sss-recycle-changed", h); window.removeEventListener("sss-data-synced", h); };
  }, []);
  const items = (window.RecycleBin ? window.RecycleBin.list() : []);
  const ttlDays = (window.RecycleBin && window.RecycleBin.TTL_DAYS) || 3;
  const leftLabel = (deletedAt) => {
    const ms = (deletedAt + ttlDays * 86400000) - Date.now();
    if (ms <= 0) return "หมดอายุ";
    const h = Math.floor(ms / 3600000);
    return h >= 24 ? `เหลือ ${Math.floor(h / 24)} วัน ${h % 24} ชม.` : `เหลือ ${h} ชม.`;
  };
  const whenLabel = (ts) => { try { return new Date(ts).toLocaleString("th-TH", { day: "2-digit", month: "short", hour: "2-digit", minute: "2-digit" }); } catch { return ""; } };
  const doRestore = (e) => {
    const r = window.RecycleBin.restore(e.id);
    if (r) { window.logActivity && window.logActivity("กู้คืนเอกสาร", `${e.label} ${e.no}`, "create"); window.toast && window.toast(`กู้คืน ${e.no} แล้ว`); }
  };
  const doPurge = async (e) => {
    const ok = await window.confirmDialog({ title: `ลบถาวร ${e.no}?`, message: "เอกสารนี้จะถูกลบออกจากถังกู้คืนอย่างถาวร — กู้คืนไม่ได้อีก", confirmText: "ลบถาวร", danger: true });
    if (!ok) return;
    window.RecycleBin.purge(e.id);
    window.toast && window.toast(`ลบ ${e.no} ถาวรแล้ว`);
  };
  return (
    <>
      <PageHead title="กู้คืนเอกสาร" sub={`เอกสารที่ลบจะถูกเก็บไว้ ${ttlDays} วัน ก่อนลบถาวรอัตโนมัติ · กู้คืนได้ทุกเครื่อง`} />
      <Card flush padded={false}>
        {items.length === 0 ? (
          <div style={{ padding: 40, textAlign: "center", color: "var(--ink-3)" }}>
            <div style={{ marginBottom: 6 }}><I.trash size={26} /></div>
            ถังกู้คืนว่าง — ยังไม่มีเอกสารที่ถูกลบใน {ttlDays} วันที่ผ่านมา
          </div>
        ) : (
          <div className="table-wrap"><table className="t">
            <thead><tr>
              <th>ประเภท</th><th>เลขที่</th><th>คู่ค้า/ผู้รับ</th><th>วันที่เอกสาร</th><th>ลบเมื่อ</th><th>โดย</th><th>คงเหลือ</th><th className="right"></th>
            </tr></thead>
            <tbody>
              {items.map(e => (
                <tr key={e.id}>
                  <td><Badge tone="outline">{e.label}</Badge></td>
                  <td className="mono">{e.no}</td>
                  <td>{e.payee || "—"}</td>
                  <td>{e.date || "—"}</td>
                  <td className="muted" style={{ fontSize: 12 }}>{whenLabel(e.deletedAt)}</td>
                  <td className="muted" style={{ fontSize: 12 }}>{e.deletedBy || "—"}</td>
                  <td><Badge tone="warning" dot>{leftLabel(e.deletedAt)}</Badge></td>
                  <td className="right" onClick={ev => ev.stopPropagation()}>
                    <div style={{ display: "flex", gap: 4, justifyContent: "flex-end" }}>
                      <Btn size="sm" kind="primary" icon={I.check} onClick={() => doRestore(e)}>กู้คืน</Btn>
                      <button className="icon-btn" title="ลบถาวร" onClick={() => doPurge(e)}><I.trash size={13} /></button>
                    </div>
                  </td>
                </tr>
              ))}
            </tbody>
          </table></div>
        )}
      </Card>

      {(() => {
        const tombs = window.RecycleBin ? window.RecycleBin.tombstones() : [];
        if (tombs.length === 0) return null;
        const doCloud = async (t) => {
          const ok = await window.confirmDialog({ title: `กู้ ${t.no} จากคลาวด์?`, message: `จะปลดสถานะ "ลบ" ของ <b>${t.label} ${t.no}</b> แล้วดึงข้อมูลกลับจากคลาวด์ (ถ้ายังมีอยู่บนเซิร์ฟเวอร์)`, confirmText: "กู้จากคลาวด์" });
          if (!ok) return;
          window.RecycleBin.restoreTombstone(t.key);
          window.logActivity && window.logActivity("กู้คืนเอกสาร (คลาวด์)", `${t.label} ${t.no}`, "create");
          window.toast && window.toast(`กำลังดึง ${t.no} กลับจากคลาวด์… ถ้ายังมีอยู่จะปรากฏในไม่กี่วินาที`);
        };
        const doForget = async (t) => {
          const ok = await window.confirmDialog({ title: `ลบ ${t.no} ถาวร?`, message: "ตัดออกจากรายการกู้คืน — ยืนยันว่าลบถาวร", confirmText: "ลบถาวร", danger: true });
          if (!ok) return;
          window.RecycleBin.forgetTombstone(t.key);
        };
        return (
          <Card title="เอกสารที่ลบก่อนหน้านี้ — กู้จากคลาวด์" sub="รายการที่ถูกลบก่อนมีถังกู้คืน · ตัวเอกสารอาจยังอยู่บนเซิร์ฟเวอร์ กดกู้เพื่อดึงกลับ" flush padded={false} className="mt-lg">
            <div className="table-wrap"><table className="t">
              <thead><tr><th>ประเภท</th><th>เลขที่</th><th className="right"></th></tr></thead>
              <tbody>
                {tombs.map(t => (
                  <tr key={t.key}>
                    <td><Badge tone="outline">{t.label}</Badge></td>
                    <td className="mono">{t.no}</td>
                    <td className="right" onClick={ev => ev.stopPropagation()}>
                      <div style={{ display: "flex", gap: 4, justifyContent: "flex-end" }}>
                        <Btn size="sm" kind="primary" icon={I.download} onClick={() => doCloud(t)}>กู้จากคลาวด์</Btn>
                        <button className="icon-btn" title="ลบถาวร" onClick={() => doForget(t)}><I.trash size={13} /></button>
                      </div>
                    </td>
                  </tr>
                ))}
              </tbody>
            </table></div>
          </Card>
        );
      })()}
    </>
  );
}
window.RecoveryPage = RecoveryPage;
