import assert from "node:assert/strict";
import { describe, it } from "node:test";
import { inflateRawSync } from "node:zlib";

import { montarZip, nomeParaZip } from "./servidor/zip.ts";

/** Lê o pacote do jeito que um descompactador lê: pelo diretório central. */
function abrirZip(bytes: Uint8Array) {
  const b = Buffer.from(bytes);
  const fim = b.length - 22;
  assert.equal(b.readUInt32LE(fim), 0x06054b50, "fim do zip");
  const quantidade = b.readUInt16LE(fim + 10);
  const tamanhoDoDiretorio = b.readUInt32LE(fim + 12);
  const inicioDoDiretorio = b.readUInt32LE(fim + 16);
  assert.equal(inicioDoDiretorio + tamanhoDoDiretorio, fim, "diretório central no lugar certo");

  const arquivos: Array<{ nome: string; conteudo: string }> = [];
  let p = inicioDoDiretorio;
  for (let i = 0; i < quantidade; i += 1) {
    assert.equal(b.readUInt32LE(p), 0x02014b50, "entrada do diretório");
    const tamanhoDoNome = b.readUInt16LE(p + 28);
    const deslocamento = b.readUInt32LE(p + 42);
    const nome = b.subarray(p + 46, p + 46 + tamanhoDoNome).toString("utf8");
    assert.equal(b.readUInt32LE(deslocamento), 0x04034b50, "cabeçalho local");
    const comprimido = b.readUInt32LE(deslocamento + 18);
    const nomeLocal = b.readUInt16LE(deslocamento + 26);
    const extraLocal = b.readUInt16LE(deslocamento + 28);
    const inicio = deslocamento + 30 + nomeLocal + extraLocal;
    const conteudo = inflateRawSync(b.subarray(inicio, inicio + comprimido)).toString("utf8");
    arquivos.push({ nome, conteudo });
    p += 46 + tamanhoDoNome + b.readUInt16LE(p + 30) + b.readUInt16LE(p + 32);
  }
  return arquivos;
}

describe("pacote de XMLs do contador", () => {
  it("guarda cada arquivo com o nome e o conteúdo certos", () => {
    const xml = '<?xml version="1.0"?><nfeProc><NFe>nota</NFe></nfeProc>';
    const pacote = montarZip([
      { nome: "nota-de-produto/10-2606.xml", conteudo: new TextEncoder().encode(xml) },
      { nome: "cupom-fiscal/3-2607.xml", conteudo: new TextEncoder().encode("<nfeProc>cupom</nfeProc>") },
    ]);
    const lidos = abrirZip(pacote);
    assert.equal(lidos.length, 2);
    assert.equal(lidos[0].nome, "nota-de-produto/10-2606.xml");
    assert.equal(lidos[0].conteudo, xml);
    assert.equal(lidos[1].conteudo, "<nfeProc>cupom</nfeProc>");
  });

  it("não repete nome nem deixa caractere que o Windows recusa", () => {
    const pacote = montarZip([
      { nome: "nota.xml", conteudo: new TextEncoder().encode("um") },
      { nome: "nota.xml", conteudo: new TextEncoder().encode("dois") },
    ]);
    const lidos = abrirZip(pacote);
    assert.deepEqual(lidos.map((a) => a.nome), ["nota.xml", "nota-2.xml"]);
    assert.equal(nomeParaZip('nota:"1"?.xml'), "nota--1--.xml");
    assert.equal(nomeParaZip("/../segredo.xml"), "segredo.xml", "não deixa o arquivo escapar da pasta");
    assert.equal(nomeParaZip(""), "arquivo");
  });

  it("arquivo grande e repetitivo encolhe de verdade", () => {
    const conteudo = new TextEncoder().encode("<item>nota fiscal</item>".repeat(500));
    const pacote = montarZip([{ nome: "grande.xml", conteudo }]);
    assert.ok(pacote.length < conteudo.length / 2, "o pacote tem de ser bem menor que o original");
    assert.equal(abrirZip(pacote)[0].conteudo.length, conteudo.length);
  });
});
