"use client";

type PdfButton = HTMLButtonElement | null | undefined;

export async function downloadElementPdf(element: HTMLElement | null, filename: string, trigger?: PdfButton) {
  if (!element) throw new Error("Document PDF introuvable.");
  const previousLabel = trigger?.textContent ?? "";
  if (trigger) {
    trigger.disabled = true;
    trigger.textContent = "Génération du PDF…";
  }
  const excluded = Array.from(element.querySelectorAll<HTMLElement>('[data-pdf-exclude="true"]'));
  const excludedDisplays = excluded.map((node) => node.style.display);
  try {
    excluded.forEach((node) => { node.style.display = "none"; });
    element.classList.add("pdf-exporting");
    await new Promise<void>((resolve) => requestAnimationFrame(() => requestAnimationFrame(() => resolve())));
    await document.fonts.ready;
    await waitForImages(element);
    const [{ toCanvas }, { jsPDF }] = await Promise.all([
      import("html-to-image"),
      import("jspdf"),
    ]);
    const captureWidth = Math.ceil(Math.max(element.scrollWidth, element.getBoundingClientRect().width));
    const captureHeight = Math.ceil(Math.max(element.scrollHeight, element.getBoundingClientRect().height));
    const canvas = await toCanvas(element, {
      backgroundColor: "#ffffff",
      cacheBust: true,
      pixelRatio: 2,
      width: captureWidth,
      height: captureHeight,
      style: { margin: "0" },
      filter: (node) => !(node instanceof HTMLElement && node.dataset.pdfExclude === "true"),
    });
    const pageWidth = 210;
    const pageHeight = 297;
    const margin = 8;
    const contentWidth = pageWidth - margin * 2;
    const contentHeight = pageHeight - margin * 2;
    const naturalHeight = canvas.height * contentWidth / canvas.width;
    const singlePage = naturalHeight <= pageHeight * 1.15;
    const renderedHeight = singlePage ? Math.min(naturalHeight, contentHeight) : naturalHeight;
    const renderedWidth = singlePage ? canvas.width * renderedHeight / canvas.height : contentWidth;
    const left = (pageWidth - renderedWidth) / 2;
    const pageCount = singlePage ? 1 : Math.max(1, Math.ceil((renderedHeight - .5) / contentHeight));
    const image = canvas.toDataURL("image/jpeg", .94);
    const pdf = new jsPDF({ orientation: "portrait", unit: "mm", format: "a4", compress: true });
    for (let page = 0; page < pageCount; page += 1) {
      if (page > 0) pdf.addPage();
      pdf.addImage(image, "JPEG", left, margin - page * contentHeight, renderedWidth, renderedHeight, "document", "FAST");
    }
    pdf.save(safePdfName(filename));
  } finally {
    element.classList.remove("pdf-exporting");
    excluded.forEach((node, index) => { node.style.display = excludedDisplays[index]; });
    if (trigger) {
      trigger.disabled = false;
      trigger.textContent = previousLabel;
    }
  }
}

async function waitForImages(element: HTMLElement) {
  const images = Array.from(element.querySelectorAll<HTMLImageElement>("img"));
  await Promise.all(images.map(async (image) => {
    if (!image.complete) {
      await new Promise<void>((resolve) => {
        image.addEventListener("load", () => resolve(), { once: true });
        image.addEventListener("error", () => resolve(), { once: true });
      });
    }
    if (typeof image.decode === "function") await image.decode().catch(() => undefined);
  }));
}

function safePdfName(filename: string) {
  const cleaned = filename.normalize("NFD").replace(/[\u0300-\u036f]/g, "")
    .replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
  return `${cleaned || "pyramidcom-document"}.pdf`.replace(/\.pdf\.pdf$/i, ".pdf");
}
