import { auth } from "@/auth";
import { db } from "@/lib/db";
import { NextRequest, NextResponse } from "next/server";
import ExcelJS from "exceljs";
import { TC_FIXED_LINE, TC_DEFAULTS } from "@/lib/validations/po";
import { parsePoTerms } from "@/lib/terms";
import { downloadBuffer } from "@/lib/storage";
import { CANCELLED_WATERMARK_PNG_BASE64, CANCELLED_WATERMARK_W, CANCELLED_WATERMARK_H } from "@/lib/cancelled-watermark";
import { getImageSize, scaleToBox } from "@/lib/image-size";
import { signatoryLayout } from "@/lib/po-export-layout";
import { canViewAllPos } from "@/lib/permissions";

// ── Company fallback constants (used when no company is linked to a PO) ──────

const DEFAULT_CO_NAME   = "PELAGIA MARINE SERVICES PVT. LTD";
const DEFAULT_CO_ADDR   = "Office address: 409-410, ZION, Plot 273, Sector-10, Kharghar, Navi Mumbai- 410210";
const DEFAULT_CO_TEL    = "Tel: +91-22-6909 9028  /  Email: technical@pelagiamarine.com  /  Mob: +91 74000 60772";
const DEFAULT_INV_ADDR  = "Pelagia Marine Services Pvt Ltd, 409-410, ZION, Plot 273, Sector-10, Kharghar, Navi Mumbai- 410210 (MH)";
const DEFAULT_INV_GST   = "Email: accounts@pelagiamarine.com    GST NO: 27AAHCP5787B1Z6";

// ── Helpers ───────────────────────────────────────────────────────────────────

function fmtDate(d: Date | null | undefined): string {
  if (!d) return "";
  return new Date(d).toLocaleDateString("en-IN", { day: "2-digit", month: "short", year: "numeric" });
}
function fmtNum(n: number, dec = 2): string {
  return n.toLocaleString("en-IN", { minimumFractionDigits: dec, maximumFractionDigits: dec });
}

// Fixed brand bar colour shown at the bottom of every exported PO (matches the sample PO).
const BRAND_BAR_COLOR = "#92D050";

function mimeForKey(key: string): string {
  const ext = key.split(".").pop()?.toLowerCase();
  return ext === "jpg" || ext === "jpeg" ? "image/jpeg" : ext === "webp" ? "image/webp" : "image/png";
}

interface EmbeddedImage { base64: string; mime: string; width: number; height: number }

// Download a stored image; return base64 + mime + pixel dimensions (or null if missing).
async function fetchImage(key: string | null | undefined): Promise<EmbeddedImage | null> {
  if (!key) return null;
  const buf = await downloadBuffer(key);
  if (!buf) return null;
  const size = getImageSize(buf) ?? { width: 100, height: 100 };
  return { base64: buf.toString("base64"), mime: mimeForKey(key), width: size.width, height: size.height };
}

// ── Route ─────────────────────────────────────────────────────────────────────

interface Props { params: Promise<{ id: string }> }

export async function GET(request: NextRequest, { params }: Props) {
  // PdfService renders this page to a real PDF (issue #14). It authenticates with
  // a short, server-only token instead of a user session — read-only, PDF only.
  const svcToken = request.nextUrl.searchParams.get("svc");
  const isService =
    !!svcToken && !!process.env.PDF_SERVICE_TOKEN && svcToken === process.env.PDF_SERVICE_TOKEN;

  const session = await auth();
  if (!session?.user && !isService) return NextResponse.json({ error: "Unauthorized" }, { status: 401 });

  const { id } = await params;
  const po = await db.purchaseOrder.findUnique({
    where: { id },
    include: {
      submitter: true, vessel: true, account: true,
      company: true,
      vendor: { include: { contacts: { where: { isPrimary: true }, take: 1 } } },
      lineItems: { orderBy: { sortOrder: "asc" } },
      actions: { include: { actor: true }, orderBy: { createdAt: "asc" } },
    },
  });
  if (!po) return NextResponse.json({ error: "Not found" }, { status: 404 });

  if (!isService) {
    // view_all_pos holders, or submitters when the view-all feature flag is on, may export
    // any PO; everyone else only their own. (PdfService bypasses this — read-only, PDF only.)
    if (!canViewAllPos(session!.user.role) && po.submitterId !== session!.user.id) {
      return NextResponse.json({ error: "Forbidden" }, { status: 403 });
    }
  }

  // Exports are available for approved POs (manager approval is a prerequisite for a valid PO
  // document) and for CANCELLED POs, which export with a diagonal "CANCELLED" watermark.
  // The submitter's signature is never embedded; only the approving manager's signature is used.
  const EXPORTABLE_STATUSES = ["MGR_APPROVED", "SENT_FOR_PAYMENT", "PAID_DELIVERED", "PARTIALLY_CLOSED", "CLOSED", "CANCELLED"];
  const isCancelled = po.status === "CANCELLED";
  if (!EXPORTABLE_STATUSES.includes(po.status)) {
    return NextResponse.json(
      { error: "Export is only available for approved purchase orders." },
      { status: 403 }
    );
  }

  const format = request.nextUrl.searchParams.get("format") ?? "pdf";
  // pdf=1 → render a clean page for PdfService: no on-screen print button and no
  // window.print() auto-trigger (Chromium's page.pdf() captures it directly).
  const cleanPdf = request.nextUrl.searchParams.get("pdf") === "1";

  // ── Company data (from linked company, or fallback to constants) ──────────
  const co = po.company;
  const CO_NAME  = co?.name           ?? DEFAULT_CO_NAME;
  const CO_ADDR  = co?.address        ? `Office address: ${co.address}` : DEFAULT_CO_ADDR;

  const telParts = [
    co?.telephone ? `Tel: ${co.telephone}` : null,
    co?.email     ? `Email: ${co.email}` : null,
    co?.mobile    ? `Mob: ${co.mobile}` : null,
  ].filter(Boolean);
  const CO_TEL   = telParts.length > 0 ? telParts.join("  /  ") : DEFAULT_CO_TEL;

  const INV_ADDR = co?.invoiceAddress ?? (co?.address ? `${co.name}, ${co.address}` : DEFAULT_INV_ADDR);
  // invoiceEmail takes priority over general email for the Invoice Details line
  const invoiceContactEmail = co?.invoiceEmail ?? co?.email ?? null;
  const INV_GST  = [
    invoiceContactEmail ? `Email: ${invoiceContactEmail}` : null,
    co?.gstNumber       ? `GST NO: ${co.gstNumber}` : null,
  ].filter(Boolean).join("    ") || DEFAULT_INV_GST;

  // ── Computed data ─────────────────────────────────────────────────────────

  const items = po.lineItems.map((li, i) => {
    const qty       = Number(li.quantity);
    const unitPrice = Number(li.unitPrice);
    const gstRate   = Number((li as { gstRate?: unknown }).gstRate ?? 0.18);
    const taxable   = Number(li.totalPrice);
    const gstAmt    = taxable * gstRate;
    const li_ = li as typeof li & { name?: string };
    const desc = li_.name ?? li.description ?? "";
    // When both name and description exist, include the optional description separately
    const optionalDesc = li_.name && li.description ? li.description : "";
    return { sn: i + 1, desc, optionalDesc, unit: li.unit, qty, unitPrice, gstRate, taxable, gstAmt, total: taxable + gstAmt };
  });

  const totalTaxable = items.reduce((s, i) => s + i.taxable, 0);
  const totalGst     = items.reduce((s, i) => s + i.gstAmt, 0);
  const grandTotal   = totalTaxable + totalGst;

  const approvalAction = [...po.actions].reverse()
    .find(a => a.actionType === "APPROVED" || a.actionType === "APPROVED_WITH_NOTE");
  const approvedBy = approvalAction?.actor.name ?? "";

  // PO date: submitter-set date → approved date → creation date
  const poDisplayDate = po.poDate ?? po.approvedAt ?? po.createdAt;

  // Fetch approver's signature for embedding in the document
  let signatureBase64: string | null = null;
  let signatureMime = "image/png";
  let signatureSize: { width: number; height: number } | null = null;
  if (approvalAction) {
    const approver = await db.user.findUnique({
      where: { id: approvalAction.actorId },
      select: { signatureKey: true },
    });
    if (approver?.signatureKey) {
      const buf = await downloadBuffer(approver.signatureKey);
      if (buf) {
        signatureBase64 = buf.toString("base64");
        const ext = approver.signatureKey.split(".").pop()?.toLowerCase();
        signatureMime = ext === "jpg" || ext === "jpeg" ? "image/jpeg" : ext === "webp" ? "image/webp" : "image/png";
        signatureSize = getImageSize(buf) ?? { width: 360, height: 96 };
      }
    }
  }

  // Company branding (logo top-left, stamp/seal in the signatory block)
  const logoImg  = await fetchImage(co?.logoKey);
  const stampImg = await fetchImage(co?.stampKey);

  const ext = po as {
    piQuotationNo?: string | null; piQuotationDate?: Date | null;
    requisitionNo?: string | null; requisitionDate?: Date | null;
    placeOfDelivery?: string | null;
    tcDelivery?: string | null; tcDispatch?: string | null;
    tcInspection?: string | null; tcTransitInsurance?: string | null;
    tcPaymentTerms?: string | null; tcOthers?: string | null;
  };
  const piNo     = ext.piQuotationNo  ?? "";
  const piDate   = fmtDate(ext.piQuotationDate);
  const reqNo    = ext.requisitionNo  ?? "";
  const reqDate  = fmtDate(ext.requisitionDate);
  const delivery = ext.placeOfDelivery ?? "";

  // T&C (issue #11): prefer the dynamic snapshot (po.terms) when present; older
  // POs fall back to the legacy tc* columns + the fixed boilerplate lines.
  const dynamicTerms = parsePoTerms((po as { terms?: unknown }).terms);
  const tcLines: [number, string, string][] =
    dynamicTerms.length > 0
      ? dynamicTerms.map((t, i) => [i + 1, (t.category || "").toUpperCase(), t.text] as [number, string, string])
      : [
          [1, "",                       TC_FIXED_LINE],
          [2, "DELIVERY",               ext.tcDelivery         ?? TC_DEFAULTS.tcDelivery],
          [3, "DISPATCH INSTRUCTIONS",  ext.tcDispatch         ?? TC_DEFAULTS.tcDispatch],
          [4, "INSPECTION",             ext.tcInspection       ?? TC_DEFAULTS.tcInspection],
          [5, "TRANSIT INSURANCE",      ext.tcTransitInsurance ?? TC_DEFAULTS.tcTransitInsurance],
          [6, "PAYMENT TERMS",          ext.tcPaymentTerms     ?? TC_DEFAULTS.tcPaymentTerms],
          ...(ext.tcOthers ? [[7, "OTHERS", ext.tcOthers] as [number, string, string]] : []),
        ];

  const vendorAddr = [
    po.vendor?.address,
    po.vendor?.gstin ? `GSTIN: ${po.vendor.gstin}` : null,
  ].filter(Boolean).join("    ");
  const primaryContact = po.vendor?.contacts?.[0];
  const vendorContact = [primaryContact?.name, primaryContact?.mobile, primaryContact?.email]
    .filter(Boolean).join("    ");

  // ── GST summary row label ─────────────────────────────────────────────────
  // If all items share the same GST rate, show it; otherwise show "GST"
  const gstRates = [...new Set(items.map(i => Math.round(i.gstRate * 100)))];
  const gstLabel = gstRates.length === 1 ? `GST (${gstRates[0]}%)` : "GST";

  // ═══════════════════════════════════════════════════════════════════════════
  // XLSX — ExcelJS with full formatting
  // ═══════════════════════════════════════════════════════════════════════════

  if (format === "xlsx") {
    const wb = new ExcelJS.Workbook();
    wb.creator = "PPMS — Pelagia Payment Management System";
    const ws = wb.addWorksheet(po.poNumber.replace(/\//g, "-"), {
      pageSetup: { paperSize: 9, orientation: "portrait", fitToPage: true, fitToWidth: 1, fitToHeight: 0 },
    });

    // ── Column widths (A-I) ─────────────────────────────────────────────────
    ws.columns = [
      { width: 22 },  // A  — label column ("Cost Centre", "Vendor Name…")
      { width: 4  },  // B  — spacer; A:B merge = 26 for labels
      { width: 28 },  // C  — main content / descriptions
      { width: 15 },  // D  — secondary labels ("Accounting Code", "Unit")
      { width: 8  },  // E  — small values (Qty, etc.)
      { width: 15 },  // F  — unit price
      { width: 15 },  // G  — labels "Requested By" / "Taxable cost" (was 7 — caused cutoff)
      { width: 8  },  // H  — GST%
      { width: 16 },  // I  — Total cost
    ];

    // ── Style constants ─────────────────────────────────────────────────────
    const thin = (argb = "FF999999") => ({ style: "thin" as const, color: { argb } });
    const med  = (argb = "FF555555") => ({ style: "medium" as const, color: { argb } });
    const bordAll  = { top: thin(), left: thin(), bottom: thin(), right: thin() };
    const bordMed  = { top: med(), left: med(), bottom: med(), right: med() };

    const ARIAL  = "Arial";
    const fBase  = { name: ARIAL, size: 9 };
    const fBold  = { name: ARIAL, size: 9, bold: true };
    const fTitle = { name: ARIAL, size: 13, bold: true };
    const fH2    = { name: ARIAL, size: 11, bold: true };
    const fSmall = { name: ARIAL, size: 8 };

    const fillHdr  = { type: "pattern" as const, pattern: "solid" as const, fgColor: { argb: "FFD8D8D8" } };
    const fillLbl  = { type: "pattern" as const, pattern: "solid" as const, fgColor: { argb: "FFF2F2F2" } };
    const fillTot  = { type: "pattern" as const, pattern: "solid" as const, fgColor: { argb: "FFF0F0F0" } };
    const fillGT   = { type: "pattern" as const, pattern: "solid" as const, fgColor: { argb: "FFD8D8D8" } };
    const fillInst = { type: "pattern" as const, pattern: "solid" as const, fgColor: { argb: "FFEAEAEA" } };

    const alignC: Partial<ExcelJS.Alignment> = { horizontal: "center", vertical: "middle", wrapText: true };
    const alignL: Partial<ExcelJS.Alignment> = { horizontal: "left",   vertical: "middle", wrapText: true };
    const alignR: Partial<ExcelJS.Alignment> = { horizontal: "right",  vertical: "middle" };

    // Helper: set cell value + style
    function sc(row: number, col: number, value: ExcelJS.CellValue, opts: {
      font?: Partial<ExcelJS.Font>;
      fill?: ExcelJS.Fill;
      border?: Partial<ExcelJS.Borders>;
      align?: Partial<ExcelJS.Alignment>;
      numFmt?: string;
    } = {}) {
      const cell = ws.getCell(row, col);
      cell.value = value;
      if (opts.font)   cell.font      = opts.font   as ExcelJS.Font;
      if (opts.fill)   cell.fill      = opts.fill;
      if (opts.border) cell.border    = opts.border;
      if (opts.align)  cell.alignment = opts.align;
      if (opts.numFmt) cell.numFmt    = opts.numFmt;
    }

    // Helper: apply border to a whole row range
    function rowBorder(row: number, c1: number, c2: number, border: Partial<ExcelJS.Borders>) {
      for (let c = c1; c <= c2; c++) ws.getCell(row, c).border = border;
    }

    // ── Row heights ─────────────────────────────────────────────────────────
    ws.getRow(1).height = 24;  // Company name
    ws.getRow(2).height = 16;  // Address
    ws.getRow(3).height = 14;  // Tel / Email
    ws.getRow(4).height = 20;  // PURCHASE ORDER title

    // ══ ROW 1: Company name ═════════════════════════════════════════════════
    sc(1, 1, CO_NAME, { font: fTitle, align: alignC });
    ws.mergeCells("A1:I1");
    ws.getRow(1).getCell(1).border = { bottom: thin() };

    // ══ ROW 2: Address ══════════════════════════════════════════════════════
    sc(2, 1, CO_ADDR, { font: fBase, align: alignC });
    ws.mergeCells("A2:I2");

    // ══ ROW 3: Tel / Email ══════════════════════════════════════════════════
    sc(3, 1, CO_TEL, { font: fSmall, align: alignC });
    ws.mergeCells("A3:I3");

    // ══ ROW 4: PURCHASE ORDER ════════════════════════════════════════════════
    sc(4, 1, "PURCHASE ORDER", { font: { ...fH2, underline: true }, align: alignC });
    ws.mergeCells("A4:I4");
    ws.getRow(4).border = { top: thin(), bottom: thin() };

    // ══ Company logo (floats top-left over the header; aspect preserved) ═════
    if (logoImg) {
      const logoId = wb.addImage({
        base64: logoImg.base64,
        extension: logoImg.mime === "image/jpeg" ? "jpeg" : "png",
      });
      ws.addImage(logoId, {
        tl: { col: 0.15, row: 0.2 } as unknown as ExcelJS.Anchor,
        ext: scaleToBox(logoImg, 96, 52),
        editAs: "oneCell",
      });
    }

    // ══ ROW 5: PO Number & Date ══════════════════════════════════════════════
    ws.getRow(5).height = 18;
    sc(5, 1, "Purchase Order No:", { font: fBold, fill: fillLbl, border: bordAll, align: alignL });
    ws.mergeCells("A5:B5");
    sc(5, 3, po.poNumber, { font: { ...fBold, color: { argb: "FF1A1A1A" } }, border: bordAll, align: alignL });
    ws.mergeCells("C5:G5");
    sc(5, 8, "Date:", { font: fBold, fill: fillLbl, border: bordAll, align: alignR });
    sc(5, 9, fmtDate(poDisplayDate), { font: fBase, border: bordAll, align: alignL });

    // ══ ROW 6: PI / Quotation ════════════════════════════════════════════════
    ws.getRow(6).height = 16;
    sc(6, 1, "Performa Invoice / Quotation No:", { font: fBold, fill: fillLbl, border: bordAll, align: alignL });
    ws.mergeCells("A6:B6");
    sc(6, 3, piNo, { font: fBase, border: bordAll, align: alignL });
    ws.mergeCells("C6:F6");
    sc(6, 7, "P I / Quotation  Date:", { font: fBold, fill: fillLbl, border: bordAll, align: alignR });
    ws.mergeCells("G6:H6");
    sc(6, 9, piDate, { font: fBase, border: bordAll, align: alignL });

    // ══ ROW 7: Cost Centre / Accounting Code / Requested By ══════════════
    ws.getRow(7).height = 18;
    sc(7, 1, "Cost Centre",    { font: fBold, fill: fillLbl, border: bordAll, align: alignL });
    ws.mergeCells("A7:B7");
    sc(7, 3, CO_NAME,          { font: fBase, border: bordAll, align: alignL });
    ws.mergeCells("C7:D7");   // span C+D so company name has room
    sc(7, 5, "Accounting Code",{ font: fBold, fill: fillLbl, border: bordAll, align: alignC });
    sc(7, 6, po.account.code,  { font: fBase, border: bordAll, align: alignC });
    sc(7, 7, "Requested By",   { font: fBold, fill: fillLbl, border: bordAll, align: alignC });
    sc(7, 8, po.submitter.name,{ font: fBase, border: bordAll, align: alignL });
    ws.mergeCells("H7:I7");

    // ══ ROW 8: Requisition / Reqn. Date / Approved By ════════════════════
    ws.getRow(8).height = 18;
    sc(8, 1, "Cost Centre/Office Requisition No.",{ font: fBold, fill: fillLbl, border: bordAll, align: alignL });
    ws.mergeCells("A8:B8");
    sc(8, 3, reqNo,            { font: fBase, border: bordAll, align: alignL });
    ws.mergeCells("C8:D8");
    sc(8, 5, "Reqn. Date",     { font: fBold, fill: fillLbl, border: bordAll, align: alignC });
    sc(8, 6, reqDate,          { font: fBase, border: bordAll, align: alignC });
    sc(8, 7, "Approved By",    { font: fBold, fill: fillLbl, border: bordAll, align: alignC });
    sc(8, 8, approvedBy,       { font: fBase, border: bordAll, align: alignL });
    ws.mergeCells("H8:I8");

    // ══ ROWS 9-10: Place of Delivery (2-row span) ═══════════════════════
    ws.getRow(9).height  = 20;
    ws.getRow(10).height = 20;
    sc(9, 1, "Place of Delivery", { font: fBold, fill: fillLbl, border: bordAll, align: alignC });
    ws.mergeCells("A9:B10");
    sc(9, 3, delivery, { font: fBase, border: bordAll, align: alignL });
    ws.mergeCells("C9:I10");

    // ══ ROWS 11-12: Invoice Details (2-row span) ════════════════════════
    ws.getRow(11).height = 18;
    ws.getRow(12).height = 16;
    sc(11, 1, "Invoice Details", { font: fBold, fill: fillLbl, border: bordAll, align: alignC });
    ws.mergeCells("A11:B12");
    sc(11, 3, INV_ADDR, { font: fSmall, border: { top: thin(), left: thin(), right: thin() }, align: alignL });
    ws.mergeCells("C11:I11");
    sc(12, 3, INV_GST, { font: fSmall, border: { bottom: thin(), left: thin(), right: thin() }, align: alignL });
    ws.mergeCells("C12:I12");

    // ══ ROW 13: Vendor Name & Address ═══════════════════════════════════
    ws.getRow(13).height = 32;
    sc(13, 1, "Vendor Name & Address", { font: fBold, fill: fillLbl, border: bordAll, align: alignC });
    ws.mergeCells("A13:B13");
    sc(13, 3, po.vendor?.name ?? "—", { font: fBold, border: bordAll, align: alignL });
    sc(13, 4, vendorAddr, { font: fSmall, border: bordAll, align: alignL });
    ws.mergeCells("D13:I13");

    // ══ ROW 14: Contact ═════════════════════════════════════════════════
    ws.getRow(14).height = 18;
    sc(14, 1, "Contact Person name / mobile no.", { font: fBold, fill: fillLbl, border: bordAll, align: alignC });
    ws.mergeCells("A14:B14");
    sc(14, 3, vendorContact, { font: fSmall, border: bordAll, align: alignL });
    ws.mergeCells("C14:I14");

    // ══ ROW 15: Line item header ═════════════════════════════════════════
    const HDR_ROW = 15;
    ws.getRow(HDR_ROW).height = 16;
    const hdrStyle = { font: fBold, fill: fillHdr, border: bordAll };
    sc(HDR_ROW, 1, "S.N.",        { ...hdrStyle, align: alignC });
    sc(HDR_ROW, 2, "Description", { ...hdrStyle, align: alignC });
    ws.mergeCells(`B${HDR_ROW}:C${HDR_ROW}`);
    sc(HDR_ROW, 4, "Unit",        { ...hdrStyle, align: alignC });
    sc(HDR_ROW, 5, "Qnty",        { ...hdrStyle, align: alignC });
    sc(HDR_ROW, 6, "Unit price",  { ...hdrStyle, align: alignC });
    sc(HDR_ROW, 7, "Taxable cost",{ ...hdrStyle, align: alignC });
    sc(HDR_ROW, 8, "GST%",        { ...hdrStyle, align: alignC });
    sc(HDR_ROW, 9, "Total cost",  { ...hdrStyle, align: alignC });

    // ══ Line items ═══════════════════════════════════════════════════════
    const NUM_FMT = '#,##0.00';
    const BODY_ROWS = Math.max(items.length, 7); // reserve at least 7 rows
    for (let idx = 0; idx < BODY_ROWS; idx++) {
      const r = HDR_ROW + 1 + idx;
      // Taller rows: long item names + potential description sub-line need room
      const descLen = (items[idx]?.desc ?? "").length + (items[idx]?.optionalDesc ?? "").length;
      ws.getRow(r).height = descLen > 40 || items[idx]?.optionalDesc ? 32 : 20;
      const item = items[idx];
      const fillAlt = idx % 2 === 1
        ? { type: "pattern" as const, pattern: "solid" as const, fgColor: { argb: "FFFAFAFA" } }
        : undefined;

      sc(r, 1, item?.sn ?? null, { font: fBase, fill: fillAlt, border: bordAll, align: alignC });
      const xlsxDesc = item
        ? (item.optionalDesc ? `${item.desc}\n${item.optionalDesc}` : item.desc)
        : "";
      sc(r, 2, xlsxDesc,   { font: fBase, fill: fillAlt, border: bordAll, align: alignL });
      ws.mergeCells(`B${r}:C${r}`);
      sc(r, 4, item?.unit ?? "",   { font: fBase, fill: fillAlt, border: bordAll, align: alignC });
      sc(r, 5, item ? item.qty : null, { font: fBase, fill: fillAlt, border: bordAll, align: alignR, numFmt: NUM_FMT });
      sc(r, 6, item ? item.unitPrice : null, { font: fBase, fill: fillAlt, border: bordAll, align: alignR, numFmt: NUM_FMT });
      sc(r, 7, item ? item.taxable : null, { font: fBase, fill: fillAlt, border: bordAll, align: alignR, numFmt: NUM_FMT });
      sc(r, 8, item ? item.gstRate : null, { font: fBase, fill: fillAlt, border: bordAll, align: alignC, numFmt: '0%' });
      sc(r, 9, item ? item.total : null, { font: { ...fBase, bold: !!item }, fill: fillAlt, border: bordAll, align: alignR, numFmt: NUM_FMT });
    }

    // ══ Totals ════════════════════════════════════════════════════════════
    const TOT_ROW = HDR_ROW + 1 + BODY_ROWS + 1;
    ws.getRow(TOT_ROW).height     = 14;
    ws.getRow(TOT_ROW + 1).height = 14;
    ws.getRow(TOT_ROW + 2).height = 16;

    // "Total taxable value"
    sc(TOT_ROW, 6, "Total taxable value", { font: fBold, fill: fillTot, border: bordAll, align: alignR });
    ws.mergeCells(`F${TOT_ROW}:G${TOT_ROW}`);
    sc(TOT_ROW, 8, totalTaxable, { font: fBold, fill: fillTot, border: bordAll, align: alignR, numFmt: NUM_FMT });
    ws.mergeCells(`H${TOT_ROW}:I${TOT_ROW}`);

    // "GST"
    sc(TOT_ROW + 1, 6, gstLabel, { font: fBold, fill: fillTot, border: bordAll, align: alignR });
    ws.mergeCells(`F${TOT_ROW + 1}:G${TOT_ROW + 1}`);
    sc(TOT_ROW + 1, 8, totalGst, { font: fBold, fill: fillTot, border: bordAll, align: alignR, numFmt: NUM_FMT });
    ws.mergeCells(`H${TOT_ROW + 1}:I${TOT_ROW + 1}`);

    // "GRAND TOTAL"
    sc(TOT_ROW + 2, 6, "GRAND TOTAL", { font: { ...fBold, size: 10 }, fill: fillGT, border: bordAll, align: alignR });
    ws.mergeCells(`F${TOT_ROW + 2}:G${TOT_ROW + 2}`);
    sc(TOT_ROW + 2, 8, grandTotal, { font: { ...fBold, size: 10 }, fill: fillGT, border: bordAll, align: alignR, numFmt: NUM_FMT });
    ws.mergeCells(`H${TOT_ROW + 2}:I${TOT_ROW + 2}`);

    // ══ Instructions ═════════════════════════════════════════════════════
    const INST_ROW = TOT_ROW + 4;
    ws.getRow(INST_ROW).height = 16;
    sc(INST_ROW, 1, "INSTRUCTIONS TO VENDORS", { font: { ...fBold, size: 10 }, fill: fillInst, border: bordAll, align: alignC });
    ws.mergeCells(`A${INST_ROW}:I${INST_ROW}`);

    tcLines.forEach(([num, label, value], i) => {
      const r = INST_ROW + 1 + i;
      ws.getRow(r).height = 14;
      sc(r, 1, num, { font: fBase, border: bordAll, align: alignC });
      const text = label ? `${label}: ${value}` : value;
      sc(r, 2, text, { font: fBase, border: bordAll, align: alignL });
      ws.mergeCells(`B${r}:I${r}`);
    });

    // ══ Signatures ═══════════════════════════════════════════════════════
    const SIG_ROW = INST_ROW + tcLines.length + 3;
    ws.getRow(SIG_ROW).height     = 40;
    ws.getRow(SIG_ROW + 1).height = 14;
    ws.getRow(SIG_ROW + 2).height = 14;

    // Left signatory block (cols A-D). Position images by absolute pixels via native
    // EMU offsets — ExcelJS's fractional-column anchors don't map cleanly to pixels.
    const EMU = 9525; // EMU per pixel
    const COL_PX = [22, 4, 28, 15, 8, 15, 15, 8, 16].map((w) => Math.round(w * 7 + 5));
    const SIG_BLOCK_PX = COL_PX[0] + COL_PX[1] + COL_PX[2] + COL_PX[3]; // A-D
    const anchorAt = (leftPx: number, row: number) => {
      let x = 0;
      for (let c = 0; c < COL_PX.length - 1; c++) {
        if (leftPx < x + COL_PX[c]) {
          return { nativeCol: c, nativeColOff: Math.round((leftPx - x) * EMU), nativeRow: row, nativeRowOff: 0 } as unknown as ExcelJS.Anchor;
        }
        x += COL_PX[c];
      }
      return { nativeCol: COL_PX.length - 1, nativeColOff: Math.round((leftPx - x) * EMU), nativeRow: row, nativeRowOff: 0 } as unknown as ExcelJS.Anchor;
    };

    const sigExt = signatureBase64 ? scaleToBox(signatureSize ?? { width: 360, height: 96 }, 165, 44) : null;
    const stampExt = stampImg ? scaleToBox(stampImg, 80, 66) : null;
    // Signature centred over the name; stamp to its RIGHT with a gap (no overlap).
    const { sigLeft, stampLeft } = signatoryLayout({ blockPx: SIG_BLOCK_PX, sig: sigExt, stamp: stampExt });

    // Stamp / seal — drawn FIRST so it layers BEHIND the signature if they ever touch.
    if (stampImg && stampExt && stampLeft != null) {
      const stampId = wb.addImage({
        base64: stampImg.base64,
        extension: stampImg.mime === "image/jpeg" ? "jpeg" : "png",
      });
      ws.addImage(stampId, {
        tl: anchorAt(stampLeft, SIG_ROW - 1),
        ext: stampExt,
        editAs: "oneCell",
      });
    }

    // Approver signature — drawn AFTER the stamp (on top), centred over the name.
    if (signatureBase64 && sigExt && sigLeft != null) {
      const imgType = signatureMime === "image/jpeg" ? "jpeg" : "png";
      const imgId = wb.addImage({ base64: signatureBase64, extension: imgType });
      ws.addImage(imgId, {
        tl: anchorAt(Math.max(0, sigLeft), SIG_ROW - 1),
        ext: sigExt,
        editAs: "oneCell",
      });
      sc(SIG_ROW,     1, "", { border: { top: thin(), left: thin(), right: thin() } });
    } else {
      sc(SIG_ROW,     1, approvedBy, { font: fBold, border: { top: thin(), left: thin(), right: thin() }, align: alignC });
    }
    ws.mergeCells(`A${SIG_ROW}:D${SIG_ROW}`);
    sc(SIG_ROW + 1, 1, approvedBy, { font: fBold, border: { left: thin(), right: thin() }, align: alignC });
    ws.mergeCells(`A${SIG_ROW + 1}:D${SIG_ROW + 1}`);
    sc(SIG_ROW + 2, 1, "Authorized Signatory & Stamp", { font: fSmall, border: { left: thin(), right: thin() }, align: alignC });
    ws.mergeCells(`A${SIG_ROW + 2}:D${SIG_ROW + 2}`);
    sc(SIG_ROW + 3, 1, "For, Pelagia Marine Services Pvt. Ltd.", { font: fSmall, border: { left: thin(), bottom: thin(), right: thin() }, align: alignC });
    ws.mergeCells(`A${SIG_ROW + 3}:D${SIG_ROW + 3}`);
    // Adjust row heights when signature present
    ws.getRow(SIG_ROW + 1).height = 14;
    ws.getRow(SIG_ROW + 2).height = 14;
    ws.getRow(SIG_ROW + 3).height = 14;

    // Right sig block (vendor)
    const vName = po.vendor?.name ?? "";
    sc(SIG_ROW,     6, vName, { font: fBold, border: { top: thin(), left: thin(), right: thin() }, align: alignC });
    ws.mergeCells(`F${SIG_ROW}:I${SIG_ROW}`);
    sc(SIG_ROW + 1, 6, "Authorized Signatory & Stamp", { font: fSmall, border: { left: thin(), right: thin() }, align: alignC });
    ws.mergeCells(`F${SIG_ROW + 1}:I${SIG_ROW + 1}`);
    sc(SIG_ROW + 2, 6, `For, ${vName}`, { font: fSmall, border: { left: thin(), bottom: thin(), right: thin() }, align: alignC });
    ws.mergeCells(`F${SIG_ROW + 2}:I${SIG_ROW + 2}`);

    // ══ Brand bar (full-width colour strip at the very bottom) ═══════════════
    const BAR_ROW = SIG_ROW + 4;
    const barArgb = "FF" + BRAND_BAR_COLOR.replace("#", "").toUpperCase();
    const barFill = { type: "pattern" as const, pattern: "solid" as const, fgColor: { argb: barArgb } };
    ws.getRow(BAR_ROW).height = 16;
    for (let c = 1; c <= 9; c++) sc(BAR_ROW, c, "", { fill: barFill });
    ws.mergeCells(`A${BAR_ROW}:I${BAR_ROW}`);

    // ══ Cancelled watermark — diagonal "CANCELLED" centred over the sheet ════
    // Pixel-sized (aspect preserved) so the text spans the page like the PDF,
    // rather than being stretched/squished by a cell-range anchor.
    if (isCancelled) {
      const wmId = wb.addImage({ base64: CANCELLED_WATERMARK_PNG_BASE64, extension: "png" });
      const ext = scaleToBox({ width: CANCELLED_WATERMARK_W, height: CANCELLED_WATERMARK_H }, 880, 720);
      ws.addImage(wmId, {
        tl: { col: 0.15, row: 5 } as unknown as ExcelJS.Anchor,
        ext,
        editAs: "oneCell",
      });
    }

    // ── Serialise ─────────────────────────────────────────────────────────
    const buf = await wb.xlsx.writeBuffer();
    const slug = po.poNumber.replace(/\//g, "-");
    return new NextResponse(Buffer.from(buf), {
      headers: {
        "Content-Type": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
        "Content-Disposition": `attachment; filename="${slug}.xlsx"`,
      },
    });
  }

  // ═══════════════════════════════════════════════════════════════════════════
  // PDF — HTML print page matching sample layout
  // ═══════════════════════════════════════════════════════════════════════════

  const itemRows = items.map((item, i) => `
    <tr style="background:${i % 2 === 0 ? "#fff" : "#fafafa"}">
      <td style="text-align:center">${item.sn}</td>
      <td>${item.desc}${item.optionalDesc ? `<br/><span style="font-size:7.5pt;color:#666;font-style:italic">${item.optionalDesc}</span>` : ""}</td>
      <td style="text-align:center">${item.unit}</td>
      <td style="text-align:right">${fmtNum(item.qty, item.qty % 1 === 0 ? 0 : 3)}</td>
      <td style="text-align:right">${fmtNum(item.unitPrice)}</td>
      <td style="text-align:right">${fmtNum(item.taxable)}</td>
      <td style="text-align:center">${Math.round(item.gstRate * 100)}%</td>
      <td style="text-align:right;font-weight:bold">${fmtNum(item.total)}</td>
    </tr>`).join("");

  // Pad to at least 7 rows so the table doesn't collapse
  const blankRows = Math.max(0, 7 - items.length);
  const padRows   = Array(blankRows).fill(
    '<tr style="height:20px"><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>'
  ).join("");

  const termRows = tcLines.map(([num, label, value]) => `
    <tr>
      <td style="text-align:center;width:24px;font-weight:bold">${num}.</td>
      <td>${label ? `<b>${label}:</b> ` : ""}${value}</td>
    </tr>`).join("");

  const html = `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<title>${po.poNumber} — Purchase Order</title>
<style>
  * { box-sizing: border-box; margin: 0; padding: 0; }
  body {
    font-family: Arial, Helvetica, sans-serif;
    font-size: 8.5pt;
    color: #111;
    margin: 10mm 12mm;
    line-height: 1.3;
    -webkit-print-color-adjust: exact;
    print-color-adjust: exact;
  }

  /* ── Header ── */
  .header-band { position: relative; }
  .co-logo {
    position: absolute;
    left: 0;
    top: 0;
    max-height: 52px;
    max-width: 92px;
    object-fit: contain;
  }
  .co-name {
    text-align: center;
    font-size: 13pt;
    font-weight: bold;
    border-bottom: 1px solid #555;
    padding-bottom: 3px;
    margin-bottom: 1px;
  }
  .co-addr, .co-tel {
    text-align: center;
    font-size: 8pt;
  }
  .po-title {
    text-align: center;
    font-size: 11pt;
    font-weight: bold;
    text-decoration: underline;
    border-top: 1.5px solid #333;
    border-bottom: 1.5px solid #333;
    padding: 3px 0;
    margin: 4px 0;
  }

  /* ── Meta tables ── */
  table { width: 100%; border-collapse: collapse; }
  .meta td { border: 1px solid #999; padding: 3px 5px; vertical-align: middle; font-size: 8.5pt; }
  .meta td.lbl { background: #f2f2f2; font-weight: bold; white-space: nowrap; }

  /* ── Line items table ── */
  .items th {
    background: #d8d8d8;
    font-weight: bold;
    border: 1px solid #888;
    padding: 4px 5px;
    text-align: center;
    font-size: 8.5pt;
  }
  .items td {
    border: 1px solid #999;
    padding: 3px 5px;
    vertical-align: middle;
    font-size: 8.5pt;
  }

  /* ── Totals ── */
  .totals { width: 55%; margin-left: auto; margin-top: 0; border-collapse: collapse; }
  .totals td { border: 1px solid #999; padding: 3px 8px; font-size: 8.5pt; }
  .totals .tot-lbl { background: #f0f0f0; font-weight: bold; text-align: right; }
  .totals .tot-val { background: #f0f0f0; text-align: right; font-weight: bold; min-width: 90px; }
  .totals .gt-lbl  { background: #d8d8d8; font-weight: bold; text-align: right; font-size: 9pt; }
  .totals .gt-val  { background: #d8d8d8; text-align: right; font-weight: bold; font-size: 9pt; min-width: 90px; }

  /* ── Instructions ── */
  .inst-hdr td { background: #eaeaea; font-weight: bold; font-size: 9pt; text-align: center; border: 1px solid #888; padding: 4px; }
  .inst-body td { border: none; padding: 2px 3px; font-size: 8pt; vertical-align: top; }

  /* ── Signatures ── */
  .sig { display: flex; justify-content: space-between; margin-top: 14px; }
  .sig-box {
    position: relative;
    border: 1px solid #999;
    width: 44%;
    min-height: 60px;
    padding: 6px 10px;
    text-align: center;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
  }
  .sig-name { font-weight: bold; font-size: 9pt; min-height: 32px; }
  .sig-sub  { font-size: 7.5pt; }
  .sig-stamp {
    position: absolute;
    right: 6px;
    top: 4px;
    max-height: 66px;
    max-width: 88px;
    object-fit: contain;
    pointer-events: none;
  }

  .spacer { margin: 4px 0; }

  /* ── Brand bar (bottom) ── */
  .brand-bar {
    height: 14px;
    width: 100%;
    margin-top: 12px;
    background: ${BRAND_BAR_COLOR};
  }

  /* ── Cancelled watermark ── */
  .cancelled-watermark {
    position: fixed;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%) rotate(-35deg);
    font-size: 96pt;
    font-weight: 800;
    letter-spacing: 8px;
    color: rgba(200, 0, 0, 0.18);
    border: 6px solid rgba(200, 0, 0, 0.18);
    padding: 8px 32px;
    border-radius: 8px;
    white-space: nowrap;
    z-index: 9999;
    pointer-events: none;
  }

  @media print {
    .no-print { display: none; }
    body { margin: 8mm 10mm; }
    @page { size: A4 portrait; margin: 0; }
  }
</style>
</head>
<body>

${isCancelled ? `<div class="cancelled-watermark">CANCELLED</div>` : ""}

${cleanPdf ? "" : `<div class="no-print" style="margin-bottom:8px">
  <button onclick="window.print()" style="padding:5px 14px;font-size:11px;cursor:pointer;border:1px solid #999;border-radius:4px">
    🖨 Print / Save as PDF
  </button>
</div>`}

<!-- ── Header ─────────────────────────────────────────────────── -->
<div class="header-band">
  ${logoImg ? `<img class="co-logo" src="data:${logoImg.mime};base64,${logoImg.base64}" alt="Logo" />` : ""}
  <div class="co-name">${CO_NAME}</div>
  <div class="co-addr">${CO_ADDR}</div>
  <div class="co-tel">${CO_TEL}</div>
</div>
<div class="po-title">PURCHASE ORDER</div>

<!-- ── PO Meta & Quotation ──────────────────────────────────── -->
<table class="meta" style="margin-bottom:0">
  <tr>
    <td class="lbl" style="width:22%">Purchase Order No:</td>
    <td style="width:28%;font-weight:bold">${po.poNumber}</td>
    <td class="lbl" style="width:14%;text-align:right">Date:</td>
    <td style="width:36%">${fmtDate(poDisplayDate)}</td>
  </tr>
  <tr>
    <td class="lbl">Performa Invoice / Quotation No:</td>
    <td>${piNo}</td>
    <td class="lbl" style="text-align:right">P I / Quotation Date:</td>
    <td>${piDate}</td>
  </tr>
</table>

<!-- ── Cost Centre / Account / Requested By ──────────────────── -->
<table class="meta" style="margin-bottom:0">
  <tr>
    <td class="lbl" style="width:22%">Cost Centre</td>
    <td style="width:24%">Pelagia Marine Services Pvt. Ltd.</td>
    <td class="lbl" style="width:12%;text-align:center">Accounting Code</td>
    <td style="width:8%;text-align:center">${po.account.code}</td>
    <td class="lbl" style="width:14%">Requested By</td>
    <td style="width:20%">${po.submitter.name}</td>
  </tr>
  <tr>
    <td class="lbl">Cost Centre/Office Requisition Number</td>
    <td>${reqNo}</td>
    <td class="lbl" style="text-align:center">Reqn. Date</td>
    <td style="text-align:center">${reqDate}</td>
    <td class="lbl">Approved By</td>
    <td>${approvedBy}</td>
  </tr>
</table>

<!-- ── Place of Delivery ─────────────────────────────────────── -->
<table class="meta" style="margin-bottom:0">
  <tr>
    <td class="lbl" style="width:22%">Place of Delivery</td>
    <td>${delivery}</td>
  </tr>
</table>

<!-- ── Invoice Details ───────────────────────────────────────── -->
<table class="meta" style="margin-bottom:0">
  <tr>
    <td class="lbl" style="width:22%;vertical-align:middle" rowspan="2">Invoice Details</td>
    <td style="border-bottom:none">${INV_ADDR}</td>
  </tr>
  <tr>
    <td style="border-top:none;font-size:8pt">${INV_GST}</td>
  </tr>
</table>

<!-- ── Vendor ────────────────────────────────────────────────── -->
<table class="meta" style="margin-bottom:4px">
  <tr>
    <td class="lbl" style="width:22%">Vendor Name &amp; Address</td>
    <td style="width:24%;font-weight:bold">${po.vendor?.name ?? "—"}</td>
    <td colspan="2">${vendorAddr}</td>
  </tr>
  <tr>
    <td class="lbl">Contact Person name / mobile no.</td>
    <td colspan="3">${vendorContact}</td>
  </tr>
</table>

<!-- ── Line Items ────────────────────────────────────────────── -->
<table class="items">
  <thead>
    <tr>
      <th style="width:4%">S.N.</th>
      <th style="width:30%">Description</th>
      <th style="width:6%;text-align:center">Unit</th>
      <th style="width:7%;text-align:right">Qnty</th>
      <th style="width:11%;text-align:right">Unit price</th>
      <th style="width:14%;text-align:right">Taxable cost</th>
      <th style="width:7%;text-align:center">GST%</th>
      <th style="width:14%;text-align:right">Total cost</th>
    </tr>
  </thead>
  <tbody>
    ${itemRows}
    ${padRows}
  </tbody>
</table>

<!-- ── Totals ────────────────────────────────────────────────── -->
<table class="totals">
  <tr>
    <td class="tot-lbl">Total taxable value</td>
    <td class="tot-val">${fmtNum(totalTaxable)}</td>
  </tr>
  <tr>
    <td class="tot-lbl">${gstLabel}</td>
    <td class="tot-val">${fmtNum(totalGst)}</td>
  </tr>
  <tr>
    <td class="gt-lbl">GRAND TOTAL</td>
    <td class="gt-val">${fmtNum(grandTotal)}</td>
  </tr>
</table>

<div class="spacer"></div>

<!-- ── Instructions to Vendors ───────────────────────────────── -->
<table style="margin-bottom:6px">
  <tr class="inst-hdr"><td colspan="2">INSTRUCTIONS TO VENDORS</td></tr>
  ${termRows}
</table>

<!-- ── Signatures ────────────────────────────────────────────── -->
<div class="sig">
  <div class="sig-box">
    ${stampImg ? `<img class="sig-stamp" src="data:${stampImg.mime};base64,${stampImg.base64}" alt="Stamp" />` : ""}
    ${signatureBase64
      ? `<img src="data:${signatureMime};base64,${signatureBase64}" alt="Signature" style="max-height:48px;max-width:180px;object-fit:contain;display:block;margin:0 auto 4px;" />`
      : `<div class="sig-name">${approvedBy}</div>`
    }
    <div>
      <div class="sig-sub" style="font-weight:bold">${approvedBy}</div>
      <div class="sig-sub">Authorized Signatory &amp; Stamp</div>
      <div class="sig-sub">For, ${CO_NAME}</div>
    </div>
  </div>
  <div class="sig-box">
    <div class="sig-name">${po.vendor?.name ?? ""}</div>
    <div>
      <div class="sig-sub">Authorized Signatory &amp; Stamp</div>
      <div class="sig-sub">For, ${po.vendor?.name ?? ""}</div>
    </div>
  </div>
</div>

<!-- ── Brand bar ─────────────────────────────────────────────── -->
<div class="brand-bar"></div>

${cleanPdf ? "" : `<script>window.onload = function() { window.print(); };</script>`}
</body>
</html>`;

  return new NextResponse(html, { headers: { "Content-Type": "text/html; charset=utf-8" } });
}
