// BOQ & Quotation — New full-featured editor with drafts, cover pages, Excel I/O, contract conversion

function BOQPage({ lang }) {
  const [view, setView] = useState("list"); // list | editor | preview | drive
  const [drafts, setDrafts] = useState([]);
  const [currentDraft, setCurrentDraft] = useState(null); // BOQ being edited or previewed
  const [loading, setLoading] = useState(false);
  const toast = useToast();

  // ── Load drafts list from DB ────────────────────────────────
  const reloadDrafts = useCallback(async () => {
    if (!window.electron?.boqDraftList) return;
    setLoading(true);
    try {
      const list = await window.electron.boqDraftList();
      setDrafts(list || []);
    } finally { setLoading(false); }
  }, []);

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

  // ── Editor actions ──────────────────────────────────────────
  const openEditor = (draft = null) => {
    setCurrentDraft(draft || emptyDraft());
    setView("editor");
  };
  const openPreview = async (id) => {
    if (!window.electron?.boqDraftGet) return;
    const d = await window.electron.boqDraftGet(id);
    if (d) {
      d.data = d.data_json ? JSON.parse(d.data_json) : { groups: [] };
      setCurrentDraft(d);
      setView("preview");
    }
  };
  const editDraft = async (id) => {
    const d = await window.electron.boqDraftGet(id);
    if (d) {
      d.data = d.data_json ? JSON.parse(d.data_json) : { groups: [] };
      setCurrentDraft(d);
      setView("editor");
    }
  };
  const duplicateDraft = async (id) => {
    const r = await window.electron.boqDraftDuplicate(id);
    if (r.success) { toast(lang === "th" ? "ทำสำเนาแล้ว" : "Duplicated", "success"); reloadDrafts(); }
    else toast((lang === "th" ? "ผิดพลาด: " : "Error: ") + r.error, "danger");
  };
  const deleteDraft = async (id) => {
    if (!confirm(lang === "th" ? "ยืนยันลบ BOQ นี้?" : "Delete this BOQ?")) return;
    const r = await window.electron.boqDraftDelete(id);
    if (r.success) { toast(lang === "th" ? "ลบแล้ว" : "Deleted", "success"); reloadDrafts(); }
  };
  const convertToContract = async (id) => {
    if (!confirm(lang === "th" ? "สร้างสัญญาจาก BOQ นี้?" : "Create contract from this BOQ?")) return;
    const r = await window.electron.boqDraftToContract(id);
    if (r.success) {
      // Refresh in-memory CONTRACTS array so contracts page sees the new contract
      if (window.loadRealData) await window.loadRealData();
      if (window.__triggerReload) window.__triggerReload();
      toast(lang === "th" ? `สร้างสัญญาสำเร็จ (ID: ${r.contractId}) — ไปดูที่ Tab สัญญา` : `Contract created (ID: ${r.contractId}) — see Contracts tab`, "success");
      reloadDrafts();
    } else toast((lang === "th" ? "ผิดพลาด: " : "Error: ") + r.error, "danger");
  };
  const exportExcel = async (id) => {
    const r = await window.electron.boqExportFilled(id);
    if (r.success) toast(lang === "th" ? `บันทึกที่: ${r.filePath}` : `Saved: ${r.filePath}`, "success");
    else if (!r.canceled) toast((lang === "th" ? "ผิดพลาด: " : "Error: ") + r.error, "danger");
  };
  const downloadTemplate = async () => {
    const r = await window.electron.boqExportTemplate({});
    if (r.success) toast(lang === "th" ? `ดาวน์โหลด Template แล้ว: ${r.filePath}` : `Template saved: ${r.filePath}`, "success");
    else if (!r.canceled) toast((lang === "th" ? "ผิดพลาด: " : "Error: ") + r.error, "danger");
  };
  const importExcel = async () => {
    const r = await window.electron.boqImportExcel();
    if (r.success) {
      const draft = {
        ...emptyDraft(),
        ...r.data,
        name: r.fileName?.replace(/\.xlsx?$/i, '') || 'Imported BOQ',
        source: 'excel',
        data: { groups: r.data.groups || [] },
      };
      setCurrentDraft(draft);
      setView("editor");
      toast(lang === "th" ? "นำเข้าจาก Excel สำเร็จ - ตรวจสอบและบันทึก" : "Imported - review and save", "success");
    } else if (!r.canceled) toast((lang === "th" ? "ผิดพลาด: " : "Error: ") + r.error, "danger");
  };

  return (
    <div className="anim-fadein">
      <PageHeader
        title={lang === "th" ? "BOQ & ใบเสนอราคา" : "BOQ & Quotation"}
        subtitle={lang === "th" ? "สร้าง แก้ไข บันทึกฉบับร่าง พรีวิว Import/Export Excel และส่งต่อเป็นสัญญา" : "Create, edit, draft, preview, Excel I/O, convert to contract"}
        actions={<>
          <button className="btn btn-sm" onClick={downloadTemplate}><I.download /> {lang === "th" ? "Template Excel" : "Template"}</button>
          <button className="btn btn-sm" onClick={importExcel}><I.upload /> {lang === "th" ? "Import Excel" : "Import"}</button>
          <button className="btn btn-sm" onClick={() => setView("drive")}><I.drive /> Drive</button>
          <button className="btn btn-sm btn-primary" onClick={() => openEditor()}><I.plus /> {lang === "th" ? "สร้างใหม่" : "New"}</button>
        </>}
      />

      {/* Tab bar */}
      <div style={{ display: "flex", gap: 6, marginBottom: 16, padding: 4, background: "var(--glass)", border: "1px solid var(--line)", borderRadius: 12, width: "fit-content" }}>
        {[
          { id: "list",    th: `รายการ (${drafts.length})`, en: `List (${drafts.length})` },
          { id: "editor",  th: "ตัวแก้ไข",              en: "Editor",      hidden: !currentDraft && view !== "editor" },
          { id: "preview", th: "พรีวิว",                en: "Preview",     hidden: !currentDraft || view !== "preview" },
          { id: "drive",   th: "Drive ย้อนหลัง",         en: "Drive history" },
        ].filter(t => !t.hidden).map((t) => (
          <button key={t.id} onClick={() => { if (t.id === "list") { setView("list"); } else if (t.id === "editor") setView("editor"); else if (t.id === "preview") setView("preview"); else setView("drive"); }}
            style={{
              padding: "8px 14px", borderRadius: 8, border: 0, cursor: "pointer",
              background: view === t.id ? "var(--glass-3)" : "transparent",
              color: view === t.id ? "var(--ink)" : "var(--ink-soft)",
              fontSize: 13, fontWeight: 500,
            }}>{lang === "th" ? t.th : t.en}</button>
        ))}
      </div>

      {view === "list" && (
        <BOQList drafts={drafts} loading={loading} lang={lang}
          onEdit={editDraft} onPreview={openPreview} onDuplicate={duplicateDraft}
          onDelete={deleteDraft} onConvert={convertToContract} onExport={exportExcel}
          onNew={() => openEditor()} />
      )}
      {view === "editor" && currentDraft && (
        <BOQEditor draft={currentDraft} lang={lang}
          onSaved={(savedId) => { reloadDrafts(); }}
          onClose={() => { setView("list"); reloadDrafts(); }}
          onPreview={(d) => { setCurrentDraft(d); setView("preview"); }}
          onConvertContract={(id) => convertToContract(id)}
          onExportExcel={(id) => exportExcel(id)}
        />
      )}
      {view === "preview" && currentDraft && (
        <BOQPreview draft={currentDraft} lang={lang} onEdit={() => setView("editor")} onClose={() => setView("list")} />
      )}
      {view === "drive" && <BOQDriveView lang={lang} />}
    </div>
  );
}

// ───────────────────────────────────────────────────────────────
// LIST
// ───────────────────────────────────────────────────────────────
function BOQList({ drafts, loading, lang, onEdit, onPreview, onDuplicate, onDelete, onConvert, onExport, onNew }) {
  const [filter, setFilter] = useState("all"); // all | draft | finalized | converted
  const [search, setSearch] = useState("");

  const filtered = drafts.filter(d => {
    if (filter === "draft" && d.status !== "draft") return false;
    if (filter === "finalized" && d.status !== "finalized") return false;
    if (filter === "converted" && d.status !== "converted_to_contract") return false;
    if (search) {
      const s = search.toLowerCase();
      if (!(d.name || '').toLowerCase().includes(s) &&
          !(d.boq_number || '').toLowerCase().includes(s) &&
          !(d.project_name || '').toLowerCase().includes(s) &&
          !(d.client_name || '').toLowerCase().includes(s)) return false;
    }
    return true;
  });

  if (loading) return <div className="card" style={{ textAlign: "center", padding: 40, color: "var(--ink-mute)" }}>Loading…</div>;

  return (
    <div>
      {/* Filter bar */}
      <div className="card card-tight" style={{ display: "flex", gap: 10, alignItems: "center", marginBottom: 14, flexWrap: "wrap" }}>
        <div style={{ position: "relative", flex: "0 1 280px" }}>
          <span style={{ position: "absolute", left: 12, top: "50%", transform: "translateY(-50%)", color: "var(--ink-mute)" }}><I.search /></span>
          <input className="field" placeholder={lang === "th" ? "ค้นหา BOQ, โครงการ, ลูกค้า…" : "Search…"}
            value={search} onChange={(e) => setSearch(e.target.value)} style={{ paddingLeft: 36, height: 36 }} />
        </div>
        <div style={{ display: "flex", gap: 4 }}>
          {[
            { id: "all", th: "ทั้งหมด", en: "All" },
            { id: "draft", th: "ฉบับร่าง", en: "Draft" },
            { id: "finalized", th: "เสร็จสมบูรณ์", en: "Finalized" },
            { id: "converted", th: "แปลงเป็นสัญญา", en: "Converted" },
          ].map(f => (
            <button key={f.id} onClick={() => setFilter(f.id)}
              style={{
                padding: "6px 12px", borderRadius: 8, border: 0, cursor: "pointer",
                background: filter === f.id ? "var(--syk-blue)" : "var(--glass-2)",
                color: filter === f.id ? "white" : "var(--ink-soft)",
                fontSize: 12, fontWeight: 500,
              }}>{lang === "th" ? f.th : f.en}</button>
          ))}
        </div>
      </div>

      {filtered.length === 0 ? (
        <div className="card" style={{ textAlign: "center", padding: 60, color: "var(--ink-mute)" }}>
          <I.doc size={36} />
          <div style={{ marginTop: 14, fontSize: 14 }}>{lang === "th" ? "ยังไม่มี BOQ" : "No BOQs yet"}</div>
          <button className="btn btn-primary" style={{ marginTop: 18 }} onClick={onNew}><I.plus /> {lang === "th" ? "สร้าง BOQ แรก" : "Create first BOQ"}</button>
        </div>
      ) : (
        <div className="card" style={{ padding: 0, overflow: "hidden" }}>
          <table className="table">
            <thead><tr>
              <th>{lang === "th" ? "ชื่อ BOQ" : "Name"}</th>
              <th>{lang === "th" ? "เลขที่" : "Number"}</th>
              <th>{lang === "th" ? "โครงการ" : "Project"}</th>
              <th>{lang === "th" ? "ลูกค้า" : "Client"}</th>
              <th style={{ textAlign: "right" }}>{lang === "th" ? "ยอดรวม" : "Total"}</th>
              <th>{lang === "th" ? "สถานะ" : "Status"}</th>
              <th>{lang === "th" ? "ที่มา" : "Source"}</th>
              <th>{lang === "th" ? "อัพเดท" : "Updated"}</th>
              <th></th>
            </tr></thead>
            <tbody>
              {filtered.map(d => (
                <tr key={d.id} className="row-int" onClick={() => onPreview(d.id)} style={{ cursor: "pointer" }}>
                  <td style={{ fontWeight: 500 }}>{d.name || '—'}</td>
                  <td className="mono" style={{ fontSize: 12, color: "var(--syk-blue-soft)" }}>{d.boq_number || '—'}</td>
                  <td style={{ fontSize: 12.5 }}>{d.project_name || '—'}</td>
                  <td style={{ fontSize: 12.5 }}>{d.client_name || '—'}</td>
                  <td className="mono" style={{ textAlign: "right", fontWeight: 600 }}>{fmtTHB(d.total_thb || 0)}</td>
                  <td><StatusBadge status={d.status === 'converted_to_contract' ? 'approved' : d.status} /></td>
                  <td><span className="chip" style={{ fontSize: 10 }}>{d.source || 'manual'}</span></td>
                  <td className="ink-soft mono" style={{ fontSize: 11 }}>{d.updated_at?.split('T')[0] || '—'}</td>
                  <td style={{ textAlign: "right", whiteSpace: "nowrap" }} onClick={(e) => e.stopPropagation()}>
                    <button className="btn btn-sm btn-ghost" title={lang === "th" ? "พรีวิว" : "Preview"} onClick={() => onPreview(d.id)}><I.eye /></button>
                    <button className="btn btn-sm btn-ghost" title={lang === "th" ? "แก้ไข" : "Edit"} onClick={() => onEdit(d.id)}><I.edit /></button>
                    <button className="btn btn-sm btn-ghost" title={lang === "th" ? "Export Excel" : "Export"} onClick={() => onExport(d.id)}><I.download /></button>
                    <button className="btn btn-sm btn-ghost" title={lang === "th" ? "ทำสำเนา" : "Duplicate"} onClick={() => onDuplicate(d.id)}><I.copy /></button>
                    <button className="btn btn-sm btn-ghost" title={lang === "th" ? "→ สัญญา" : "→ Contract"} onClick={() => onConvert(d.id)} style={{ color: "var(--emerald)" }}><I.shield /></button>
                    <button className="btn btn-sm btn-ghost" title={lang === "th" ? "ลบ" : "Delete"} onClick={() => onDelete(d.id)} style={{ color: "var(--rose)" }}><I.trash /></button>
                  </td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      )}
    </div>
  );
}

// ───────────────────────────────────────────────────────────────
// EDITOR — full editor with free-form line items + cover pages
// ───────────────────────────────────────────────────────────────
function BOQEditor({ draft, lang, onSaved, onClose, onPreview, onConvertContract, onExportExcel }) {
  const dirtyRef = useRef(false);
  const savingRef = useRef(false); // suppress auto-dirty trigger while save() updates state
  // Helper: ensure every group/item has a stable _id (for React keys)
  const ensureIds = (groups) => (groups || []).map(g => ({
    _id: g._id || `g_${Date.now()}_${Math.random().toString(36).slice(2, 6)}`,
    name: g.name || '',
    coverPageEnabled: g.coverPageEnabled || false,
    coverPageNote: g.coverPageNote || '',
    items: (g.items || []).map((it, i) => ({
      _id: it._id || `it_${Date.now()}_${i}_${Math.random().toString(36).slice(2, 6)}`,
      ...it,
    })),
  }));

  // Mark dirty whenever d state changes (after first render)
  const markDirty = useCallback(() => {
    if (!dirtyRef.current) {
      dirtyRef.current = true;
      window.__setDirty?.(true, lang === 'th' ? 'BOQ Editor' : 'BOQ Editor');
    }
  }, [lang]);
  const markClean = useCallback(() => {
    dirtyRef.current = false;
    window.__setDirty?.(false);
  }, []);
  // Clear dirty when editor unmounts (e.g., after Save→Close flow)
  useEffect(() => () => { window.__setDirty?.(false); }, []);

  const [d, setD] = useState(() => ({
    id: draft.id,
    name: draft.name || '',
    boq_number: draft.boq_number || autoNumber(),
    project_id: draft.project_id || null,
    project_name: draft.project_name || '',
    client_name: draft.client_name || '',
    site: draft.site || '',
    area: draft.area || 0,
    margin_pct: draft.margin_pct ?? 12,
    valid_days: draft.valid_days ?? 30,
    payment_terms: draft.payment_terms || '',
    notes: draft.notes || '',
    status: draft.status || 'draft',
    source: draft.source || 'manual',
    include_vat: draft.include_vat === false || draft.include_vat === 0 ? false : true,
    vat_pct: draft.vat_pct ?? 7,
    retention_pct: draft.retention_pct ?? 5,
    wht_pct: draft.wht_pct ?? 3,
    data: { groups: ensureIds(draft.data?.groups) },
  }));
  const toast = useToast();
  const [saving, setSaving] = useState(false);
  const [section, setSection] = useState("info"); // info | items | terms

  // Auto-save indicator
  const [lastSavedAt, setLastSavedAt] = useState(null);
  // Track first render to skip initial "dirty" trigger
  const firstRender = useRef(true);
  useEffect(() => {
    if (firstRender.current) { firstRender.current = false; return; }
    if (savingRef.current) return; // skip during save — id update isn't user input
    markDirty();
  }, [d, markDirty]);

  // ── Totals computation ─────────────────────────────────────
  const totals = useMemo(() => {
    let subtotal = 0;
    let totalLab = 0;
    let totalMat = 0;
    for (const g of d.data.groups) {
      for (const it of (g.items || [])) {
        const qty = +it.qty || 0;
        const mat = +it.mat || 0;
        const lab = +it.lab || 0;
        subtotal += qty * (mat + lab);
        totalMat += qty * mat;
        totalLab += qty * lab;
      }
    }
    const margin = subtotal * (d.margin_pct || 0) / 100;
    const beforeVAT = subtotal + margin;
    const vatPct = d.vat_pct ?? 7;
    const vat = d.include_vat ? beforeVAT * vatPct / 100 : 0;
    const total = beforeVAT + vat;
    return { subtotal, margin, beforeVAT, vat, total, totalMat, totalLab, vatPct };
  }, [d.data, d.margin_pct, d.include_vat, d.vat_pct]);

  // ── Group/item actions ─────────────────────────────────────
  const addGroup = useCallback(() => {
    setD(prev => {
      const newG = { _id: `g_${Date.now()}_${Math.random().toString(36).slice(2, 6)}`, name: `${prev.data.groups.length + 1}. หมวดงานใหม่`, items: [], coverPageNote: '', coverPageEnabled: false };
      return { ...prev, data: { ...prev.data, groups: [...prev.data.groups, newG] } };
    });
  }, []);
  const removeGroup = useCallback((gi) => {
    if (!confirm(lang === "th" ? "ลบหมวดนี้?" : "Delete category?")) return;
    setD(prev => {
      const newGroups = [...prev.data.groups];
      newGroups.splice(gi, 1);
      return { ...prev, data: { ...prev.data, groups: newGroups } };
    });
  }, [lang]);
  const updateGroup = useCallback((gi, patch) => {
    setD(prev => {
      const newGroups = [...prev.data.groups];
      newGroups[gi] = { ...newGroups[gi], ...patch };
      return { ...prev, data: { ...prev.data, groups: newGroups } };
    });
  }, []);
  const moveGroup = useCallback((gi, dir) => {
    setD(prev => {
      const newGroups = [...prev.data.groups];
      const tgt = gi + dir;
      if (tgt < 0 || tgt >= newGroups.length) return prev;
      [newGroups[gi], newGroups[tgt]] = [newGroups[tgt], newGroups[gi]];
      return { ...prev, data: { ...prev.data, groups: newGroups } };
    });
  }, []);
  const addItem = useCallback((gi) => {
    setD(prev => {
      const newGroups = [...prev.data.groups];
      const g = { ...newGroups[gi] };
      const items = [...(g.items || [])];
      const nextNo = `${gi + 1}.${items.length + 1}`;
      items.push({ _id: `it_${Date.now()}_${Math.random().toString(36).slice(2, 6)}`, no: nextNo, desc: '', qty: '', unit: '', mat: '', lab: '', note: '' });
      g.items = items;
      newGroups[gi] = g;
      return { ...prev, data: { ...prev.data, groups: newGroups } };
    });
  }, []);
  const updateItem = useCallback((gi, ii, patch) => {
    setD(prev => {
      const newGroups = [...prev.data.groups];
      const g = { ...newGroups[gi] };
      const items = [...(g.items || [])];
      items[ii] = { ...items[ii], ...patch };
      g.items = items;
      newGroups[gi] = g;
      return { ...prev, data: { ...prev.data, groups: newGroups } };
    });
  }, []);
  const removeItem = useCallback((gi, ii) => {
    setD(prev => {
      const newGroups = [...prev.data.groups];
      const g = { ...newGroups[gi] };
      const items = [...(g.items || [])];
      items.splice(ii, 1);
      g.items = items;
      newGroups[gi] = g;
      return { ...prev, data: { ...prev.data, groups: newGroups } };
    });
  }, []);
  const moveItem = useCallback((gi, ii, dir) => {
    setD(prev => {
      const newGroups = [...prev.data.groups];
      const g = { ...newGroups[gi] };
      const items = [...(g.items || [])];
      const tgt = ii + dir;
      if (tgt < 0 || tgt >= items.length) return prev;
      [items[ii], items[tgt]] = [items[tgt], items[ii]];
      g.items = items;
      newGroups[gi] = g;
      return { ...prev, data: { ...prev.data, groups: newGroups } };
    });
  }, []);

  // ── Save (single unified button) ─────────────────────────────
  // Uses d.status as-is (user controls status via dropdown in Info section)
  const save = async () => {
    setSaving(true);
    savingRef.current = true; // prevent useEffect from marking dirty on the id update
    try {
      const isFinal = d.status && d.status !== 'draft';
      const payload = {
        ...d,
        subtotal_thb: totals.subtotal,
        total_thb: totals.total,
        is_draft: !isFinal,
        status: d.status || 'draft',
      };
      const r = await window.electron.boqDraftSave(payload);
      if (r.success) {
        setD(prev => ({ ...prev, id: r.id }));
        setLastSavedAt(new Date());
        markClean();
        const statusLabel = {
          'draft': 'ฉบับร่าง',
          'review': 'รอตรวจสอบ',
          'finalized': 'ฉบับสมบูรณ์',
          'sent': 'ส่งให้ลูกค้าแล้ว',
          'approved': 'อนุมัติแล้ว',
        }[payload.status] || payload.status;
        toast(lang === "th" ? `บันทึก (${statusLabel}) สำเร็จ` : `Saved (${payload.status})`, "success");
        onSaved?.(r.id);
      } else toast((lang === "th" ? "ผิดพลาด: " : "Error: ") + r.error, "danger");
    } finally {
      setSaving(false);
      // Allow next user-driven state changes to mark dirty again
      setTimeout(() => { savingRef.current = false; }, 150);
    }
  };

  const handlePreview = async () => {
    if (!d.id) {
      // Save first
      await save();
    }
    onPreview({ ...d, subtotal_thb: totals.subtotal, total_thb: totals.total });
  };

  const handleConvertContract = async () => {
    if (!d.id) {
      toast(lang === "th" ? "กรุณาบันทึกก่อน" : "Save first", "warn");
      return;
    }
    await save();
    onConvertContract(d.id);
  };

  const handleExportExcel = async () => {
    if (!d.id) await save();
    onExportExcel(d.id || draft.id);
  };

  return (
    <div className="anim-fadein">
      {/* Editor toolbar */}
      <div className="card card-tight" style={{ display: "flex", gap: 8, alignItems: "center", marginBottom: 14, flexWrap: "wrap" }}>
        <div style={{ display: "flex", gap: 4 }}>
          {[
            { id: "info", th: "ข้อมูลทั่วไป", en: "Info", icon: "doc" },
            { id: "items", th: `รายการ (${d.data.groups.reduce((a, g) => a + (g.items?.length || 0), 0)})`, en: `Items (${d.data.groups.reduce((a, g) => a + (g.items?.length || 0), 0)})`, icon: "grid" },
            { id: "terms", th: "เงื่อนไข & สรุป", en: "Terms & Total", icon: "receipt" },
          ].map(s => {
            const Icon = I[s.icon] || I.dot;
            return (
              <button key={s.id} onClick={() => setSection(s.id)}
                style={{
                  display: "flex", alignItems: "center", gap: 6,
                  padding: "8px 14px", borderRadius: 8, border: 0, cursor: "pointer",
                  background: section === s.id ? "var(--syk-blue)" : "transparent",
                  color: section === s.id ? "white" : "var(--ink-soft)",
                  fontSize: 12.5, fontWeight: 500,
                }}><Icon /> {lang === "th" ? s.th : s.en}</button>
            );
          })}
        </div>
        <div style={{ flex: 1 }} />
        {lastSavedAt && (
          <span className="ink-mute" style={{ fontSize: 11 }}>
            {lang === "th" ? "บันทึกล่าสุด:" : "Last saved:"} {lastSavedAt.toLocaleTimeString()}
          </span>
        )}
        <button className="btn btn-sm" onClick={handleExportExcel}><I.download /> Excel</button>
        <button className="btn btn-sm" onClick={async () => {
          if (!d.id) await save();
          const r = await window.electron.boqExportPdf(d.id || draft.id);
          if (r.success) toast(lang === "th" ? `บันทึก PDF แล้ว: ${r.filePath}` : `PDF saved: ${r.filePath}`, "success");
          else if (!r.canceled) toast((lang === "th" ? "PDF Error: " : "PDF Error: ") + r.error, "danger");
        }}><I.pdf /> PDF</button>
        <button className="btn btn-sm" onClick={handlePreview}><I.eye /> {lang === "th" ? "พรีวิว" : "Preview"}</button>
        <button className="btn btn-sm" style={{ color: "var(--emerald)" }} onClick={handleConvertContract}><I.shield /> {lang === "th" ? "→ สัญญา" : "→ Contract"}</button>
        <button className="btn btn-sm btn-primary" onClick={save} disabled={saving} style={{ minWidth: 100 }}>
          <I.save /> {saving ? (lang === "th" ? "กำลังบันทึก..." : "Saving...") : (lang === "th" ? "บันทึก" : "Save")}
        </button>
        <button className="btn btn-sm btn-ghost" onClick={() => {
          if (window.__confirmLeave && !window.__confirmLeave(lang === "th" ? "ปิดตัวแก้ไข BOQ?" : "Close BOQ editor?")) return;
          markClean();
          onClose();
        }} title="Close"><I.x /></button>
      </div>

      {/* Section content */}
      {section === "info" && (
        <div className="card anim-fadein">
          <h3 className="h2" style={{ marginBottom: 18 }}>{lang === "th" ? "ข้อมูล BOQ" : "BOQ Information"}</h3>
          <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 14 }}>
            {/* Work type — datalist combobox: pick from list OR type freely */}
            <div style={{ display: "flex", flexDirection: "column", gap: 6 }}>
              <label style={{ fontSize: 11.5, color: "var(--ink-soft)" }}>
                {lang === "th" ? "ประเภทงาน" : "Work type"}
                <span style={{ color: "var(--syk-blue-soft)", marginLeft: 6, fontSize: 10 }}>(เลือกจากรายการ หรือพิมพ์เอง)</span>
              </label>
              <input className="field" list="syk-work-types"
                value={d.name} onChange={(e) => setD(prev => ({ ...prev, name: e.target.value }))}
                placeholder={lang === "th" ? "▼ เลือกประเภทงาน..." : "Select work type..."} />
              <datalist id="syk-work-types">
                <option value="สร้างใหม่ - บ้านพักอาศัย" />
                <option value="สร้างใหม่ - อาคารพาณิชย์" />
                <option value="สร้างใหม่ - โรงงาน" />
                <option value="สร้างใหม่ - โกดัง / คลังสินค้า" />
                <option value="ต่อเติม - ห้องครัว" />
                <option value="ต่อเติม - โรงรถ" />
                <option value="ต่อเติม - ชั้น 2" />
                <option value="ต่อเติม - หลังคา" />
                <option value="ซ่อมแซม - โครงสร้าง" />
                <option value="ซ่อมแซม - หลังคารั่ว" />
                <option value="ซ่อมแซม - ระบบประปา" />
                <option value="ซ่อมแซม - ระบบไฟฟ้า" />
                <option value="ปรับปรุง / รีโนเวท - บ้าน" />
                <option value="ปรับปรุง / รีโนเวท - สำนักงาน" />
                <option value="ปรับปรุง / รีโนเวท - ร้านค้า" />
                <option value="ตกแต่งภายใน - บ้าน" />
                <option value="ตกแต่งภายใน - คอนโด" />
                <option value="ตกแต่งภายใน - ออฟฟิศ" />
                <option value="งานระบบ - ไฟฟ้า" />
                <option value="งานระบบ - ประปา / สุขาภิบาล" />
                <option value="งานระบบ - แอร์ / ระบายอากาศ" />
                <option value="งานภูมิทัศน์ - สวน" />
                <option value="งานภูมิทัศน์ - รั้ว" />
                <option value="งานภูมิทัศน์ - ลานจอด / ทางเดิน" />
                <option value="งานสระว่ายน้ำ" />
                <option value="งานรื้อถอน" />
                <option value="งานโครงสร้างเฉพาะ - ฐานราก / เสาเข็ม" />
                <option value="งานโครงสร้างเฉพาะ - ลิฟต์" />
                <option value="อื่นๆ" />
              </datalist>
            </div>
            <div style={{ display: "flex", flexDirection: "column", gap: 6 }}>
              <label style={{ fontSize: 11.5, color: "var(--ink-soft)" }}>{lang === "th" ? "เลขที่ BOQ" : "BOQ number"}</label>
              <input className="field" value={d.boq_number}
                onChange={(e) => setD(prev => ({ ...prev, boq_number: e.target.value }))} />
            </div>
            {/* Status dropdown — replaces 2nd save button */}
            <div style={{ display: "flex", flexDirection: "column", gap: 6, gridColumn: "span 2" }}>
              <label style={{ fontSize: 11.5, color: "var(--ink-soft)", display: "flex", justifyContent: "space-between" }}>
                <span>{lang === "th" ? "สถานะเอกสาร" : "Document status"}</span>
                <span style={{ color: "var(--ink-mute)", fontSize: 10 }}>
                  {lang === "th" ? "ปุ่ม \"บันทึก\" จะใช้สถานะนี้" : "Save button uses this status"}
                </span>
              </label>
              <div style={{ display: "flex", gap: 6, flexWrap: "wrap" }}>
                {[
                  { v: 'draft',     th: '📝 ฉบับร่าง',           en: 'Draft',     color: '#9ca3af' },
                  { v: 'review',    th: '🔍 รอตรวจสอบ',          en: 'Review',    color: '#fbbf24' },
                  { v: 'finalized', th: '✅ ฉบับสมบูรณ์ (ล็อค)', en: 'Finalized', color: '#4ade80' },
                  { v: 'sent',      th: '📤 ส่งให้ลูกค้าแล้ว',   en: 'Sent',      color: '#60a5fa' },
                  { v: 'approved',  th: '✓ ลูกค้าอนุมัติ',       en: 'Approved',  color: '#22c55e' },
                ].map(s => (
                  <button key={s.v} type="button"
                    onClick={() => setD(prev => ({ ...prev, status: s.v }))}
                    style={{
                      padding: "8px 14px", borderRadius: 8, cursor: "pointer",
                      border: "1px solid " + (d.status === s.v ? s.color : "var(--line)"),
                      background: d.status === s.v ? `${s.color}22` : "var(--glass-2)",
                      color: d.status === s.v ? s.color : "var(--ink-soft)",
                      fontSize: 12, fontWeight: 600,
                      transition: "all .15s",
                    }}>
                    {lang === "th" ? s.th : s.en}
                  </button>
                ))}
              </div>
            </div>
            <div style={{ display: "flex", flexDirection: "column", gap: 6 }}>
              <label style={{ fontSize: 11.5, color: "var(--ink-soft)" }}>{lang === "th" ? "ชื่อโครงการ" : "Project name"}</label>
              <input className="field" value={d.project_name}
                onChange={(e) => setD(prev => ({ ...prev, project_name: e.target.value }))} />
            </div>
            <div style={{ display: "flex", flexDirection: "column", gap: 6 }}>
              <label style={{ fontSize: 11.5, color: "var(--ink-soft)" }}>{lang === "th" ? "ชื่อลูกค้า" : "Client"}</label>
              <input className="field" value={d.client_name}
                onChange={(e) => setD(prev => ({ ...prev, client_name: e.target.value }))} />
            </div>
            <div style={{ display: "flex", flexDirection: "column", gap: 6 }}>
              <label style={{ fontSize: 11.5, color: "var(--ink-soft)" }}>{lang === "th" ? "ที่ตั้งโครงการ" : "Site"}</label>
              <input className="field" value={d.site}
                onChange={(e) => setD(prev => ({ ...prev, site: e.target.value }))} />
            </div>
            <div style={{ display: "flex", flexDirection: "column", gap: 6 }}>
              <label style={{ fontSize: 11.5, color: "var(--ink-soft)" }}>{lang === "th" ? "พื้นที่ใช้สอย (ตร.ม.)" : "Area (sqm)"}</label>
              <input className="field" type="number" value={d.area}
                onChange={(e) => setD(prev => ({ ...prev, area: parseFloat(e.target.value) || 0 }))} />
            </div>
            <div style={{ gridColumn: "span 2", display: "flex", flexDirection: "column", gap: 6 }}>
              <label style={{ fontSize: 11.5, color: "var(--ink-soft)" }}>{lang === "th" ? "หมายเหตุ" : "Notes"}</label>
              <textarea className="field" rows="3" value={d.notes}
                onChange={(e) => setD(prev => ({ ...prev, notes: e.target.value }))}
                placeholder={lang === "th" ? "เช่น ส่งมอบภายใน 6 เดือน รวมงานไฟฟ้า-ประปา" : "e.g. 6-month delivery"} />
            </div>
          </div>
        </div>
      )}

      {section === "items" && (
        <div className="anim-fadein">
          <div className="card card-tight" style={{ marginBottom: 14, display: "flex", justifyContent: "space-between", alignItems: "center" }}>
            <div style={{ fontSize: 12.5, color: "var(--ink-soft)" }}>
              {lang === "th" ? "จัดการหมวดงานและรายการ — แต่ละหมวดมีใบปกแยก สามารถเพิ่ม/ลบ/ย้ายได้อิสระ" : "Manage categories and items"}
            </div>
            <button className="btn btn-primary btn-sm" onClick={addGroup}><I.plus /> {lang === "th" ? "เพิ่มหมวด" : "Add category"}</button>
          </div>

          {d.data.groups.length === 0 ? (
            <div className="card" style={{ textAlign: "center", padding: 60, color: "var(--ink-mute)" }}>
              <I.grid size={36} />
              <div style={{ marginTop: 12 }}>{lang === "th" ? "ยังไม่มีหมวดงาน" : "No categories"}</div>
              <button className="btn btn-primary" style={{ marginTop: 16 }} onClick={addGroup}><I.plus /> {lang === "th" ? "เพิ่มหมวดแรก" : "Add first category"}</button>
            </div>
          ) : (
            <div style={{ display: "flex", flexDirection: "column", gap: 16 }}>
              {d.data.groups.map((g, gi) => (
                <BOQGroupEditor key={g._id || gi} g={g} gi={gi} total={d.data.groups.length} lang={lang}
                  onUpdate={(patch) => updateGroup(gi, patch)}
                  onRemove={() => removeGroup(gi)}
                  onMove={(dir) => moveGroup(gi, dir)}
                  onAddItem={() => addItem(gi)}
                  onUpdateItem={(ii, patch) => updateItem(gi, ii, patch)}
                  onRemoveItem={(ii) => removeItem(gi, ii)}
                  onMoveItem={(ii, dir) => moveItem(gi, ii, dir)}
                />
              ))}
            </div>
          )}
        </div>
      )}

      {section === "terms" && (
        <BOQTermsSection d={d} setD={setD} totals={totals} lang={lang} />
      )}
    </div>
  );
}

// ───────────────────────────────────────────────────────────────
// GROUP EDITOR (with cover page)
// ───────────────────────────────────────────────────────────────
function BOQGroupEditor({ g, gi, total, lang, onUpdate, onRemove, onMove, onAddItem, onUpdateItem, onRemoveItem, onMoveItem }) {
  const items = g.items || [];
  const groupTotal = items.reduce((a, it) => a + (+it.qty || 0) * ((+it.mat || 0) + (+it.lab || 0)), 0);
  // Extract category code from group name (e.g. "A.1 งานดิน" → "A.1")
  const categoryCode = useMemo(() => {
    const m = (g.name || '').match(/^([A-Z]\.\d+|[A-Z]\d*)/);
    return m ? m[1] : '';
  }, [g.name]);

  return (
    <div style={{ borderRadius: 12, border: "1px solid var(--line)", background: "var(--glass)", overflow: "hidden" }}>
      {/* Group header */}
      <div style={{ padding: 12, background: "linear-gradient(90deg, rgba(40,72,255,0.10), rgba(40,72,255,0.02))", borderBottom: "1px solid var(--line)" }}>
        <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
          <input className="field" value={g.name} onChange={(e) => onUpdate({ name: e.target.value })}
            style={{ flex: 1, fontWeight: 600, fontSize: 14, fontFamily: "var(--font-display)" }} />
          <span className="chip">{items.length} {lang === "th" ? "รายการ" : "items"}</span>
          <span className="mono" style={{ fontWeight: 700, fontSize: 14, color: "var(--syk-blue-soft)" }}>{fmtTHB(groupTotal)}</span>
          <button className="btn btn-sm btn-ghost" disabled={gi === 0} onClick={() => onMove(-1)} title="Move up">▲</button>
          <button className="btn btn-sm btn-ghost" disabled={gi === total - 1} onClick={() => onMove(1)} title="Move down">▼</button>
          <button className="btn btn-sm btn-ghost" onClick={onRemove} style={{ color: "var(--rose)" }}><I.trash /></button>
        </div>

        {/* Cover page toggle */}
        <div style={{ display: "flex", alignItems: "center", gap: 12, marginTop: 10 }}>
          <label style={{ display: "flex", alignItems: "center", gap: 6, fontSize: 12, color: "var(--ink-soft)", cursor: "pointer" }}>
            <input type="checkbox" checked={g.coverPageEnabled || false} onChange={(e) => onUpdate({ coverPageEnabled: e.target.checked })} />
            {lang === "th" ? "ใส่ใบปกหมวด" : "Cover page"}
          </label>
          {g.coverPageEnabled && (
            <input className="field" style={{ flex: 1, height: 32, fontSize: 12 }}
              placeholder={lang === "th" ? "ข้อความบนใบปก (เช่น 'หมวดงานโครงสร้าง รายละเอียดงานก่อสร้าง...')" : "Cover page text"}
              value={g.coverPageNote || ''} onChange={(e) => onUpdate({ coverPageNote: e.target.value })} />
          )}
        </div>
      </div>

      {/* Items table */}
      <table className="table" style={{ fontSize: 12.5 }}>
        <thead>
          <tr>
            <th style={{ width: 28 }}></th>
            <th style={{ width: 60 }}>No.</th>
            <th>{lang === "th" ? "รายการ" : "Description"}</th>
            <th style={{ width: 80, textAlign: "right" }}>{lang === "th" ? "ปริมาณ" : "Qty"}</th>
            <th style={{ width: 70 }}>{lang === "th" ? "หน่วย" : "Unit"}</th>
            <th style={{ width: 110, textAlign: "right" }}>{lang === "th" ? "วัสดุ/หน่วย" : "Material"}</th>
            <th style={{ width: 110, textAlign: "right" }}>{lang === "th" ? "แรง/หน่วย" : "Labor"}</th>
            <th style={{ width: 130, textAlign: "right" }}>{lang === "th" ? "รวม" : "Total"}</th>
            <th style={{ width: 40 }}></th>
          </tr>
        </thead>
        <tbody>
          {items.map((it, ii) => (
            <BOQItemRow
              key={it._id || ii}
              item={it} idx={ii}
              isFirst={ii === 0} isLast={ii === items.length - 1}
              lang={lang}
              categoryCode={categoryCode}
              onUpdate={(patch) => onUpdateItem(ii, patch)}
              onRemove={() => onRemoveItem(ii)}
              onMove={(dir) => onMoveItem(ii, dir)}
            />
          ))}
          <tr>
            <td colSpan="9" style={{ padding: 8, textAlign: "center", background: "var(--glass-2)" }}>
              <button className="btn btn-sm" onClick={onAddItem}><I.plus /> {lang === "th" ? "เพิ่มรายการ" : "Add item"}</button>
            </td>
          </tr>
        </tbody>
      </table>
    </div>
  );
}

// ───────────────────────────────────────────────────────────────
// ITEM ROW (isolated for reliable input handling)
// ───────────────────────────────────────────────────────────────
function BOQItemRow({ item, idx, isFirst, isLast, lang, categoryCode, onUpdate, onRemove, onMove }) {
  // Local state for inputs — keeps focus stable across parent re-renders
  // and lets user type strings like "12." without losing intermediate state
  const [local, setLocal] = useState({
    no: item.no ?? '',
    desc: item.desc ?? '',
    qty: item.qty ?? '',
    unit: item.unit ?? '',
    mat: item.mat ?? '',
    lab: item.lab ?? '',
  });
  const [suggestions, setSuggestions] = useState([]);
  const [showSuggest, setShowSuggest] = useState(false);
  const [dropdownPos, setDropdownPos] = useState({ top: 0, left: 0, width: 0 });
  const searchTimer = useRef(null);
  const inputRef = useRef(null);

  // Recompute dropdown position relative to viewport (handles scroll + resize)
  const updateDropdownPos = useCallback(() => {
    if (inputRef.current) {
      const rect = inputRef.current.getBoundingClientRect();
      setDropdownPos({
        top: rect.bottom + 4,
        left: rect.left,
        width: Math.max(rect.width, 460),
      });
    }
  }, []);

  // Update on scroll/resize while dropdown is open
  useEffect(() => {
    if (!showSuggest) return;
    updateDropdownPos();
    const onScroll = () => updateDropdownPos();
    window.addEventListener('scroll', onScroll, true);
    window.addEventListener('resize', onScroll);
    return () => {
      window.removeEventListener('scroll', onScroll, true);
      window.removeEventListener('resize', onScroll);
    };
  }, [showSuggest, updateDropdownPos]);

  // Sync from parent when item identity changes (e.g., row order swap)
  useEffect(() => {
    setLocal({
      no: item.no ?? '',
      desc: item.desc ?? '',
      qty: item.qty ?? '',
      unit: item.unit ?? '',
      mat: item.mat ?? '',
      lab: item.lab ?? '',
    });
  }, [item._id]);

  // Load category-default suggestions when input gets focus (empty)
  const loadCategoryDefaults = async () => {
    if (!categoryCode || !window.electron?.boqPriceByCategory) {
      setSuggestions([]);
      return;
    }
    const r = await window.electron.boqPriceByCategory(categoryCode);
    setSuggestions((r || []).slice(0, 40));
  };

  // Debounced search — prioritize current category, then fallback to global
  const searchPrice = (q) => {
    clearTimeout(searchTimer.current);
    if (!q || q.length < 1) { loadCategoryDefaults(); return; }
    searchTimer.current = setTimeout(async () => {
      if (window.electron?.boqPriceSearch) {
        const results = await window.electron.boqPriceSearch(q);
        // Sort: items in current category first
        const sorted = (results || []).sort((a, b) => {
          if (a.category_code === categoryCode && b.category_code !== categoryCode) return -1;
          if (a.category_code !== categoryCode && b.category_code === categoryCode) return 1;
          return 0;
        });
        setSuggestions(sorted);
      }
    }, 200);
  };

  const applySuggestion = (s) => {
    const patch = {
      desc: s.description,
      unit: s.unit,
      mat: s.material_price,
      lab: s.labor_price,
    };
    setLocal(prev => ({ ...prev, ...patch }));
    onUpdate(patch);
    setShowSuggest(false);
  };

  const deleteSuggestion = async (e, suggestionId) => {
    e.stopPropagation();
    if (!confirm(lang === "th" ? "ลบรายการ guideline นี้?" : "Delete this guideline?")) return;
    await window.electron.boqPriceDelete(suggestionId);
    setSuggestions(prev => prev.filter(s => s.id !== suggestionId));
  };

  // Live total based on local input values
  const qtyN = parseFloat(local.qty) || 0;
  const matN = parseFloat(local.mat) || 0;
  const labN = parseFloat(local.lab) || 0;
  const lineTotal = qtyN * (matN + labN);

  // Push changes to parent state — for numbers, only push when value is parseable
  const change = (field, value) => {
    setLocal(prev => ({ ...prev, [field]: value }));
    if (field === 'qty' || field === 'mat' || field === 'lab') {
      const n = parseFloat(value);
      onUpdate({ [field]: isNaN(n) ? 0 : n });
    } else {
      onUpdate({ [field]: value });
    }
  };

  const inp = { className: "field", style: { height: 28, fontSize: 12, padding: "0 6px" } };
  const inpMono = { ...inp, style: { ...inp.style, fontFamily: "var(--font-mono)", textAlign: "right" } };

  return (
    <tr className="row-int">
      <td style={{ color: "var(--ink-mute)", verticalAlign: "middle" }}>
        <button className="btn btn-sm btn-ghost" style={{ padding: 0, width: 18, height: 18, fontSize: 10 }} disabled={isFirst} onClick={() => onMove(-1)}>▲</button>
        <button className="btn btn-sm btn-ghost" style={{ padding: 0, width: 18, height: 18, fontSize: 10 }} disabled={isLast} onClick={() => onMove(1)}>▼</button>
      </td>
      <td><input {...inp} style={{ ...inp.style, fontFamily: "var(--font-mono)", fontSize: 11 }}
        value={local.no} onChange={(e) => change('no', e.target.value)} /></td>
      <td>
        <input {...inp}
          ref={inputRef}
          value={local.desc}
          onChange={(e) => { change('desc', e.target.value); searchPrice(e.target.value); setShowSuggest(true); updateDropdownPos(); }}
          onFocus={() => { loadCategoryDefaults(); setShowSuggest(true); updateDropdownPos(); }}
          onBlur={() => setTimeout(() => setShowSuggest(false), 250)}
          placeholder={lang === "th"
            ? (categoryCode ? `▼ เลือกจาก ${categoryCode} หรือพิมพ์เอง...` : "พิมพ์รายการ...")
            : "Description"} />
        {showSuggest && suggestions.length > 0 && (
          <div className="boq-suggest-dropdown" style={{
            position: "fixed",
            top: dropdownPos.top,
            left: dropdownPos.left,
            width: dropdownPos.width,
            zIndex: 9999,
            background: "rgba(15, 18, 32, 0.78)",
            WebkitBackdropFilter: "blur(28px) saturate(180%)",
            backdropFilter: "blur(28px) saturate(180%)",
            border: "1px solid rgba(120, 144, 255, 0.35)",
            borderRadius: 12,
            maxHeight: 380,
            overflowY: "auto",
            boxShadow: "0 24px 80px rgba(0,0,0,0.6), 0 0 0 1px rgba(255,255,255,0.05) inset",
          }}>
            <div style={{
              position: "sticky", top: 0,
              padding: "10px 12px",
              background: "rgba(26, 38, 112, 0.85)",
              WebkitBackdropFilter: "blur(20px)",
              backdropFilter: "blur(20px)",
              borderBottom: "1px solid rgba(255,255,255,0.15)",
              fontSize: 11,
              color: "rgba(255,255,255,0.95)",
              letterSpacing: "0.04em", fontWeight: 600,
              display: "flex", justifyContent: "space-between", alignItems: "center",
              borderRadius: "12px 12px 0 0",
            }}>
              <span>
                {categoryCode
                  ? `📋 GUIDELINE หมวด ${categoryCode} · ${suggestions.filter(s => s.category_code === categoryCode).length} ใน ${suggestions.length} รายการ`
                  : `📋 PRICE DB · ${suggestions.length} รายการ`}
              </span>
              <span style={{ fontSize: 9.5, opacity: 0.7 }}>คลิก = ใช้ราคา · × = ลบ</span>
            </div>
            {suggestions.map((s) => {
              const inCurrentCat = s.category_code === categoryCode;
              return (
                <div key={s.id} onMouseDown={(e) => { e.preventDefault(); applySuggestion(s); }}
                  style={{
                    padding: "10px 12px", cursor: "pointer",
                    borderBottom: "1px solid rgba(255,255,255,0.06)",
                    fontSize: 12, display: "flex", gap: 10, alignItems: "center",
                    background: inCurrentCat ? "rgba(40,72,255,0.18)" : "transparent",
                    borderLeft: inCurrentCat ? "3px solid #6f86ff" : "3px solid transparent",
                    color: "rgba(255,255,255,0.92)",
                    transition: "background .12s",
                  }}
                  onMouseEnter={(e) => e.currentTarget.style.background = "rgba(120,144,255,0.22)"}
                  onMouseLeave={(e) => e.currentTarget.style.background = inCurrentCat ? "rgba(40,72,255,0.18)" : "transparent"}>
                  <span style={{ flex: 1, minWidth: 0, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", fontWeight: 500 }}>{s.description}</span>
                  <span style={{ fontSize: 10, minWidth: 40, textAlign: "right", color: "rgba(255,255,255,0.55)" }}>{s.unit}</span>
                  <div style={{ display: "flex", flexDirection: "column", alignItems: "flex-end", minWidth: 96, gap: 2 }}>
                    <span style={{ fontFamily: "var(--font-mono)", fontSize: 11, fontWeight: 700, color: "#9bb0ff" }}>{fmtTHB(s.total_price)}</span>
                    <span style={{ fontSize: 9, color: "rgba(255,255,255,0.4)", fontFamily: "var(--font-mono)" }}>
                      วัสดุ {fmtTHB(s.material_price)} · แรง {fmtTHB(s.labor_price)}
                    </span>
                  </div>
                  <span style={{
                    fontSize: 9, padding: "2px 6px", borderRadius: 4,
                    background: s.source_file === 'user-learned' ? "rgba(74,222,128,0.18)"
                              : s.source_file === 'user-added' ? "rgba(245,158,11,0.18)"
                              : "rgba(255,255,255,0.08)",
                    color: s.source_file === 'user-learned' ? "#4ade80"
                         : s.source_file === 'user-added' ? "#fbbf24"
                         : "rgba(255,255,255,0.6)",
                    fontWeight: 600,
                  }}>
                    {s.source_file === 'user-learned' ? '⚡' : s.source_file === 'user-added' ? '✎' : '📄'} {s.category_code || '—'}
                  </span>
                  <button onMouseDown={(e) => deleteSuggestion(e, s.id)}
                    style={{
                      background: "transparent", border: 0, color: "#ff6b8a",
                      cursor: "pointer", padding: "4px 8px", fontSize: 16, lineHeight: 1,
                      opacity: 0.5, borderRadius: 4,
                    }}
                    onMouseEnter={(e) => { e.currentTarget.style.opacity = 1; e.currentTarget.style.background = "rgba(255,107,138,0.15)"; }}
                    onMouseLeave={(e) => { e.currentTarget.style.opacity = 0.5; e.currentTarget.style.background = "transparent"; }}
                    title={lang === "th" ? "ลบรายการนี้" : "Delete"}>×</button>
                </div>
              );
            })}
          </div>
        )}
      </td>
      <td><input {...inpMono} inputMode="decimal"
        value={local.qty} onChange={(e) => change('qty', e.target.value)} placeholder="0" /></td>
      <td><input {...inp}
        value={local.unit} onChange={(e) => change('unit', e.target.value)} placeholder="ตร.ม." /></td>
      <td><input {...inpMono} inputMode="decimal"
        value={local.mat} onChange={(e) => change('mat', e.target.value)} placeholder="0.00" /></td>
      <td><input {...inpMono} inputMode="decimal"
        value={local.lab} onChange={(e) => change('lab', e.target.value)} placeholder="0.00" /></td>
      <td className="mono" style={{ textAlign: "right", fontWeight: 600, fontSize: 12.5, color: lineTotal > 0 ? "var(--ink)" : "var(--ink-mute)" }}>{fmtTHB(lineTotal)}</td>
      <td><button className="btn btn-sm btn-ghost" onClick={onRemove} style={{ color: "var(--rose)", padding: "0 6px" }}>×</button></td>
    </tr>
  );
}

// ───────────────────────────────────────────────────────────────
// TERMS & SUMMARY SECTION
// ───────────────────────────────────────────────────────────────
function BOQTermsSection({ d, setD, totals, lang }) {
  return (
    <div className="anim-fadein" style={{ display: "grid", gridTemplateColumns: "1.2fr 1fr", gap: 14 }}>
      <div className="card">
        <h3 className="h2" style={{ marginBottom: 18 }}>{lang === "th" ? "เงื่อนไข & ภาษี" : "Terms & Tax"}</h3>

        <div style={{ marginBottom: 18 }}>
          <label style={{ fontSize: 11.5, color: "var(--ink-soft)", display: "flex", justifyContent: "space-between" }}>
            <span>{lang === "th" ? "ค่าดำเนินการ + กำไร (Margin)" : "Operation + Margin"}</span>
            <span className="mono" style={{ fontWeight: 600 }}>{d.margin_pct}%</span>
          </label>
          <input type="range" min="0" max="40" step="0.5" value={d.margin_pct}
            onChange={(e) => setD(prev => ({ ...prev, margin_pct: parseFloat(e.target.value) }))}
            style={{ width: "100%", accentColor: "var(--syk-blue)", marginTop: 6 }} />
        </div>

        {/* VAT toggle */}
        <div style={{ marginBottom: 14, padding: 14, background: "var(--glass-2)", border: "1px solid var(--line)", borderRadius: 10 }}>
          <label style={{ display: "flex", alignItems: "center", gap: 10, cursor: "pointer" }}>
            <input type="checkbox" checked={d.include_vat}
              onChange={(e) => setD(prev => ({ ...prev, include_vat: e.target.checked }))}
              style={{ width: 18, height: 18, accentColor: "var(--syk-blue)" }} />
            <div style={{ flex: 1 }}>
              <div style={{ fontSize: 13, fontWeight: 600 }}>
                {lang === "th" ? "รวม VAT 7%" : "Include VAT 7%"}
              </div>
              <div style={{ fontSize: 11, color: "var(--ink-mute)", marginTop: 2 }}>
                {d.include_vat
                  ? (lang === "th" ? "ยอดสุทธิจะรวมภาษีมูลค่าเพิ่ม 7%" : "Grand total includes VAT 7%")
                  : (lang === "th" ? "ยอดสุทธิจะไม่รวม VAT (ลูกค้าจ่ายแยก)" : "Grand total excludes VAT")}
              </div>
            </div>
            <input type="number" className="field" value={d.vat_pct}
              onChange={(e) => setD(prev => ({ ...prev, vat_pct: parseFloat(e.target.value) || 0 }))}
              disabled={!d.include_vat}
              style={{ width: 70, height: 32, fontSize: 12, fontFamily: "var(--font-mono)", textAlign: "right" }} />
            <span style={{ fontSize: 12, color: "var(--ink-mute)" }}>%</span>
          </label>
        </div>

        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr 1fr", gap: 12, marginBottom: 14 }}>
          <div>
            <label style={{ fontSize: 11.5, color: "var(--ink-soft)" }}>{lang === "th" ? "ราคามีผล (วัน)" : "Valid (days)"}</label>
            <input type="number" className="field" value={d.valid_days}
              onChange={(e) => setD(prev => ({ ...prev, valid_days: parseInt(e.target.value) || 30 }))} />
          </div>
          <div>
            <label style={{ fontSize: 11.5, color: "var(--ink-soft)" }}>{lang === "th" ? "เงินประกัน (%)" : "Retention (%)"}</label>
            <input type="number" className="field" value={d.retention_pct}
              onChange={(e) => setD(prev => ({ ...prev, retention_pct: parseFloat(e.target.value) || 0 }))} />
          </div>
          <div>
            <label style={{ fontSize: 11.5, color: "var(--ink-soft)" }}>{lang === "th" ? "หัก ณ ที่จ่าย (%)" : "WHT (%)"}</label>
            <input type="number" className="field" value={d.wht_pct}
              onChange={(e) => setD(prev => ({ ...prev, wht_pct: parseFloat(e.target.value) || 0 }))} />
          </div>
        </div>

        <div>
          <label style={{ fontSize: 11.5, color: "var(--ink-soft)" }}>{lang === "th" ? "เงื่อนไขการชำระเงิน" : "Payment terms"}</label>
          <textarea className="field" rows="6" value={d.payment_terms}
            onChange={(e) => setD(prev => ({ ...prev, payment_terms: e.target.value }))}
            placeholder={lang === "th" ? "งวดที่ 1 (15%) ลงเสาเข็ม-ฐานราก\nงวดที่ 2 (20%) โครงสร้าง ค.ส.ล. ชั้น 1\n..." : "Milestone schedule..."} />
        </div>
      </div>

      <div className="card" style={{ background: "var(--glass-2)" }}>
        <div className="micro" style={{ marginBottom: 14 }}>{lang === "th" ? "สรุปราคา" : "Summary"}</div>
        {[
          { l: lang === "th" ? "ต้นทุนวัสดุ" : "Material cost", v: fmtTHB(totals.totalMat), muted: true },
          { l: lang === "th" ? "ต้นทุนแรง" : "Labor cost", v: fmtTHB(totals.totalLab), muted: true },
          { l: lang === "th" ? "รวมต้นทุน (Subtotal)" : "Subtotal cost", v: fmtTHB(totals.subtotal), bold: true },
          { l: `${lang === "th" ? "ค่าดำเนินการ+กำไร" : "Operation+Margin"} (${d.margin_pct}%)`, v: fmtTHB(totals.margin), accent: "var(--gold)" },
          { l: lang === "th" ? "รวมหลังค่าดำเนินการ" : "After margin", v: fmtTHB(totals.beforeVAT), bold: true, accent: "var(--syk-blue-soft)" },
          ...(d.include_vat ? [{ l: `VAT ${totals.vatPct}%`, v: fmtTHB(totals.vat) }] : []),
          { l: d.include_vat ? (lang === "th" ? "ยอดรวมสุทธิ (รวม VAT)" : "Grand total (incl. VAT)") : (lang === "th" ? "ยอดรวมสุทธิ (ไม่รวม VAT)" : "Grand total (excl. VAT)"),
            v: fmtTHB(totals.total), big: true, accent: "var(--emerald)" },
        ].map((r, i, arr) => (
          <div key={i} style={{ display: "flex", justifyContent: "space-between", padding: "9px 0",
            borderBottom: i < arr.length - 1 ? "1px solid var(--line)" : "0",
            fontSize: r.big ? 17 : 13, paddingLeft: r.muted ? 16 : 0 }}>
            <span style={{ color: r.muted ? "var(--ink-mute)" : "var(--ink-soft)" }}>{r.l}</span>
            <span className="mono" style={{ fontWeight: r.bold || r.big ? 700 : 500, color: r.accent || "var(--ink)" }}>{r.v}</span>
          </div>
        ))}
      </div>
    </div>
  );
}

// ───────────────────────────────────────────────────────────────
// PREVIEW — A4 Portrait layout with cover page, item pages, summary
// ───────────────────────────────────────────────────────────────
function BOQPreview({ draft, lang, onEdit, onClose }) {
  const data = draft.data || (draft.data_json ? JSON.parse(draft.data_json) : { groups: [] });
  const groups = data.groups || [];

  // Recompute totals — honor include_vat
  let subtotal = 0, totalMat = 0, totalLab = 0;
  for (const g of groups) {
    for (const it of (g.items || [])) {
      const qty = +it.qty || 0, mat = +it.mat || 0, lab = +it.lab || 0;
      subtotal += qty * (mat + lab);
      totalMat += qty * mat;
      totalLab += qty * lab;
    }
  }
  const marginPct = draft.margin_pct || 12;
  const vatPct = draft.vat_pct ?? 7;
  const includeVat = draft.include_vat === false || draft.include_vat === 0 ? false : true;
  const margin = subtotal * marginPct / 100;
  const beforeVAT = subtotal + margin;
  const vat = includeVat ? beforeVAT * vatPct / 100 : 0;
  const total = beforeVAT + vat;
  const dateStr = (draft.created_at || draft.created_date || new Date().toISOString()).split('T')[0];

  return (
    <div className="anim-fadein boq-preview-wrap">
      {/* A4 + print styles */}
      <style>{`
        .boq-preview-wrap .a4-page {
          width: 210mm;
          min-height: 297mm;
          padding: 18mm 16mm;
          margin: 0 auto 14px;
          background: white;
          color: #0c1226;
          box-shadow: 0 12px 40px rgba(0,0,0,0.25);
          page-break-after: always;
          break-after: page;
          position: relative;
          box-sizing: border-box;
          font-family: 'Sarabun', sans-serif;
        }
        .boq-preview-wrap .a4-page:last-child { page-break-after: auto; }
        @media print {
          html, body { background: white !important; margin: 0; padding: 0; }
          body::before, body::after { display: none !important; }
          .app, .clock-widget, .dirty-indicator, .tweaks-panel, header, aside,
          .boq-preview-toolbar, .toast-wrap { display: none !important; }
          .boq-preview-wrap { padding: 0 !important; }
          .boq-preview-wrap .a4-page {
            width: 210mm; min-height: 297mm;
            margin: 0; box-shadow: none;
            padding: 14mm 14mm;
          }
        }
        @page { size: A4 portrait; margin: 0; }
      `}</style>

      <div className="card card-tight boq-preview-toolbar" style={{ display: "flex", gap: 8, marginBottom: 14 }}>
        <button className="btn btn-sm" onClick={onClose}><I.chev dir="left" /> {lang === "th" ? "กลับ" : "Back"}</button>
        <div style={{ flex: 1 }} />
        <span className="chip" style={{ background: includeVat ? "rgba(74,222,128,0.12)" : "rgba(245,158,11,0.12)", color: includeVat ? "var(--emerald)" : "var(--amber)" }}>
          {includeVat ? `รวม VAT ${vatPct}%` : "ไม่รวม VAT"}
        </span>
        <button className="btn btn-sm" onClick={async () => {
          if (!draft.id) return;
          const r = await window.electron.boqExportFilled(draft.id);
        }}><I.download /> Excel</button>
        <button className="btn btn-sm" onClick={async () => {
          if (!draft.id) return;
          const r = await window.electron.boqExportPdf(draft.id);
        }}><I.pdf /> PDF</button>
        <button className="btn btn-sm" onClick={() => window.print()}><I.print /> {lang === "th" ? "พิมพ์" : "Print"}</button>
        <button className="btn btn-sm btn-primary" onClick={onEdit}><I.edit /> {lang === "th" ? "แก้ไข" : "Edit"}</button>
      </div>

      {/* ── PAGE 1: Cover page redesigned ───────────────────────── */}
      <div className="a4-page" style={{ display: "flex", flexDirection: "column" }}>
        {/* HEADER: Logo + Company info + Quotation number */}
        <header style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-start", paddingBottom: 14, borderBottom: "3px solid #1a2670" }}>
          <div style={{ display: "flex", gap: 14, alignItems: "flex-start" }}>
            <img src="assets/syk-logo.svg"
              onError={(e) => { e.target.onerror = null; e.target.src = "assets/syk-logo.png"; }}
              alt="SYK"
              style={{ width: 70, height: 70, borderRadius: 12, flexShrink: 0 }} />
            <div>
              <div style={{ fontSize: 17, fontWeight: 800, color: "#1a2670", letterSpacing: "-0.01em", lineHeight: 1.2 }}>{COMPANY.nameTH}</div>
              <div style={{ fontSize: 10.5, color: "#1a2670", opacity: 0.7, marginTop: 1, fontStyle: "italic" }}>{COMPANY.nameEN}</div>
              <div style={{ fontSize: 9.5, color: "#666", marginTop: 6, lineHeight: 1.5, maxWidth: 290 }}>
                {COMPANY.addressTH}<br />
                โทร {COMPANY.phone} · มือถือ {COMPANY.mobile}<br />
                เลขประจำตัวผู้เสียภาษี {COMPANY.taxId}
              </div>
            </div>
          </div>
          <div style={{ textAlign: "right" }}>
            <div style={{ fontSize: 10, color: "#888", letterSpacing: "0.25em", fontWeight: 600 }}>QUOTATION</div>
            <div style={{ fontFamily: "var(--font-mono)", fontSize: 19, fontWeight: 700, color: "#1a2670", marginTop: 4 }}>{draft.boq_number || '—'}</div>
            <div style={{ fontSize: 10, color: "#888", marginTop: 8, lineHeight: 1.5 }}>
              วันที่ออก {dateStr}<br />
              ราคามีผล {draft.valid_days || 30} วัน
            </div>
          </div>
        </header>

        {/* TITLE SECTION */}
        <div style={{ textAlign: "center", padding: "14mm 0 10mm" }}>
          <div style={{ fontSize: 13, color: "#1a2670", letterSpacing: "0.4em", fontWeight: 600 }}>ใบเสนอราคา</div>
          <div style={{ fontSize: 26, fontWeight: 800, marginTop: 8, color: "#1a2670", letterSpacing: "0.04em" }}>BILL OF QUANTITIES</div>
          <div style={{ width: 80, height: 3, background: "#1a2670", margin: "12px auto 16px" }} />
          <div style={{ fontSize: 17, fontWeight: 700, color: "#0c1226" }}>{draft.project_name || '—'}</div>
          <div style={{ fontSize: 12, color: "#666", marginTop: 3 }}>{draft.site || '—'}</div>
        </div>

        {/* NUMBERED INFO TABLE */}
        <div style={{ border: "1px solid #ddd", borderRadius: 6, padding: "10px 14px", marginBottom: 14 }}>
          {[
            { num: '1', label: 'ลูกค้า', en: 'Client', value: draft.client_name || '—' },
            { num: '2', label: 'โครงการ', en: 'Project', value: draft.project_name || '—' },
            { num: '3', label: 'ที่ตั้งโครงการ', en: 'Site', value: draft.site || '—' },
            { num: '4', label: 'พื้นที่ใช้สอย', en: 'Area', value: `${draft.area || 0} ตร.ม.` },
            { num: '5', label: 'จำนวนงาน', en: 'Scope', value: `${groups.filter(g => g.items?.length).length} หมวด · ${groups.reduce((a, g) => a + (g.items?.length || 0), 0)} รายการ` },
            { num: '6', label: 'ราคามีผล', en: 'Valid', value: `${draft.valid_days || 30} วัน นับจากวันที่ออกใบ` },
          ].map((r, i, arr) => (
            <div key={r.num} style={{
              display: "flex", gap: 10, padding: "6px 0", alignItems: "baseline",
              borderBottom: i < arr.length - 1 ? "1px dashed #eee" : "0",
              fontSize: 11.5,
            }}>
              <span style={{ fontFamily: "var(--font-mono)", color: "#1a2670", fontWeight: 700, width: 18 }}>{r.num}.</span>
              <span style={{ color: "#777", width: 130, fontSize: 11 }}>
                {r.label} <span style={{ color: "#aaa", fontSize: 9.5 }}>/ {r.en}</span>
              </span>
              <span style={{ fontWeight: 600, flex: 1 }}>{r.value}</span>
            </div>
          ))}
        </div>

        {/* CATEGORY BREAKDOWN + SUMMARY BOX (split layout) */}
        <div style={{ flex: 1, display: "grid", gridTemplateColumns: "1.5fr 1fr", gap: 14, alignItems: "stretch", minHeight: 0 }}>
          {/* LEFT: Category breakdown */}
          <div>
            <div style={{ fontSize: 11, color: "#1a2670", textTransform: "uppercase", letterSpacing: "0.15em", fontWeight: 700, marginBottom: 8 }}>
              สรุปยอดรายหมวด / Category Breakdown
            </div>
            <table style={{ width: "100%", borderCollapse: "collapse", fontSize: 10.5 }}>
              <thead>
                <tr style={{ background: "#eef0fa", color: "#1a2670" }}>
                  <th style={{ padding: "5px 8px", textAlign: "left", fontSize: 9.5, fontWeight: 700 }}>หมวด</th>
                  <th style={{ padding: "5px 8px", textAlign: "right", fontSize: 9.5, fontWeight: 700, width: 50 }}>รายการ</th>
                  <th style={{ padding: "5px 8px", textAlign: "right", fontSize: 9.5, fontWeight: 700, width: 90 }}>มูลค่า (บาท)</th>
                </tr>
              </thead>
              <tbody>
                {groups.filter(g => (g.items?.length || 0) > 0).map((g, gi) => {
                  const gt = g.items.reduce((a, it) => a + (+it.qty || 0) * ((+it.mat || 0) + (+it.lab || 0)), 0);
                  return (
                    <tr key={gi} style={{ borderBottom: "1px dashed #eee" }}>
                      <td style={{ padding: "5px 8px", color: "#1a2670", fontWeight: 500 }}>{g.name}</td>
                      <td style={{ padding: "5px 8px", textAlign: "right", color: "#888", fontFamily: "var(--font-mono)" }}>{g.items.length}</td>
                      <td style={{ padding: "5px 8px", textAlign: "right", fontFamily: "var(--font-mono)", fontWeight: 600 }}>{fmtTHB(gt)}</td>
                    </tr>
                  );
                })}
                {groups.filter(g => (g.items?.length || 0) === 0).length > 0 && (
                  <tr>
                    <td colSpan="3" style={{ padding: "5px 8px", color: "#bbb", fontSize: 9.5, fontStyle: "italic" }}>
                      (อีก {groups.filter(g => (g.items?.length || 0) === 0).length} หมวด ยังไม่มีรายการ)
                    </td>
                  </tr>
                )}
              </tbody>
              <tfoot>
                <tr style={{ borderTop: "2px solid #1a2670" }}>
                  <td style={{ padding: "8px", fontWeight: 800, color: "#1a2670", fontSize: 11 }}>รวมต้นทุนทุกหมวด</td>
                  <td></td>
                  <td style={{ padding: "8px", textAlign: "right", fontFamily: "var(--font-mono)", fontWeight: 800, color: "#1a2670", fontSize: 11.5 }}>{fmtTHB(subtotal)}</td>
                </tr>
              </tfoot>
            </table>
          </div>

          {/* RIGHT: Summary box (in dark navy frame, middle-bottom) */}
          <div style={{ display: "flex", flexDirection: "column", justifyContent: "flex-end" }}>
            <div style={{
              background: "linear-gradient(180deg, #1a2670, #0f1850)",
              color: "white",
              borderRadius: 10,
              padding: "14px 16px",
              boxShadow: "0 4px 20px rgba(26,38,112,0.25)",
            }}>
              <div style={{ fontSize: 9.5, opacity: 0.7, letterSpacing: "0.2em", fontWeight: 600, marginBottom: 10 }}>
                สรุปยอดเงิน / SUMMARY
              </div>
              {[
                { l: "รวมต้นทุน", v: fmtTHB(subtotal) },
                { l: `+ ค่าดำเนินการ ${marginPct}%`, v: fmtTHB(margin) },
                { l: "ยอดก่อน VAT", v: fmtTHB(beforeVAT), divider: true },
                ...(includeVat ? [{ l: `+ VAT ${vatPct}%`, v: fmtTHB(vat) }] : []),
              ].map((r, i) => (
                <div key={i} style={{
                  display: "flex", justifyContent: "space-between", padding: "5px 0",
                  borderBottom: r.divider ? "2px solid rgba(255,255,255,0.3)" : "1px solid rgba(255,255,255,0.1)",
                  fontSize: 11,
                }}>
                  <span style={{ opacity: 0.85 }}>{r.l}</span>
                  <span style={{ fontFamily: "var(--font-mono)", fontWeight: r.divider ? 700 : 500 }}>{r.v}</span>
                </div>
              ))}
              <div style={{ marginTop: 10, paddingTop: 10, borderTop: "2px solid rgba(255,255,255,0.5)" }}>
                <div style={{ fontSize: 9, opacity: 0.7, letterSpacing: "0.08em" }}>
                  {includeVat ? "ยอดสุทธิรวม VAT" : "ยอดสุทธิไม่รวม VAT"}
                </div>
                <div style={{ fontSize: 22, fontWeight: 800, fontFamily: "var(--font-mono)", marginTop: 2, letterSpacing: "-0.01em" }}>{fmtTHB(total)}</div>
              </div>
            </div>
            {!includeVat && (
              <div style={{ marginTop: 6, fontSize: 9.5, color: "#d97706", textAlign: "right", fontStyle: "italic" }}>
                *ราคานี้ยังไม่รวม VAT 7%
              </div>
            )}
          </div>
        </div>

        {/* Payment terms (if exists, before signatures) */}
        {draft.payment_terms && (
          <div style={{ marginTop: 14, padding: "10px 12px", background: "#fafafa", borderLeft: "3px solid #1a2670", borderRadius: "0 6px 6px 0" }}>
            <div style={{ fontSize: 9.5, color: "#1a2670", textTransform: "uppercase", letterSpacing: "0.1em", marginBottom: 4, fontWeight: 700 }}>
              เงื่อนไขการชำระเงิน
            </div>
            <div style={{ fontSize: 10, color: "#555", whiteSpace: "pre-wrap", lineHeight: 1.5 }}>{draft.payment_terms}</div>
          </div>
        )}

        {/* Signature lines */}
        <div style={{ marginTop: 14, display: "grid", gridTemplateColumns: "1fr 1fr", gap: 30 }}>
          <div style={{ textAlign: "center" }}>
            <div style={{ borderTop: "1px solid #999", marginTop: 24, marginBottom: 4 }} />
            <div style={{ fontSize: 10.5, color: "#666" }}>ผู้เสนอราคา (Quoted by)</div>
            <div style={{ fontSize: 9.5, color: "#999", marginTop: 2 }}>วันที่: {dateStr}</div>
          </div>
          <div style={{ textAlign: "center" }}>
            <div style={{ borderTop: "1px solid #999", marginTop: 24, marginBottom: 4 }} />
            <div style={{ fontSize: 10.5, color: "#666" }}>ผู้อนุมัติ / ลูกค้า (Approved)</div>
            <div style={{ fontSize: 9.5, color: "#999", marginTop: 2 }}>วันที่: ____________</div>
          </div>
        </div>
      </div>

      {/* ── PAGE 2+: Items pages — A4 portrait per group ───────── */}
      {groups.map((g, gi) => {
        const groupTotal = (g.items || []).reduce((a, it) => a + (+it.qty || 0) * ((+it.mat || 0) + (+it.lab || 0)), 0);
        return (
          <React.Fragment key={gi}>
            {/* Optional cover page per category */}
            {g.coverPageEnabled && (
              <div className="a4-page" style={{ display: "flex", flexDirection: "column", justifyContent: "center", textAlign: "center", background: "#f8f9ff" }}>
                <div style={{ fontSize: 12, color: "#888", letterSpacing: "0.3em", fontWeight: 600 }}>หมวดที่ {gi + 1}</div>
                <div style={{ fontSize: 42, fontWeight: 800, marginTop: 20, color: "#1a2670", letterSpacing: "-0.01em" }}>{g.name}</div>
                {g.coverPageNote && (
                  <div style={{ fontSize: 14, color: "#555", marginTop: 32, maxWidth: 480, margin: "32px auto 0", lineHeight: 1.8 }}>{g.coverPageNote}</div>
                )}
                <div style={{ marginTop: 50, fontSize: 12, color: "#999" }}>{(g.items || []).length} รายการ · มูลค่ารวม {fmtTHB(groupTotal)}</div>
              </div>
            )}

            {/* Items page */}
            <div className="a4-page">
              <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", paddingBottom: 10, borderBottom: "2px solid #1a2670", marginBottom: 12 }}>
                <div style={{ fontSize: 14, fontWeight: 700, color: "#1a2670" }}>{g.name}</div>
                <div style={{ fontSize: 10, color: "#666" }}>BOQ: {draft.boq_number || '—'} · หน้า {gi + 2}/{groups.length + 2}</div>
              </div>

              <table style={{ width: "100%", borderCollapse: "collapse", fontSize: 10 }}>
                <thead>
                  <tr style={{ background: "#1a2670", color: "white" }}>
                    <th style={{ padding: "6px 6px", textAlign: "left", fontSize: 9, width: 36 }}>No.</th>
                    <th style={{ padding: "6px 8px", textAlign: "left", fontSize: 9 }}>รายการ</th>
                    <th style={{ padding: "6px 6px", textAlign: "right", fontSize: 9, width: 48 }}>ปริมาณ</th>
                    <th style={{ padding: "6px 6px", textAlign: "center", fontSize: 9, width: 44 }}>หน่วย</th>
                    <th style={{ padding: "6px 6px", textAlign: "right", fontSize: 9, width: 62 }}>วัสดุ/หน่วย</th>
                    <th style={{ padding: "6px 6px", textAlign: "right", fontSize: 9, width: 62 }}>แรง/หน่วย</th>
                    <th style={{ padding: "6px 6px", textAlign: "right", fontSize: 9, width: 78 }}>รวม</th>
                  </tr>
                </thead>
                <tbody>
                  {(g.items || []).map((it, ii) => {
                    const t = (+it.qty || 0) * ((+it.mat || 0) + (+it.lab || 0));
                    return (
                      <tr key={ii} style={{ borderBottom: "1px solid #eee" }}>
                        <td style={{ padding: "5px 6px", fontFamily: "var(--font-mono)", color: "#888", fontSize: 9 }}>{it.no}</td>
                        <td style={{ padding: "5px 8px" }}>{it.desc}</td>
                        <td style={{ padding: "5px 6px", textAlign: "right", fontFamily: "var(--font-mono)" }}>{(+it.qty || 0).toLocaleString()}</td>
                        <td style={{ padding: "5px 6px", textAlign: "center", color: "#666" }}>{it.unit}</td>
                        <td style={{ padding: "5px 6px", textAlign: "right", fontFamily: "var(--font-mono)" }}>{fmtTHB(+it.mat || 0)}</td>
                        <td style={{ padding: "5px 6px", textAlign: "right", fontFamily: "var(--font-mono)" }}>{fmtTHB(+it.lab || 0)}</td>
                        <td style={{ padding: "5px 6px", textAlign: "right", fontFamily: "var(--font-mono)", fontWeight: 600 }}>{fmtTHB(t)}</td>
                      </tr>
                    );
                  })}
                  <tr style={{ background: "#eef0fa" }}>
                    <td colSpan="6" style={{ padding: "8px 8px", textAlign: "right", fontWeight: 700, color: "#1a2670", fontSize: 11 }}>รวม {g.name}</td>
                    <td style={{ padding: "8px 6px", textAlign: "right", fontWeight: 800, color: "#1a2670", fontFamily: "var(--font-mono)", fontSize: 11 }}>{fmtTHB(groupTotal)}</td>
                  </tr>
                </tbody>
              </table>
            </div>
          </React.Fragment>
        );
      })}

      {/* ── FINAL PAGE: Grand total summary ────────────────────── */}
      <div className="a4-page">
        <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", paddingBottom: 10, borderBottom: "2px solid #1a2670", marginBottom: 18 }}>
          <div style={{ fontSize: 16, fontWeight: 700, color: "#1a2670" }}>สรุปรวมทุกหมวด / GRAND SUMMARY</div>
          <div style={{ fontSize: 10, color: "#666" }}>BOQ: {draft.boq_number || '—'}</div>
        </div>

        <table style={{ width: "100%", borderCollapse: "collapse", fontSize: 11, marginBottom: 20 }}>
          <thead>
            <tr style={{ background: "#1a2670", color: "white" }}>
              <th style={{ padding: "8px 10px", textAlign: "left", fontSize: 10 }}>หมวดงาน</th>
              <th style={{ padding: "8px 10px", textAlign: "right", fontSize: 10, width: 80 }}>จำนวนรายการ</th>
              <th style={{ padding: "8px 10px", textAlign: "right", fontSize: 10, width: 130 }}>มูลค่า (บาท)</th>
            </tr>
          </thead>
          <tbody>
            {groups.map((g, gi) => {
              const gt = (g.items || []).reduce((a, it) => a + (+it.qty || 0) * ((+it.mat || 0) + (+it.lab || 0)), 0);
              if (gt === 0 && (g.items?.length || 0) === 0) return null;
              return (
                <tr key={gi} style={{ borderBottom: "1px solid #eee" }}>
                  <td style={{ padding: "8px 10px", fontWeight: 500 }}>{g.name}</td>
                  <td style={{ padding: "8px 10px", textAlign: "right", color: "#666" }}>{g.items?.length || 0}</td>
                  <td style={{ padding: "8px 10px", textAlign: "right", fontFamily: "var(--font-mono)", fontWeight: 600 }}>{fmtTHB(gt)}</td>
                </tr>
              );
            })}
          </tbody>
        </table>

        <div style={{ padding: 18, background: "#f5f7ff", border: "1.5px solid #1a2670", borderRadius: 8 }}>
          <div style={{ fontSize: 11, color: "#1a2670", textTransform: "uppercase", letterSpacing: "0.15em", fontWeight: 700, marginBottom: 12 }}>
            สรุปยอดเงินสุทธิ
          </div>
          {[
            { l: "รวมต้นทุนวัสดุและค่าแรง", v: fmtTHB(subtotal), bold: true },
            { l: `ค่าดำเนินการ + กำไร (${marginPct}%)`, v: fmtTHB(margin) },
            { l: "ยอดรวมหลังค่าดำเนินการ", v: fmtTHB(beforeVAT), bold: true, big: !includeVat, color: !includeVat ? "#1a2670" : "#0c1226" },
            ...(includeVat ? [
              { l: `ภาษีมูลค่าเพิ่ม VAT ${vatPct}%`, v: fmtTHB(vat) },
              { l: "ยอดรวมหลัง VAT (สุทธิที่ต้องชำระ)", v: fmtTHB(total), bold: true, big: true, color: "#1a2670" },
            ] : [
              { l: "*ราคานี้ไม่รวม VAT (ลูกค้าจ่าย VAT แยก)", v: "", note: true },
            ]),
          ].map((r, i, arr) => (
            <div key={i} style={{
              display: "flex", justifyContent: "space-between", padding: "10px 0",
              borderBottom: i < arr.length - 1 ? "1px solid rgba(26,38,112,0.15)" : "0",
              fontSize: r.big ? 18 : 13,
              fontStyle: r.note ? "italic" : "normal",
            }}>
              <span style={{ color: r.note ? "#d97706" : "#555", fontWeight: r.bold ? 700 : 400 }}>{r.l}</span>
              <span style={{ fontFamily: "var(--font-mono)", fontWeight: r.bold || r.big ? 800 : 500, color: r.color || "#0c1226" }}>{r.v}</span>
            </div>
          ))}
        </div>

        {/* Signatures */}
        <div style={{ position: "absolute", bottom: 18, left: 16, right: 16, display: "grid", gridTemplateColumns: "1fr 1fr", gap: 30 }}>
          <div style={{ textAlign: "center" }}>
            <div style={{ borderTop: "1px solid #999", marginBottom: 6, marginTop: 40 }} />
            <div style={{ fontSize: 11, color: "#666" }}>ผู้เสนอราคา (Quoted by)</div>
            <div style={{ fontSize: 10, color: "#999", marginTop: 2 }}>วันที่: {dateStr}</div>
          </div>
          <div style={{ textAlign: "center" }}>
            <div style={{ borderTop: "1px solid #999", marginBottom: 6, marginTop: 40 }} />
            <div style={{ fontSize: 11, color: "#666" }}>ผู้อนุมัติ / ลูกค้า (Approved)</div>
            <div style={{ fontSize: 10, color: "#999", marginTop: 2 }}>วันที่: ____________</div>
          </div>
        </div>
      </div>
    </div>
  );
}

// ───────────────────────────────────────────────────────────────
// DRIVE VIEW — files imported from Google Drive
// ───────────────────────────────────────────────────────────────
function BOQDriveView({ lang }) {
  const [list, setList] = useState([]);
  const [loading, setLoading] = useState(true);
  const toast = useToast();

  useEffect(() => {
    (async () => {
      if (!window.electron?.getBOQs) return;
      try { const r = await window.electron.getBOQs(); setList(r || []); }
      finally { setLoading(false); }
    })();
  }, []);

  const openFile = async (b) => {
    if (!b.drive_file_id) return toast(lang === "th" ? "ไม่มีไฟล์ Drive" : "No Drive file", "warn");
    toast(lang === "th" ? "กำลังเปิดไฟล์..." : "Opening...", "info");
    const r = await window.electron.boqOpenDriveFile(b.drive_file_id, b.drive_file_name || 'file.xlsx');
    if (!r.success) toast((lang === "th" ? "ผิดพลาด: " : "Error: ") + r.error, "danger");
  };

  if (loading) return <div className="card" style={{ textAlign: "center", padding: 40 }}>Loading…</div>;

  return (
    <div className="card">
      <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 18 }}>
        <div style={{ width: 36, height: 36, borderRadius: 10, background: "rgba(74,222,128,0.12)", border: "1px solid rgba(74,222,128,0.3)", display: "flex", alignItems: "center", justifyContent: "center", color: "var(--emerald)" }}><I.drive /></div>
        <div style={{ flex: 1 }}>
          <h3 className="h2">{lang === "th" ? "BOQ จาก Google Drive" : "BOQ from Drive"} ({list.length})</h3>
          <div style={{ fontSize: 11.5, color: "var(--ink-mute)" }}>{lang === "th" ? "ไฟล์ Excel ที่ Import มาจาก Drive — คลิกเพื่อดาวน์โหลด & เปิด" : "Click to download & open"}</div>
        </div>
      </div>
      <table className="table">
        <thead><tr>
          <th>{lang === "th" ? "ชื่อไฟล์" : "File"}</th>
          <th>{lang === "th" ? "โครงการ" : "Project"}</th>
          <th>{lang === "th" ? "วันที่" : "Date"}</th>
          <th>{lang === "th" ? "สถานะ" : "Status"}</th>
          <th></th>
        </tr></thead>
        <tbody>
          {list.slice(0, 50).map(b => (
            <tr key={b.id} className="row-int" onClick={() => openFile(b)} style={{ cursor: "pointer" }}>
              <td style={{ fontSize: 12.5 }}>📊 {b.drive_file_name || b.boq_number}</td>
              <td style={{ fontSize: 12, color: "var(--ink-soft)" }}>{b.project_name || '—'}</td>
              <td className="mono ink-soft" style={{ fontSize: 11 }}>{b.created_date || '—'}</td>
              <td><StatusBadge status={b.status || 'submitted'} /></td>
              <td style={{ textAlign: "right" }}><button className="btn btn-sm btn-ghost"><I.eye /></button></td>
            </tr>
          ))}
        </tbody>
      </table>
      {list.length > 50 && <div style={{ textAlign: "center", padding: 12, color: "var(--ink-mute)", fontSize: 12 }}>{lang === "th" ? `และอีก ${list.length - 50} ไฟล์...` : `and ${list.length - 50} more...`}</div>}
    </div>
  );
}

// ───────────────────────────────────────────────────────────────
// HELPERS
// ───────────────────────────────────────────────────────────────
function Field({ label, value, onChange, type = "text", placeholder }) {
  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 6 }}>
      <label style={{ fontSize: 11.5, color: "var(--ink-soft)" }}>{label}</label>
      <input className="field" type={type} value={value} placeholder={placeholder}
        onChange={(e) => onChange(type === "number" ? e.target.value : e.target.value)} />
    </div>
  );
}

// Master BOQ template — based on the SYK "BOQ บ้าน" file (Thai house construction standard)
// Categories A.1-A.3 (Structure) + B.1-B.12 (Architecture)
const MASTER_CATEGORIES = [
  { name: 'A.1 งานดิน', items: [], coverPageEnabled: false, coverPageNote: '' },
  { name: 'A.2 งานคอนกรีต', items: [], coverPageEnabled: false, coverPageNote: '' },
  { name: 'A.3 งานเชิงชาย, ปูนซิเมนต์และทรายสำหรับมุงหลังคา', items: [], coverPageEnabled: false, coverPageNote: '' },
  { name: 'B.1 งานพื้น', items: [], coverPageEnabled: false, coverPageNote: '' },
  { name: 'B.2 งานผนัง', items: [], coverPageEnabled: false, coverPageNote: '' },
  { name: 'B.3 งานฝ้าเพดาน', items: [], coverPageEnabled: false, coverPageNote: '' },
  { name: 'B.4 งานประตู-หน้าต่าง', items: [], coverPageEnabled: false, coverPageNote: '' },
  { name: 'B.5 งานบันได', items: [], coverPageEnabled: false, coverPageNote: '' },
  { name: 'B.6 งานระบบประปาและสุขาภิบาล', items: [], coverPageEnabled: false, coverPageNote: '' },
  { name: 'B.7 งานระบบไฟฟ้าและระบบสื่อสาร', items: [], coverPageEnabled: false, coverPageNote: '' },
  { name: 'B.8 งานสี', items: [], coverPageEnabled: false, coverPageNote: '' },
  { name: 'B.9 งานราวระเบียง', items: [], coverPageEnabled: false, coverPageNote: '' },
  { name: 'B.10 งานสุขภัณฑ์', items: [], coverPageEnabled: false, coverPageNote: '' },
  { name: 'B.11 งานอื่น ๆ', items: [], coverPageEnabled: false, coverPageNote: '' },
  { name: 'B.12 ค่าประสานงาน+ค่าเก็บรักษา', items: [], coverPageEnabled: false, coverPageNote: '' },
];

function emptyDraft(useMaster = true) {
  return {
    id: null,
    name: '',
    boq_number: autoNumber(),
    project_id: null,
    project_name: '',
    client_name: '',
    site: '',
    area: 0,
    margin_pct: 12,
    valid_days: 30,
    payment_terms: '',
    notes: '',
    status: 'draft',
    source: 'manual',
    data: { groups: useMaster
      ? MASTER_CATEGORIES.map(c => ({ ...c, items: [] }))
      : [{ name: '1. หมวดใหม่', items: [], coverPageEnabled: false, coverPageNote: '' }]
    },
  };
}

function autoNumber() {
  const d = new Date();
  const yy = String(d.getFullYear() + 543).slice(-2);
  const rand = Math.floor(1000 + Math.random() * 9000);
  return `QT-${yy}-${rand}`;
}

window.BOQPage = BOQPage;
