// Render an HTML document into an A4 PORTRAIT image-only PDF.
// Mirrors the property-brochure pipeline (skills/property-brochure/references/build.md).
// Each <section class="page"> is screenshot as ONE PNG; bound to A4 via img2pdf;
// linearized via qpdf. The HTML CSS sizes each .page to exactly A4 (210mm × 297mm).
//
//   node render-pdf.mjs <input.html> <output.pdf> [scale]

import { chromium } from 'playwright';
import { execFileSync } from 'node:child_process';
import { mkdirSync, readdirSync, rmSync, statSync } from 'node:fs';
import { dirname, join, resolve, basename } from 'node:path';
import { fileURLToPath } from 'node:url';

const __dirname = dirname(fileURLToPath(import.meta.url));

const inputArg = process.argv[2];
const outputArg = process.argv[3];
const SCALE = parseFloat(process.argv[4] || '3.125');   // 3.125× → 300 dpi
if (!inputArg || !outputArg) {
  console.error('Usage: node render-pdf.mjs <input.html> <output.pdf> [scale]');
  process.exit(2);
}

const INPUT = resolve(inputArg);
const OUTPUT = resolve(outputArg);
const TMP = join(__dirname, `.tmp-${basename(INPUT, '.html')}`);

rmSync(TMP, { recursive: true, force: true });
mkdirSync(TMP, { recursive: true });

const browser = await chromium.launch();
const context = await browser.newContext({
  viewport: { width: 794, height: 1123 },               // A4 portrait at 96 dpi
  deviceScaleFactor: SCALE,
});
const page = await context.newPage();
await page.emulateMedia({ media: 'print' });               // trigger @media print rules
await page.goto(`file://${INPUT}`, { waitUntil: 'networkidle' });

// Hide screen-only chrome but DO NOT inject layout CSS — the HTML's @media print
// rules are responsible for sizing each .page to A4. Per the brochure pipeline.
await page.addStyleTag({ content: `
  .pdf-btn { display: none !important; }
  html, body { background: #fff !important; margin: 0 !important; }
` });

await page.evaluate(() => Promise.all(
  Array.from(document.images, img => img.complete ? null : new Promise(r => { img.onload = img.onerror = r; }))
));

const sections = await page.$$('section.page');
if (!sections.length) {
  console.error('No <section class="page"> found in', INPUT);
  process.exit(3);
}
console.log(`Rendering ${sections.length} A4 page(s) from ${basename(INPUT)} at ${SCALE}× ...`);

// Per build.md: "Verify every page fits A4: measure scrollHeight for all .page elements"
// A4 portrait at 96 dpi is 1123 CSS px. Anything past that is content overflow that will
// be clipped by `overflow: hidden` and lost from the rendered PDF.
const A4_H = 1123;
let overflowed = false;
for (let i = 0; i < sections.length; i++) {
  const box = await sections[i].boundingBox();
  const scrollH = await sections[i].evaluate(el => el.scrollHeight);
  const ok = scrollH <= A4_H + 2;     // 2px slack
  if (!ok) overflowed = true;
  console.log(`  page ${i + 1}: scrollHeight ${scrollH}px${ok ? '' : `  ⚠ EXCEEDS A4 (${A4_H}px) — content will be clipped`}`);
}
if (overflowed) {
  console.error('\nOne or more <section class="page"> elements overflow A4 height. Split their content into separate <section class="page"> blocks. Aborting render.');
  await browser.close();
  process.exit(4);
}

for (let i = 0; i < sections.length; i++) {
  const file = join(TMP, `page-${String(i + 1).padStart(2, '0')}.png`);
  await sections[i].scrollIntoViewIfNeeded();
  await sections[i].screenshot({ path: file, type: 'png' });
  const bytes = statSync(file).size;
  console.log(`  page ${i + 1}: ${(bytes / 1024).toFixed(0)} KB`);
}

await browser.close();

const pngs = readdirSync(TMP).filter(f => f.startsWith('page-') && f.endsWith('.png')).sort().map(f => join(TMP, f));
const preLinear = join(TMP, 'pre-linearize.pdf');
execFileSync('img2pdf', ['--pagesize', '210mmx297mm', '--output', preLinear, ...pngs], { stdio: 'inherit' });
execFileSync('qpdf', ['--linearize', '--object-streams=disable', preLinear, OUTPUT], { stdio: 'inherit' });

console.log(`\nWrote ${OUTPUT}`);
