// Quote / Invoice detail modal — open an existing document, edit its line
// items, change status per the never-delete lifecycle, download the PDF,
// and email it to the customer. One component for both doc kinds: they
// differ only in labels, status vocabulary, and the payments panel.
//
// Lifecycle rules enforced here (mirror the backend):
//   - Quotes: items editable while draft or sent (a quote is a negotiation
//     document); issued quotes are cancelled, never deleted.
//   - Invoices: items editable only in draft — an issued invoice is a
//     financial record; fix mistakes by voiding and reissuing.
//   - paid/partial are ledger-derived, never set by hand.

const DOC_KIND = {
    quote: {
        noun: 'Quote', numberKey: 'quote_number', dateKey: 'expiry_date', dateLabel: 'Valid until',
        manualStatuses: ['draft', 'sent', 'accepted', 'declined', 'expired', 'cancelled'],
        deadStatus: 'cancelled', deadVerb: 'Cancel',
        canEditItems: (s) => s === 'draft' || s === 'sent',
    },
    invoice: {
        noun: 'Invoice', numberKey: 'invoice_number', dateKey: 'due_date', dateLabel: 'Due date',
        manualStatuses: ['draft', 'sent', 'overdue', 'void'],
        deadStatus: 'void', deadVerb: 'Void',
        canEditItems: (s) => s === 'draft',
    },
};

// inline: render as a docked side pane (no overlay) instead of a modal —
// used by the account detail split view. Same content, different chrome.
// allowPopout=false hides the open-in-window button (set inside the popout
// itself, where popping out again makes no sense).
const DocDetailModal = ({ kind, docId, currentUser, onClose, onChanged, inline, allowPopout = true }) => {
    const K = DOC_KIND[kind];
    const [doc, setDoc]         = React.useState(null);
    const [products, setProducts] = React.useState([]);
    const [err, setErr]         = React.useState('');
    const [busy, setBusy]       = React.useState(false);

    // Line-item editing: local draft rows keyed by item id ('new' = the add row).
    const [editRows, setEditRows] = React.useState({});
    const [notesDraft, setNotesDraft] = React.useState(null); // null = not editing
    const [billToDraft, setBillToDraft] = React.useState(null); // null = not editing

    // Acceptance evidence (quotes): loaded when the quote is accepted; the
    // terms text itself expands on demand.
    const [acceptance, setAcceptance] = React.useState(null);
    const [showTermsText, setShowTermsText] = React.useState(false);

    // Send panel
    const [showSend, setShowSend]   = React.useState(false);
    const [sendTo, setSendTo]       = React.useState('');
    const [sendMsg, setSendMsg]     = React.useState('');
    const [sendResult, setSendResult] = React.useState(null);

    const getDoc = kind === 'quote' ? api.getQuote : api.getInvoice;

    const load = async () => {
        try {
            const d = await getDoc(docId);
            setDoc(d);
            if (kind === 'quote' && d.status === 'accepted') {
                api.getQuoteAcceptance(d.id).then(rows => setAcceptance(rows[0] || null)).catch(() => {});
            }
            // Prefill the send recipient, best-effort: the doc's contact, or —
            // when there is none (individual accounts ARE the client, no
            // contact row) — the account's own email.
            if (d.contact_id && !sendTo) {
                api.getContacts({ account_id: d.account_id }).then(cs => {
                    const c = cs.find(x => x.id === d.contact_id);
                    const email = c?.emails?.find(e => e.is_primary)?.value || c?.emails?.[0]?.value;
                    if (email) setSendTo(prev => prev || email);
                }).catch(() => {});
            } else if (!d.contact_id && !sendTo) {
                api.getAccount(d.account_id).then(a => {
                    if (a?.main_email) setSendTo(prev => prev || a.main_email);
                }).catch(() => {});
            }
        } catch (e) { setErr(e.message); }
    };

    React.useEffect(() => {
        // Clear the previous document first — the docked pane reuses this
        // component across doc switches, and stale data/errors must not show
        // while the new fetch is in flight.
        setDoc(null); setErr(null);
        load();
        api.getProducts({ active: 'true' }).then(setProducts).catch(() => {});
    }, [docId]);

    if (!doc) {
        const pending = err ? <div className="api-error">{err}</div>
                            : <div className="loading-state"><i className="fas fa-spinner fa-spin"></i><p>Loading…</p></div>;
        if (inline) return <div className="doc-pane"><div className="doc-pane-body">{pending}</div></div>;
        return (
            <div className="modal-overlay" onClick={(e) => e.target === e.currentTarget && onClose()}>
                <div className="modal builder-modal"><div className="modal-body">{pending}</div></div>
            </div>
        );
    }

    const number   = doc[K.numberKey];
    const isDead   = doc.status === 'void' || doc.status === 'cancelled';
    const editable = !isDead && K.canEditItems(doc.status);
    const money    = (v) => '$' + Number(v || 0).toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
    const balance  = kind === 'invoice' ? Number(doc.total) - Number(doc.amount_paid || 0) : 0;

    const run = async (fn) => {
        setBusy(true); setErr('');
        try { await fn(); await load(); onChanged && onChanged(); }
        catch (e) { setErr(e.message); }
        finally { setBusy(false); }
    };

    // ── Handlers ─────────────────────────────────────────────────

    const handleStatus = (status) => {
        if (status === K.deadStatus && !window.confirm(
            `${K.deadVerb} ${number}? This can't be undone — the ${kind} stays on record as ${K.deadStatus}.`)) return;
        run(() => (kind === 'quote' ? api.updateQuote : api.updateInvoice)(doc.id, { status }));
    };

    const handleTax = (tax_rate) =>
        run(() => (kind === 'quote' ? api.updateQuote : api.updateInvoice)(doc.id, { tax_rate }));

    // Bill To on the PDF: derived from the account (name + attn + address)
    // unless a per-doc override (doc.bill_to) is set. The derived text doubles
    // as the edit prefill so tweaking starts from what already prints.
    const accountAddressLines = [
        doc.address_street,
        [doc.address_city, [doc.address_state, doc.address_zip].filter(Boolean).join(' ')].filter(Boolean).join(', '),
        doc.address_country,
    ].filter(Boolean);
    const derivedBillTo = [
        doc.account_name,
        doc.contact_name && doc.contact_name.trim() ? `Attn: ${doc.contact_name}` : null,
        ...accountAddressLines,
    ].filter(Boolean).join('\n');

    const handleBillToSave = () =>
        run(async () => {
            await (kind === 'quote' ? api.updateQuote : api.updateInvoice)(doc.id, { bill_to: billToDraft });
            setBillToDraft(null);
        });
    const handleBillToReset = () =>
        run(async () => {
            await (kind === 'quote' ? api.updateQuote : api.updateInvoice)(doc.id, { bill_to: null });
            setBillToDraft(null);
        });

    const handleNotesSave = () =>
        run(async () => {
            await (kind === 'quote' ? api.updateQuote : api.updateInvoice)(doc.id, { notes: notesDraft });
            setNotesDraft(null);
        });

    const itemApi = kind === 'quote'
        ? { add: api.addQuoteItem, update: api.updateQuoteItem, remove: api.deleteQuoteItem }
        : { add: api.addInvoiceItem, update: api.updateInvoiceItem, remove: api.deleteInvoiceItem };

    const startEdit = (item) => setEditRows(p => ({ ...p, [item.id]: {
        description: item.description, quantity: item.quantity, unit_price: item.unit_price, product_id: item.product_id,
        list_price: item.list_price ?? '',
    }}));
    const cancelEdit = (id) => setEditRows(p => { const n = { ...p }; delete n[id]; return n; });
    const setEditField = (id, key, val) => setEditRows(p => {
        const row = { ...p[id], [key]: val };
        if (key === 'product_id' && val) {
            const prod = products.find(x => x.id === parseInt(val));
            if (prod) { row.description = prod.name; row.unit_price = prod.unit_price; }
        }
        return { ...p, [id]: row };
    });
    const saveEdit = (id) => run(async () => {
        const row = editRows[id];
        const payload = {
            description: row.description,
            quantity: parseFloat(row.quantity || 0),
            unit_price: parseFloat(row.unit_price || 0),
            list_price: row.list_price === '' || row.list_price == null ? null : parseFloat(row.list_price),
            product_id: row.product_id ? parseInt(row.product_id) : null,
        };
        if (id === 'new') await itemApi.add(doc.id, payload);
        else await itemApi.update(doc.id, id, payload);
        cancelEdit(id);
    });
    const removeItem = (item) => {
        if (!window.confirm(`Remove "${item.description}" from ${number}?`)) return;
        run(() => itemApi.remove(doc.id, item.id));
    };

    const handleDownload = () =>
        run(() => api.downloadDocPdf(kind, doc.id, `${number}.pdf`));

    const handleSend = () => run(async () => {
        const result = await api.sendDoc(kind, doc.id, { to: sendTo, message: sendMsg });
        setSendResult(result);
        if (result.sent) { setShowSend(false); setSendMsg(''); }
    });

    // ── Row renderers ────────────────────────────────────────────

    const renderItemRow = (item) => {
        const edit = editRows[item.id];
        // Subscription lines show their term wherever the price does (" /mo",
        // " /yr") — mirrors the PDF and accept page, from the 0034 snapshot.
        const per = item.is_subscription ? (item.billing_interval === 'year' ? ' /yr' : ' /mo') : '';
        if (!edit) return (
            <tr key={item.id}>
                <td>{item.description}{item.product_sku ? <span style={{ color: 'var(--text-3)', fontSize: '0.75rem' }}> ({item.product_sku})</span> : null}</td>
                <td style={{ textAlign: 'right' }}>{Number(item.quantity)}</td>
                <td style={{ textAlign: 'right' }}>
                    {/* Discount look: reg. price struck through when higher than charged */}
                    {item.list_price != null && Number(item.list_price) > Number(item.unit_price) && (
                        <span style={{ textDecoration: 'line-through', color: 'var(--text-3)', marginRight: '0.4rem' }}>{money(item.list_price)}{per}</span>
                    )}
                    {money(item.unit_price)}{per}
                </td>
                <td style={{ textAlign: 'right', fontWeight: 500 }}>{money(item.total)}{per}</td>
                <td>
                    {editable && (
                        <div style={{ display: 'flex', gap: '0.25rem', justifyContent: 'flex-end' }}>
                            <button className="btn-icon-sm" title="Edit item" onClick={() => startEdit(item)}><i className="fas fa-pen"></i></button>
                            <button className="btn-icon-sm danger" title="Remove item" onClick={() => removeItem(item)}><i className="fas fa-times"></i></button>
                        </div>
                    )}
                </td>
            </tr>
        );
        return (
            <tr key={item.id}>
                <td>
                    <div style={{ display: 'flex', gap: '0.25rem' }}>
                        <select value={edit.product_id || ''} onChange={e => setEditField(item.id, 'product_id', e.target.value || null)} style={{ maxWidth: '40%' }}>
                            <option value="">Custom</option>
                            {products.map(p => <option key={p.id} value={p.id}>{p.name}</option>)}
                        </select>
                        <input value={edit.description} onChange={e => setEditField(item.id, 'description', e.target.value)} placeholder="Description" />
                    </div>
                </td>
                <td><input type="number" min="0" step="any" value={edit.quantity} onChange={e => setEditField(item.id, 'quantity', e.target.value)} style={{ textAlign: 'right' }} /></td>
                <td>
                    <input type="number" min="0" step="0.01" value={edit.list_price ?? ''} placeholder="Reg. price"
                        title="Optional — prints crossed out when higher than unit price"
                        onChange={e => setEditField(item.id, 'list_price', e.target.value)}
                        style={{ textAlign: 'right', marginBottom: '0.2rem' }} />
                    <input type="number" min="0" step="0.01" value={edit.unit_price} onChange={e => setEditField(item.id, 'unit_price', e.target.value)} style={{ textAlign: 'right' }} />
                </td>
                <td style={{ textAlign: 'right' }}>{money(parseFloat(edit.quantity || 0) * parseFloat(edit.unit_price || 0))}</td>
                <td>
                    <div style={{ display: 'flex', gap: '0.25rem', justifyContent: 'flex-end' }}>
                        <button className="btn-icon-sm" title="Save" disabled={busy || !edit.description} onClick={() => saveEdit(item.id)}><i className="fas fa-check"></i></button>
                        <button className="btn-icon-sm" title="Cancel" onClick={() => cancelEdit(item.id)}><i className="fas fa-undo"></i></button>
                    </div>
                </td>
            </tr>
        );
    };

    const statusOptions = K.manualStatuses.includes(doc.status)
        ? K.manualStatuses
        : [doc.status, ...K.manualStatuses]; // ledger-derived (paid/partial) shows but stays reachable-only-by-ledger

    // Header + body are built once; the chrome around them (modal overlay vs
    // docked pane) is chosen at the bottom based on the inline prop.
    const headerContent = (
        <>
            <h2 className="modal-title" style={{ display: 'flex', alignItems: 'center', gap: '0.6rem' }}>
                <span style={{ fontFamily: 'monospace' }}>{number}</span>
                <span className={`badge ${isDead ? 'secondary' : 'success'}`} style={{ textTransform: 'uppercase', fontSize: '0.65rem' }}>{doc.status}</span>
                {/* Stripe-born invoices (0040): auto-generated revenue record, void-only */}
                {doc.source === 'stripe' && (
                    <span className="badge secondary" style={{ fontSize: '0.65rem' }}
                          title="Generated automatically from a Stripe payment — a record of money already collected. It can be voided but not edited.">
                        <i className="fab fa-stripe-s" style={{ marginRight: '0.3rem' }}></i>Paid via Stripe
                    </span>
                )}
            </h2>
            <div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}>
                {allowPopout && (
                    <button className="btn-icon-sm" title="Open in its own window"
                            onClick={() => { popoutDoc(kind, number); onClose(); }}>
                        <i className="fas fa-external-link-alt"></i>
                    </button>
                )}
                <button className="modal-close-btn" onClick={onClose} title="Close">&times;</button>
            </div>
        </>
    );

    const body = (
        <>
                    {err && <div className="api-error">{err}</div>}
                    {sendResult && !sendResult.sent && (
                        <div className="api-error">
                            {sendResult.reason === 'no_mailbox'
                                ? 'No connected mailbox — connect one in Email Settings (envelope icon, top right), or download the PDF and send it yourself.'
                                : 'Sending failed — check Email Settings for the mailbox status, or download the PDF and send it yourself.'}
                        </div>
                    )}
                    {sendResult && sendResult.sent && (
                        // "Accepted" is the honest word — the provider took it, which
                        // is not the same as it landing. Naming the lane here is the
                        // fix for the "it's not in my Outlook Sent folder" question:
                        // a platform-lane send never transits the rep's mailbox.
                        <div className="api-success" style={{ marginBottom: '0.75rem' }}>
                            <div>Accepted for delivery from {sendResult.from}.</div>
                            {deliveryLaneNote(sendResult.via) && (
                                <div style={{ marginTop: '0.35rem', fontSize: '0.8125rem', opacity: 0.85 }}>
                                    {deliveryLaneNote(sendResult.via)}
                                </div>
                            )}
                            <div style={{ marginTop: '0.35rem', fontSize: '0.8125rem', opacity: 0.85 }}>
                                Track delivery on the account&rsquo;s Activity tab.
                            </div>
                        </div>
                    )}

                    {/* Header block: who + when + status */}
                    <div className="form-grid" style={{ marginBottom: '1rem' }}>
                        <div className="form-group">
                            <label className="form-label">
                                {kind === 'invoice' ? 'Bill To' : 'Prepared For'}
                                {!isDead && billToDraft === null && (
                                    <button className="btn-icon-sm" style={{ marginLeft: '0.5rem' }} title="Edit what prints on the document"
                                        onClick={() => setBillToDraft(doc.bill_to || derivedBillTo)}><i className="fas fa-pen"></i></button>
                                )}
                            </label>
                            {billToDraft === null ? (
                                doc.bill_to && doc.bill_to.trim()
                                    ? <div style={{ whiteSpace: 'pre-line' }}>
                                          <span style={{ fontWeight: 600 }}>{doc.bill_to}</span>
                                          <div style={{ fontSize: '0.75rem', color: 'var(--text-3)', marginTop: '0.25rem' }}>Custom — overrides the account details on the PDF</div>
                                      </div>
                                    : <div>
                                          <div style={{ fontWeight: 600 }}>{doc.account_name}</div>
                                          {doc.contact_name && doc.contact_name.trim() && <div style={{ fontSize: '0.8125rem', color: 'var(--text-3)' }}>Attn: {doc.contact_name}</div>}
                                          {accountAddressLines.map((l, i) => <div key={i} style={{ fontSize: '0.8125rem', color: 'var(--text-3)' }}>{l}</div>)}
                                      </div>
                            ) : (
                                <div>
                                    <textarea className="form-input" style={{ minHeight: 80, width: '100%' }} value={billToDraft}
                                        onChange={e => setBillToDraft(e.target.value)}
                                        placeholder={'Name\nStreet\nCity, ST ZIP'} />
                                    <div className="btn-group" style={{ marginTop: '0.375rem' }}>
                                        <button className="btn btn-primary btn-sm" disabled={busy} onClick={handleBillToSave}><i className="fas fa-check"></i> Save</button>
                                        <button className="btn btn-secondary btn-sm" disabled={busy} onClick={handleBillToReset} title="Go back to the account's own name and address">Use account details</button>
                                        <button className="btn btn-secondary btn-sm" onClick={() => setBillToDraft(null)}>Cancel</button>
                                    </div>
                                </div>
                            )}
                        </div>
                        <div className="form-group">
                            <label className="form-label">Status</label>
                            <select className="form-input" value={doc.status} disabled={busy || isDead} onChange={e => handleStatus(e.target.value)}>
                                {statusOptions.map(s => (
                                    <option key={s} value={s} disabled={(s === 'paid' || s === 'partial') && s !== doc.status}>
                                        {s.charAt(0).toUpperCase() + s.slice(1)}
                                    </option>
                                ))}
                            </select>
                        </div>
                        <div className="form-group">
                            <label className="form-label">Issued</label>
                            <div>{formatDate(doc.issue_date || doc.created_at)}</div>
                        </div>
                        <div className="form-group">
                            <label className="form-label">{K.dateLabel}</label>
                            <input type="date" className="form-input" disabled={busy || isDead}
                                value={doc[K.dateKey] ? String(doc[K.dateKey]).slice(0, 10) : ''}
                                onChange={e => run(() => (kind === 'quote' ? api.updateQuote : api.updateInvoice)(doc.id, { [K.dateKey]: e.target.value || null }))} />
                        </div>
                    </div>

                    {/* Line items */}
                    <h4 style={{ margin: '0 0 0.5rem', fontSize: '0.9375rem' }}>Line Items
                        {!editable && !isDead && <span style={{ fontWeight: 400, fontSize: '0.75rem', color: 'var(--text-3)', marginLeft: '0.5rem' }}>
                            (locked — {kind === 'invoice' ? 'issued invoices are financial records; void and reissue to change them' : 'reopen by setting status back'})
                        </span>}
                    </h4>
                    <table className="line-items-table">
                        <thead><tr>
                            <th style={{ width: '45%' }}>Description</th>
                            <th style={{ width: '12%', textAlign: 'right' }}>Qty</th>
                            <th style={{ width: '16%', textAlign: 'right' }}>Unit Price</th>
                            <th style={{ width: '16%', textAlign: 'right' }}>Total</th>
                            <th style={{ width: '11%' }}></th>
                        </tr></thead>
                        <tbody>
                            {doc.items.length === 0 && !editRows.new
                                ? <tr><td colSpan={5} style={{ textAlign: 'center', color: 'var(--text-3)', padding: '1rem' }}>No line items</td></tr>
                                : doc.items.map(renderItemRow)}
                            {editRows.new && renderItemRow({ id: 'new', description: '', quantity: 1, unit_price: 0 })}
                        </tbody>
                    </table>
                    {editable && !editRows.new && (
                        <button className="btn btn-secondary btn-small" onClick={() => setEditRows(p => ({ ...p, new: { description: '', quantity: 1, unit_price: 0, product_id: null } }))}>
                            <i className="fas fa-plus"></i> Add Line Item
                        </button>
                    )}

                    {/* Totals (server-computed — the numbers on record) */}
                    <div style={{ display: 'flex', justifyContent: 'flex-end', marginTop: '1rem' }}>
                        <div className="totals-block">
                            <div className="totals-row"><span>Subtotal</span><span>{money(doc.subtotal)}</span></div>
                            <div className="totals-row">
                                <span>Tax {editable
                                    ? <input type="number" min="0" max="100" step="0.1" defaultValue={doc.tax_rate}
                                        onBlur={e => parseFloat(e.target.value) !== parseFloat(doc.tax_rate) && handleTax(e.target.value)}
                                        style={{ width: '4rem', marginLeft: '0.5rem' }} />
                                    : `(${Number(doc.tax_rate)}%)`}{editable ? '%' : ''}</span>
                                <span>{money(doc.tax_amount)}</span>
                            </div>
                            {/* All-recurring single-cadence doc → Total wears the term */}
                            <div className="totals-row total-final"><span>Total</span><span>{money(doc.total)}{(() => {
                                const its = doc.items || [];
                                if (!its.length || !its.every(it => it.is_subscription)) return '';
                                const ivs = new Set(its.map(it => it.billing_interval === 'year' ? ' /yr' : ' /mo'));
                                return ivs.size === 1 ? [...ivs][0] : '';
                            })()}</span></div>
                            {kind === 'invoice' && <div className="totals-row"><span>Paid</span><span>{money(doc.amount_paid)}</span></div>}
                            {kind === 'invoice' && <div className="totals-row" style={{ fontWeight: 600 }}><span>Balance</span><span>{money(balance)}</span></div>}
                            {/* Recurring summary — the total alone would read one-time */}
                            {(() => {
                                const rec = { month: 0, year: 0 };
                                (doc.items || []).forEach(it => {
                                    if (it.is_subscription) rec[it.billing_interval === 'year' ? 'year' : 'month'] += Number(it.total || 0);
                                });
                                return [
                                    rec.month > 0 && <div key="m" className="totals-row" style={{ color: 'var(--text-3)' }}><span>Billed monthly</span><span>{money(rec.month)} /mo</span></div>,
                                    rec.year > 0 && <div key="y" className="totals-row" style={{ color: 'var(--text-3)' }}><span>Billed annually</span><span>{money(rec.year)} /yr</span></div>,
                                ];
                            })()}
                        </div>
                    </div>

                    {/* Payment history (invoices) */}
                    {kind === 'invoice' && doc.payments && doc.payments.length > 0 && (
                        <div style={{ marginTop: '1rem' }}>
                            <h4 style={{ margin: '0 0 0.5rem', fontSize: '0.9375rem' }}>Payments</h4>
                            <table className="data-table">
                                <thead><tr><th>Date</th><th>Amount</th><th>Method</th><th>Reference</th><th>Recorded by</th></tr></thead>
                                <tbody>
                                    {doc.payments.map(p => (
                                        <tr key={p.id}>
                                            <td>{formatDate(p.paid_at)}</td>
                                            <td style={{ fontWeight: 500 }}>{money(p.amount)}</td>
                                            <td>{p.method || '—'}</td>
                                            <td>{p.reference || '—'}</td>
                                            <td>{p.recorded_by_name || '—'}</td>
                                        </tr>
                                    ))}
                                </tbody>
                            </table>
                        </div>
                    )}

                    {/* Acceptance record — the clickwrap evidence, read-only */}
                    {kind === 'quote' && acceptance && (
                        <div style={{ marginTop: '1rem', padding: '0.75rem', border: '1px solid var(--border)', borderRadius: '0.5rem', background: 'var(--surface-2)' }}>
                            <h4 style={{ margin: '0 0 0.375rem', fontSize: '0.9375rem' }}>
                                <i className="fas fa-file-signature" style={{ marginRight: '0.375rem', color: 'var(--accent)' }}></i>Acceptance record
                            </h4>
                            <div style={{ fontSize: '0.8125rem', color: 'var(--text-2)', lineHeight: 1.7 }}>
                                Accepted {acceptance.accepted_name ? <>by <strong>{acceptance.accepted_name}</strong> </> : ''}
                                on {new Date(acceptance.accepted_at).toLocaleString()}
                                {acceptance.ip ? <> · from {acceptance.ip}</> : ''}
                                {acceptance.terms_version
                                    ? <> · agreed to Terms of Service v{acceptance.terms_version}{' '}
                                        <a href="#" onClick={(e) => { e.preventDefault(); setShowTermsText(v => !v); }}>
                                            {showTermsText ? 'hide text' : 'view exact text'}
                                        </a></>
                                    : <> · no terms were configured at acceptance</>}
                            </div>
                            {showTermsText && acceptance.terms_text && (
                                <div style={{ maxHeight: 'min(14rem, 35vh)', overflowY: 'auto', border: '1px solid var(--border)', borderRadius: '0.375rem', padding: '0.625rem 0.875rem', marginTop: '0.5rem', fontSize: '0.75rem', whiteSpace: 'pre-wrap' }}>
                                    {acceptance.terms_text}
                                </div>
                            )}
                        </div>
                    )}

                    {/* Notes */}
                    <div style={{ marginTop: '1rem' }}>
                        <h4 style={{ margin: '0 0 0.5rem', fontSize: '0.9375rem' }}>Notes
                            {!isDead && notesDraft === null && (
                                <button className="btn-icon-sm" style={{ marginLeft: '0.5rem' }} title="Edit notes" onClick={() => setNotesDraft(doc.notes || '')}><i className="fas fa-pen"></i></button>
                            )}
                        </h4>
                        {notesDraft === null
                            ? <div style={{ whiteSpace: 'pre-line', color: doc.notes ? 'inherit' : 'var(--text-3)' }}>{doc.notes || 'No notes'}</div>
                            : (
                                <div>
                                    <textarea className="form-input" style={{ minHeight: 60, width: '100%' }} value={notesDraft} onChange={e => setNotesDraft(e.target.value)} />
                                    <div style={{ display: 'flex', gap: '0.5rem', marginTop: '0.5rem' }}>
                                        <button className="btn btn-primary btn-small" disabled={busy} onClick={handleNotesSave}><i className="fas fa-check"></i> Save</button>
                                        <button className="btn btn-secondary btn-small" onClick={() => setNotesDraft(null)}>Cancel</button>
                                    </div>
                                </div>
                            )}
                    </div>

                    {/* Send panel */}
                    {showSend && !isDead && (
                        <div style={{ marginTop: '1rem', padding: '0.75rem', border: '1px solid var(--border)', borderRadius: '0.5rem' }}>
                            <h4 style={{ margin: '0 0 0.5rem', fontSize: '0.9375rem' }}>Email this {kind}</h4>
                            <div className="form-group" style={{ marginBottom: '0.5rem' }}>
                                <label className="form-label">To</label>
                                <input type="email" className="form-input" value={sendTo} onChange={e => setSendTo(e.target.value)} placeholder="customer@example.com" />
                            </div>
                            <div className="form-group" style={{ marginBottom: '0.5rem' }}>
                                <label className="form-label">Personal message (optional)</label>
                                <textarea className="form-input" style={{ minHeight: 50 }} value={sendMsg} onChange={e => setSendMsg(e.target.value)} placeholder="A short note above the attached PDF…" />
                            </div>
                            <div style={{ display: 'flex', gap: '0.5rem' }}>
                                <button className="btn btn-primary btn-small" disabled={busy || !sendTo} onClick={handleSend}>
                                    {busy ? <><i className="fas fa-spinner fa-spin"></i> Sending…</> : <><i className="fas fa-paper-plane"></i> Send with PDF</>}
                                </button>
                                <button className="btn btn-secondary btn-small" onClick={() => setShowSend(false)}>Cancel</button>
                            </div>
                            <div style={{ fontSize: '0.75rem', color: 'var(--text-3)', marginTop: '0.5rem' }}>
                                Sends from your connected mailbox and logs to the account's communications.
                            </div>
                        </div>
                    )}

                    {/* Footer actions */}
                    <div className="btn-group" style={{ marginTop: '1.25rem' }}>
                        <button className="btn btn-secondary btn-small" disabled={busy} onClick={handleDownload}><i className="fas fa-file-pdf"></i> Download PDF</button>
                        {!isDead && <button className="btn btn-primary btn-small" disabled={busy} onClick={() => setShowSend(s => !s)}><i className="fas fa-paper-plane"></i> Email…</button>}
                        {kind === 'quote' && (doc.status === 'draft' || doc.status === 'sent') && (
                            <button className="btn btn-secondary btn-small" disabled={busy} title="Copy the customer's Review & Accept link"
                                    onClick={() => run(async () => {
                                        const { url } = await api.mintAcceptLink(doc.id);
                                        try { await navigator.clipboard.writeText(url); alert('Accept link copied. Note: minting a new link retires any previous one.'); }
                                        catch { window.prompt('Copy the accept link:', url); }
                                    })}>
                                <i className="fas fa-signature"></i> Accept Link
                            </button>
                        )}
                        {kind === 'quote' && !isDead && (doc.status === 'draft' || doc.status === 'sent' || doc.status === 'accepted') && currentUser.role !== 'rep' && (
                            <button className="btn btn-secondary btn-small" disabled={busy} onClick={() => {
                                if (window.confirm(`Convert ${number} to an invoice?`)) run(() => api.convertQuote(doc.id, {}));
                            }}><i className="fas fa-file-invoice"></i> Convert to Invoice</button>
                        )}
                        {!isDead && doc.status !== 'draft' && (
                            <button className="btn btn-secondary btn-small" disabled={busy} style={{ color: '#ef4444' }} onClick={() => handleStatus(K.deadStatus)}>
                                <i className="fas fa-ban"></i> {K.deadVerb}
                            </button>
                        )}
                        {/* Delete mirrors the server's never-delete lifecycle: quote drafts
                            deletable by anyone with access, issued quotes admin-only (audited);
                            invoice drafts only — issued invoices are void-only, so no button. */}
                        {(kind === 'quote'
                            ? (doc.status === 'draft' || currentUser.role === 'admin')
                            : doc.status === 'draft') && (
                            <button className="btn btn-secondary btn-small danger" disabled={busy} style={{ color: '#ef4444' }}
                                    title={doc.status === 'draft' ? 'Delete draft' : `Delete ${kind} (admin — audited)`}
                                    onClick={() => {
                                        if (!window.confirm(`Delete ${kind} ${number}? This removes it entirely.`)) return;
                                        // Not run(): the post-action reload would 404 on a deleted doc.
                                        setBusy(true); setErr('');
                                        (kind === 'quote' ? api.deleteQuote : api.deleteInvoice)(doc.id)
                                            .then(() => { onChanged && onChanged(); onClose(); })
                                            .catch(e => { setErr(e.message); setBusy(false); });
                                    }}>
                                <i className="fas fa-trash"></i> Delete
                            </button>
                        )}
                        <button className="btn btn-secondary btn-small" onClick={onClose}>Close</button>
                    </div>
        </>
    );

    if (inline) return (
        <div className="doc-pane">
            <div className="doc-pane-header">{headerContent}</div>
            <div className="doc-pane-body">{body}</div>
        </div>
    );

    return (
        <div className="modal-overlay" onClick={(e) => e.target === e.currentTarget && onClose()}>
            <div className="modal builder-modal">
                <div className="modal-header">{headerContent}</div>
                <div className="modal-body">{body}</div>
            </div>
        </div>
    );
};
