// apps/<app>/src/api/generateInvoice.ts
import { z } from 'zod';
import { createEndpoint } from 'zitejs/backend';
import { ZitePdf } from 'zitejs/pdf';
import { zite } from 'zitejs/db';
export default createEndpoint({
description: 'Render an invoice PDF and save its URL',
inputSchema: z.object({
invoiceNumber: z.string(),
customer: z.object({ name: z.string(), email: z.string() }),
lineItems: z.array(z.object({
description: z.string(),
quantity: z.number(),
unitPrice: z.number(),
})),
}),
outputSchema: z.object({ url: z.string() }),
execute: async ({ input }) => {
// Escape every user-controlled string you interpolate into the HTML.
const esc = (s: unknown) =>
String(s).replace(/&/g, '&').replace(/</g, '<')
.replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, ''');
// Coerce numbers so NaN never reaches the document.
const num = (n: unknown) => (Number.isFinite(Number(n)) ? Number(n) : 0);
const total = input.lineItems.reduce((s, li) => s + num(li.quantity) * num(li.unitPrice), 0);
const rows = input.lineItems.map(li => {
const qty = num(li.quantity), unit = num(li.unitPrice);
return `<tr>
<td>${esc(li.description)}</td>
<td class="num">${qty}</td>
<td class="num">$${unit.toFixed(2)}</td>
<td class="num">$${(qty * unit).toFixed(2)}</td>
</tr>`;
}).join('');
const html = `<!doctype html>
<html><head><meta charset="utf-8" /><style>
@page { size: letter; margin: 0.75in; }
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; color: #111827; font-size: 11pt; }
h1 { font-size: 24pt; margin: 0 0 18px; }
table { width: 100%; border-collapse: collapse; font-variant-numeric: tabular-nums; }
th { text-align: left; border-bottom: 1.5px solid #cbd5e1; padding: 8px 0; font-size: 9pt; color: #475569; }
td { padding: 8px 0; border-bottom: 1px solid #f1f5f9; }
td.num, th.num { text-align: right; }
tfoot td { border-top: 1.5px solid #cbd5e1; font-weight: 700; }
</style></head>
<body>
<h1>Invoice #${esc(input.invoiceNumber)}</h1>
<p>Bill to ${esc(input.customer.name)} · ${esc(input.customer.email)}<br />${new Date().toLocaleDateString()}</p>
<table>
<thead><tr><th>Description</th><th class="num">Qty</th><th class="num">Unit</th><th class="num">Amount</th></tr></thead>
<tbody>${rows}</tbody>
<tfoot><tr><td colspan="3" class="num">Total</td><td class="num">$${total.toFixed(2)}</td></tr></tfoot>
</table>
</body></html>`;
const { url } = await ZitePdf.renderHtml({ html, filename: `invoice-${input.invoiceNumber}.pdf` });
// Optional: keep the URL on a record so it shows up in the app.
await zite.Invoices.create({ record: { pdfUrl: url, customerName: input.customer.name } });
return { url };
},
});