/* ============================================================
 * Copyright (c) 2026 RAVAPI Soluções. www.ravapi.com
 * Fase 4 — montar o documento impresso a partir do recibo selado
 *
 * Lê só do snapshot gravado no selo. Nunca vai buscar o cadastro atual
 * da empresa ou da contraparte: o recibo tem que sair hoje exatamente
 * como saiu no dia em que foi emitido, mesmo que tudo tenha mudado.
 * ============================================================ */

import QRCode from 'qrcode';
import { emTransacao, type Pool } from '../db/client.ts';
import { exigir, type UsuarioRef } from '../core/identity.ts';
import { NaoEncontrado } from '../core/errors.ts';
import { renderizarRecibo, type ReciboImpresso, type Direcao } from '../render/template.ts';
import type { GeradorPdf } from '../render/pdf.ts';

export class ImpressaoService {
  constructor(
    private readonly pool: Pool,
    private readonly urlVerificacaoBase: string,
    private readonly pdf?: GeradorPdf,
  ) {}

  /**
   * Impressão e leitura NUNCA dependem da licença. Documento de quitação
   * já emitido pertence ao cliente; reter isso por questão comercial
   * seria indefensável.
   */
  async montar(u: UsuarioRef, receiptId: string): Promise<ReciboImpresso> {
    exigir(u, 'ler');

    const dados = await emTransacao(this.pool, async (x) => {
      const { rows } = await x.query(
        `SELECT r.*, c.razao_social, c.nome_fantasia, c.cnpj_cpf, c.tipo_pessoa,
                c.inscricao_municipal, c.telefone, c.email, c.aviso_rodape,
                c.logradouro, c.numero AS c_numero, c.bairro, c.cidade, c.uf, c.cep,
                can.motivo AS cancel_motivo, can.cancelado_em
           FROM recibos.receipts r
           JOIN recibos.companies c ON c.id = r.company_id
           LEFT JOIN recibos.receipt_cancellations can ON can.receipt_id = r.id
          WHERE r.id = $1`,
        [receiptId],
      );
      if (!rows[0]) throw new NaoEncontrado('Recibo');
      return rows[0];
    });

    const url = `${this.urlVerificacaoBase.replace(/\/$/, '')}/${dados.codigo_verificacao}`;
    const qrSvg = await QRCode.toString(url, {
      type: 'svg', margin: 0, errorCorrectionLevel: 'M',
      color: { dark: '#1a2230', light: '#ffffff' },
    });

    const endereco = [dados.logradouro, dados.c_numero, dados.bairro,
      dados.cidade && dados.uf ? `${dados.cidade}/${dados.uf}` : dados.cidade, dados.cep]
      .filter(Boolean).join(' — ');

    return {
      direcao: dados.direcao as Direcao,
      numeroFormatado: dados.numero_formatado,
      codigoVerificacao: dados.codigo_verificacao,
      valorCentavos: Number(dados.valor_centavos),
      valorExtenso: dados.valor_extenso,
      descricao: dados.descricao,
      formaPagamento: dados.forma_pagamento,
      dataPagamento: typeof dados.data_pagamento === 'string'
        ? dados.data_pagamento
        : new Date(dados.data_pagamento).toISOString().slice(0, 10),
      localPagamento: dados.local_pagamento,
      referencia: dados.referencia,
      emitente: {
        nome: dados.razao_social,
        nomeFantasia: dados.nome_fantasia,
        documento: dados.cnpj_cpf,
        tipo: dados.tipo_pessoa,
        inscricaoMunicipal: dados.inscricao_municipal,
        telefone: dados.telefone,
        email: dados.email,
        endereco,
        avisoRodape: dados.aviso_rodape,
      },
      // do snapshot congelado, não do cadastro de hoje
      credor: dados.credor_snapshot,
      devedor: dados.devedor_snapshot,
      signatarioNome: dados.signatario_nome,
      signatarioDocumento: dados.signatario_documento,
      qrSvg,
      urlVerificacao: url,
      cancelamento: dados.cancel_motivo
        ? {
            motivo: dados.cancel_motivo,
            canceladoEm: new Date(dados.cancelado_em).toISOString().slice(0, 10),
          }
        : null,
    };
  }

  async html(u: UsuarioRef, receiptId: string): Promise<string> {
    return renderizarRecibo(await this.montar(u, receiptId));
  }

  async gerarPdf(u: UsuarioRef, receiptId: string): Promise<Buffer> {
    if (!this.pdf) {
      throw new Error('Nenhum gerador de PDF configurado no RecibosModule.');
    }
    const html = await this.html(u, receiptId);
    const buffer = await this.pdf.gerar(html);
    await this.registrar(u, receiptId, 'PDF_GERADO');
    return buffer;
  }

  async registrarImpressao(u: UsuarioRef, receiptId: string) {
    await this.registrar(u, receiptId, 'IMPRESSO', { vias: 2 });
  }

  private async registrar(u: UsuarioRef, receiptId: string, tipo: string, extra?: unknown) {
    await emTransacao(this.pool, async (x) => {
      await x.query(
        `INSERT INTO recibos.receipt_events (tenant_id, receipt_id, tipo, usuario_id, metadata)
         VALUES (current_setting('app.tenant_id')::uuid, $1, $2, $3, $4)`,
        [receiptId, tipo, u.id, extra ? JSON.stringify(extra) : null],
      );
    });
  }
}
