import { PDFDocument, StandardFonts, rgb, type PDFFont, type PDFImage } from "pdf-lib";

import { dataPorExtenso, paraWinAnsi } from "../certificado.ts";

/*
 * O PDF do certificado (A4 deitado): o nome do aluno, o curso, a carga
 * horária, a data, a assinatura das Configurações e o código para conferir.
 * Só o servidor monta (a biblioteca de PDF não vai para as páginas).
 */

function corDoHex(hex: string, padrao = { r: 0.31, g: 0.27, b: 0.9 }) {
  const m = /^#?([0-9a-f]{6})$/i.exec(String(hex || "").trim());
  if (!m) return rgb(padrao.r, padrao.g, padrao.b);
  const n = parseInt(m[1], 16);
  return rgb(((n >> 16) & 255) / 255, ((n >> 8) & 255) / 255, (n & 255) / 255);
}

/* O tamanho da letra que cabe na largura (de `maior` para baixo, até `menor`). */
function tamanhoQueCabe(fonte: PDFFont, texto: string, largura: number, maior: number, menor: number): number {
  let tamanho = maior;
  while (tamanho > menor && fonte.widthOfTextAtSize(texto, tamanho) > largura) tamanho -= 1;
  return tamanho;
}

/* Quebra o texto em até `linhas` linhas que cabem na largura. */
function quebrar(fonte: PDFFont, texto: string, tamanho: number, largura: number, linhas: number): string[] {
  const palavras = texto.split(" ");
  const saida: string[] = [];
  let atual = "";
  for (const palavra of palavras) {
    const tentativa = atual ? `${atual} ${palavra}` : palavra;
    if (fonte.widthOfTextAtSize(tentativa, tamanho) <= largura || !atual) atual = tentativa;
    else {
      saida.push(atual);
      atual = palavra;
    }
  }
  if (atual) saida.push(atual);
  if (saida.length <= linhas) return saida;
  const cortadas = saida.slice(0, linhas);
  let ultima = cortadas[linhas - 1];
  while (ultima.length > 1 && fonte.widthOfTextAtSize(`${ultima}…`, tamanho) > largura) ultima = ultima.slice(0, -1);
  cortadas[linhas - 1] = `${ultima.trimEnd()}…`;
  return cortadas;
}

export type DadosDoCertificado = {
  loja: string;
  cor: string;
  aluno: string;
  curso: string;
  cargaHoraria: string;
  emitidoEm: string;
  codigo: string;
  /* Onde conferir (a página pública do certificado). */
  endereco: string;
  assinatura: string | null;
  logo?: { bytes: Uint8Array; tipo: "png" | "jpg" } | null;
};

/** O PDF do certificado (A4 deitado). */
export async function pdfDoCertificado(dados: DadosDoCertificado): Promise<Uint8Array> {
  const documento = await PDFDocument.create();
  documento.setTitle(paraWinAnsi(`Certificado - ${dados.curso}`));
  documento.setAuthor(paraWinAnsi(dados.loja));
  documento.setCreator("Ktalogu");
  const pagina = documento.addPage([841.89, 595.28]);
  const { width: largura, height: altura } = pagina.getSize();
  const normal = await documento.embedFont(StandardFonts.Helvetica);
  const negrito = await documento.embedFont(StandardFonts.HelveticaBold);
  const cor = corDoHex(dados.cor);
  const cinza = rgb(0.37, 0.39, 0.47);
  const escuro = rgb(0.11, 0.14, 0.25);

  /* As molduras. */
  pagina.drawRectangle({ x: 22, y: 22, width: largura - 44, height: altura - 44, borderColor: cor, borderWidth: 2.5 });
  pagina.drawRectangle({ x: 32, y: 32, width: largura - 64, height: altura - 64, borderColor: rgb(0.86, 0.87, 0.9), borderWidth: 0.8 });

  const centro = (texto: string, y: number, fonte: PDFFont, tamanho: number, cordoTexto = escuro) => {
    const limpo = paraWinAnsi(texto);
    pagina.drawText(limpo, { x: (largura - fonte.widthOfTextAtSize(limpo, tamanho)) / 2, y, size: tamanho, font: fonte, color: cordoTexto });
  };

  /* O topo: a logo da loja (ou o nome). */
  let y = altura - 128;
  let imagem: PDFImage | null = null;
  if (dados.logo) {
    try {
      imagem = dados.logo.tipo === "png" ? await documento.embedPng(dados.logo.bytes) : await documento.embedJpg(dados.logo.bytes);
    } catch {
      imagem = null;
    }
  }
  if (imagem) {
    const escala = Math.min(150 / imagem.width, 46 / imagem.height, 1);
    const w = imagem.width * escala;
    const h = imagem.height * escala;
    pagina.drawImage(imagem, { x: (largura - w) / 2, y: y - h + 16, width: w, height: h });
    y -= h + 8;
  } else {
    centro(dados.loja, y, negrito, 15, cor);
    y -= 26;
  }

  centro("CERTIFICADO DE CONCLUSÃO", y - 18, negrito, 13, cor);
  centro("Certificamos que", y - 62, normal, 13, cinza);

  const aluno = paraWinAnsi(dados.aluno) || "Cliente";
  centro(aluno, y - 104, negrito, tamanhoQueCabe(negrito, aluno, largura - 160, 34, 18));

  centro("concluiu o conteúdo", y - 140, normal, 13, cinza);
  const curso = paraWinAnsi(dados.curso);
  const tamanhoDoCurso = tamanhoQueCabe(negrito, curso, largura - 180, 22, 15);
  const linhasDoCurso = quebrar(negrito, curso, tamanhoDoCurso, largura - 180, 2);
  linhasDoCurso.forEach((linha, i) => centro(linha, y - 172 - i * (tamanhoDoCurso + 6), negrito, tamanhoDoCurso));
  const depoisDoCurso = y - 172 - (linhasDoCurso.length - 1) * (tamanhoDoCurso + 6);
  centro(`com carga horária de ${dados.cargaHoraria}, em ${dataPorExtenso(dados.emitidoEm)}.`, depoisDoCurso - 34, normal, 12, cinza);

  /* A assinatura, à esquerda. */
  const baseDoRodape = 92;
  if (dados.assinatura) {
    const assinatura = paraWinAnsi(dados.assinatura);
    pagina.drawLine({ start: { x: 80, y: baseDoRodape + 18 }, end: { x: 330, y: baseDoRodape + 18 }, thickness: 0.8, color: rgb(0.6, 0.62, 0.68) });
    pagina.drawText(assinatura, { x: 80, y: baseDoRodape, size: 11, font: negrito, color: escuro });
    pagina.drawText(paraWinAnsi(dados.loja), { x: 80, y: baseDoRodape - 15, size: 9.5, font: normal, color: cinza });
  } else {
    pagina.drawText(paraWinAnsi(dados.loja), { x: 80, y: baseDoRodape, size: 11, font: negrito, color: escuro });
  }

  /* O código e onde conferir, à direita. */
  const codigo = paraWinAnsi(`Código: ${dados.codigo}`);
  const tamanhoDoEndereco = tamanhoQueCabe(normal, paraWinAnsi(dados.endereco), 330, 9, 6);
  pagina.drawText(codigo, { x: largura - 80 - negrito.widthOfTextAtSize(codigo, 11), y: baseDoRodape, size: 11, font: negrito, color: escuro });
  const conferir = paraWinAnsi("Confira em:");
  pagina.drawText(conferir, { x: largura - 80 - normal.widthOfTextAtSize(conferir, 9.5), y: baseDoRodape - 15, size: 9.5, font: normal, color: cinza });
  const endereco = paraWinAnsi(dados.endereco);
  pagina.drawText(endereco, {
    x: largura - 80 - normal.widthOfTextAtSize(endereco, tamanhoDoEndereco),
    y: baseDoRodape - 28,
    size: tamanhoDoEndereco,
    font: normal,
    color: cor,
  });

  return documento.save();
}
