/*
  O Chrome que imprime o catálogo.

  É o mesmo motor que gerou os PDFs aprovados: o HTML do modelo entra, o PDF
  sai com o tamanho de página do próprio CSS (@page), as cores chapadas e os
  links clicáveis (Comprar, WhatsApp, índice, QR).

  Onde achar o Chrome, nesta ordem:
  1. CATALOGO_PDF_CHROME, se alguém apontou um;
  2. o Chrome do puppeteer (o deploy instala o "chrome-headless-shell");
  3. um Chrome instalado no sistema.

  A página não busca nada de fora além das fontes do Google: toda foto já
  vem dentro do HTML, e qualquer outro pedido é recusado.
*/

import { existsSync } from "node:fs";
import puppeteer, { type Browser } from "puppeteer";

export class ChromeAusente extends Error {}

type Candidato = { caminho: string; casca: boolean };

function candidatos(): Candidato[] {
  const lista: Candidato[] = [];
  const escolhido = String(process.env.CATALOGO_PDF_CHROME || "").trim();
  if (escolhido) lista.push({ caminho: escolhido, casca: /headless[-_]shell/i.test(escolhido) });
  try {
    lista.push({ caminho: puppeteer.executablePath({ headless: "shell" }), casca: true });
  } catch {
    /* sem casca baixada */
  }
  try {
    lista.push({ caminho: puppeteer.executablePath({ headless: true }), casca: false });
  } catch {
    /* sem Chrome do puppeteer */
  }
  const sistema = [
    "/usr/bin/google-chrome-stable",
    "/usr/bin/google-chrome",
    "/usr/bin/chromium",
    "/usr/bin/chromium-browser",
    "/snap/bin/chromium",
    "C:/Program Files/Google/Chrome/Application/chrome.exe",
    "C:/Program Files (x86)/Google/Chrome/Application/chrome.exe",
    "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
  ];
  for (const caminho of sistema) lista.push({ caminho, casca: false });
  return lista;
}

export function chromeDisponivel(): Candidato | null {
  return candidatos().find((candidato) => candidato.caminho && existsSync(candidato.caminho)) ?? null;
}

/** O Chrome da casa (também imprime os documentos que o assistente entrega). */
export async function abrirChrome(): Promise<Browser> {
  const chrome = chromeDisponivel();
  if (!chrome) throw new ChromeAusente("Nenhum Chrome encontrado para imprimir o catálogo.");
  return puppeteer.launch({
    executablePath: chrome.caminho,
    headless: chrome.casca ? "shell" : true,
    args: ["--no-sandbox", "--disable-setuid-sandbox", "--disable-dev-shm-usage", "--no-first-run", "--no-default-browser-check"],
    /* Catálogo grande (70 páginas, 250 fotos) leva mais que o padrão. */
    protocolTimeout: 10 * 60 * 1000,
  });
}

function pedidoPermitido(url: string) {
  if (url.startsWith("data:") || url.startsWith("about:")) return true;
  try {
    const { protocol, hostname } = new URL(url);
    return protocol === "https:" && (hostname === "fonts.googleapis.com" || hostname === "fonts.gstatic.com");
  } catch {
    return false;
  }
}

export type Impresso = { pdf: Buffer; paginas: number; miniaturas: string[] };

/**
 * Imprime o HTML e fotografa algumas páginas pequenas, para a tela mostrar
 * como ficou antes de a pessoa baixar. `quais` recebe o total de páginas e
 * devolve os índices (a partir de 0).
 */
export async function imprimir(html: string, formato: "celular" | "a4", quais: (total: number) => number[]): Promise<Impresso> {
  const navegador = await abrirChrome();
  try {
    const pagina = await navegador.newPage();
    await pagina.setRequestInterception(true);
    pagina.on("request", (pedido) => {
      if (pedidoPermitido(pedido.url())) pedido.continue().catch(() => undefined);
      else pedido.abort().catch(() => undefined);
    });
    const largura = formato === "a4" ? 794 : 450;
    /* A mesma janela do protótipo aprovado. */
    await pagina.setViewport({ width: largura + 80, height: 1000, deviceScaleFactor: 2 });
    await pagina.setContent(html, { waitUntil: "load", timeout: 120000 });
    /* Só as fontes vêm de fora: espera a rede calar, como o protótipo fazia. */
    await pagina.waitForNetworkIdle({ idleTime: 500, timeout: 60000 }).catch(() => undefined);
    await pagina.evaluate(async () => {
      document.body.getBoundingClientRect();
      await document.fonts.ready;
      await Promise.all(
        [...document.images].map((img) =>
          img.complete
            ? null
            : new Promise((pronto) => {
                img.onload = img.onerror = pronto;
              })
        )
      );
    });
    const pdf = await pagina.pdf({ preferCSSPageSize: true, printBackground: true, timeout: 0 });
    /* As miniaturas são para a tela: resolução comum basta e pesa menos. */
    await pagina.setViewport({ width: largura + 80, height: 1000, deviceScaleFactor: 1 });
    const secoes = await pagina.$$(".pagina");
    const miniaturas: string[] = [];
    for (const indice of quais(secoes.length)) {
      const secao = secoes[indice];
      if (!secao) continue;
      const foto = await secao.screenshot({ type: "jpeg", quality: 72, encoding: "base64" });
      miniaturas.push(`data:image/jpeg;base64,${foto}`);
    }
    return { pdf: Buffer.from(pdf), paginas: secoes.length, miniaturas };
  } finally {
    await navegador.close().catch(() => undefined);
  }
}
