import type { jsPDFOptions } from 'jspdf';

import html2canvas from '@wtto00/html2canvas';
import { jsPDF as JsPdf } from 'jspdf';

export interface LabelPdfOptions {
  fileName: string;
  html: string;
  paperHeight: number;
  paperWidth: number;
}

interface LabelPdfDependencies {
  createCanvas: typeof html2canvas;
  createPdf: (options: jsPDFOptions) => Pick<JsPdf, 'addImage' | 'addPage' | 'save'>;
}

const defaultDependencies: LabelPdfDependencies = {
  createCanvas: html2canvas,
  createPdf: (options) => new JsPdf(options),
};

function waitForImage(image: HTMLImageElement) {
  if (image.complete) return Promise.resolve();
  return new Promise<void>((resolve) => {
    image.addEventListener('load', () => resolve(), { once: true });
    image.addEventListener('error', () => resolve(), { once: true });
  });
}

async function waitForAssets(container: HTMLElement) {
  const fonts = (document as Document & { fonts?: { ready?: Promise<unknown> } }).fonts;
  await Promise.all([fonts?.ready, ...[...container.querySelectorAll('img')].map((image) => waitForImage(image))].filter(Boolean));
}

export async function exportLabelLayoutPdf(options: LabelPdfOptions, dependencies: LabelPdfDependencies = defaultDependencies) {
  const { fileName, html, paperHeight, paperWidth } = options;
  if (!html.trim()) throw new Error('标签拼版内容为空，无法导出 PDF');
  if (!(paperWidth > 0) || !(paperHeight > 0)) throw new Error('PDF 纸张宽高必须大于 0');

  const staging = document.createElement('div');
  staging.setAttribute('aria-hidden', 'true');
  staging.style.cssText = 'position:fixed;left:-100000px;top:0;background:#fff;pointer-events:none;';
  staging.innerHTML = html;
  document.body.append(staging);

  try {
    const sheets = [...staging.querySelectorAll<HTMLElement>('.jnpf-label-sheet')];
    if (!sheets.length) throw new Error('未找到可导出的标签页面');
    await waitForAssets(staging);

    const pdf = dependencies.createPdf({
      format: [paperWidth, paperHeight],
      orientation: paperWidth > paperHeight ? 'landscape' : 'portrait',
      unit: 'mm',
    });
    for (const [index, sheet] of sheets.entries()) {
      if (index > 0) pdf.addPage([paperWidth, paperHeight], paperWidth > paperHeight ? 'landscape' : 'portrait');
      const canvas = await dependencies.createCanvas(sheet, {
        backgroundColor: '#fff',
        logging: false,
        scale: 2,
        useCORS: true,
      });
      pdf.addImage(canvas.toDataURL('image/png'), 'PNG', 0, 0, paperWidth, paperHeight, undefined, 'FAST');
    }
    pdf.save(`${fileName}.pdf`);
  } finally {
    staging.remove();
  }
}
