import assert from "node:assert/strict";
import { test } from "node:test";
import {
  cabecalhosDeAutorizacao,
  caminhoCodificado,
  codificar,
  enderecoAssinado,
  HASH_DO_CORPO_VAZIO,
  hashHex,
  textoDaConsulta,
} from "./assinatura-s3.ts";

/* As credenciais e datas dos exemplos da documentação da AWS (S3, SigV4). */
const AWS = { idDaChave: "AKIAIOSFODNN7EXAMPLE", segredo: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", regiao: "us-east-1" };
const DIA_DO_EXEMPLO = new Date("2013-05-24T00:00:00Z");

test("endereço assinado igual ao exemplo da AWS (GET de 24 horas)", () => {
  const url = enderecoAssinado(
    { metodo: "GET", host: "examplebucket.s3.amazonaws.com", caminho: "/test.txt", expiraEmSegundos: 86400 },
    AWS,
    DIA_DO_EXEMPLO,
  );
  const assinatura = new URL(url).searchParams.get("X-Amz-Signature");
  assert.equal(assinatura, "aeeed9bbccd4d02ee5c0109b86d86835f995330da4c265957d157751f604d404");
  assert.ok(url.startsWith("https://examplebucket.s3.amazonaws.com/test.txt?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIOSFODNN7EXAMPLE%2F20130524%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20130524T000000Z&X-Amz-Expires=86400&X-Amz-SignedHeaders=host&X-Amz-Signature="));
});

test("cabeçalhos assinados iguais ao exemplo da AWS (GET com Range)", () => {
  const cab = cabecalhosDeAutorizacao(
    {
      metodo: "GET",
      host: "examplebucket.s3.amazonaws.com",
      caminho: "/test.txt",
      cabecalhos: { Range: "bytes=0-9" },
      hashDoCorpo: HASH_DO_CORPO_VAZIO,
    },
    AWS,
    DIA_DO_EXEMPLO,
  );
  assert.equal(
    cab.authorization,
    "AWS4-HMAC-SHA256 Credential=AKIAIOSFODNN7EXAMPLE/20130524/us-east-1/s3/aws4_request, SignedHeaders=host;range;x-amz-content-sha256;x-amz-date, Signature=f0e8bdb87c964420e857bd35b5d6ed310bd44f0170aba48dd91039c6036bdb41",
  );
  assert.equal(cab["x-amz-date"], "20130524T000000Z");
  assert.equal(cab.range, "bytes=0-9");
  assert.equal("host" in cab, false, "o Host quem põe é o fetch");
});

test("o hash do corpo vazio é o conhecido", () => {
  assert.equal(hashHex(""), HASH_DO_CORPO_VAZIO);
});

test("codificação segue a RFC 3986 e o caminho mantém as barras", () => {
  assert.equal(codificar("a b+c/d~e*f'g(h)i!"), "a%20b%2Bc%2Fd~e%2Af%27g%28h%29i%21");
  assert.equal(caminhoCodificado("/balde/pasta/Aula 1 – ação.mp4"), "/balde/pasta/Aula%201%20%E2%80%93%20a%C3%A7%C3%A3o.mp4");
  assert.equal(caminhoCodificado("sem-barra"), "/sem-barra");
});

test("consulta extra entra na assinatura e no endereço, em ordem", () => {
  const url = enderecoAssinado(
    {
      metodo: "PUT",
      host: "conta.r2.cloudflarestorage.com",
      caminho: "/ktalogu-digitais/loja/arquivo",
      consulta: { uploadId: "abc/def", partNumber: "3" },
      expiraEmSegundos: 3600,
    },
    { idDaChave: "id", segredo: "segredo", regiao: "auto" },
    DIA_DO_EXEMPLO,
  );
  const busca = new URL(url).search;
  assert.ok(busca.indexOf("X-Amz-Algorithm") < busca.indexOf("partNumber=3"), "maiúsculas vêm antes na ordem de bytes");
  assert.ok(busca.includes("uploadId=abc%2Fdef"));
  assert.ok(busca.includes("%2Fauto%2Fs3%2Faws4_request"), "a região do R2 é auto");
});

test("prazo do endereço fica entre 1 segundo e 7 dias", () => {
  const cred = { idDaChave: "id", segredo: "s", regiao: "auto" };
  const longo = new URL(enderecoAssinado({ metodo: "GET", host: "h", caminho: "/a", expiraEmSegundos: 99999999 }, cred));
  assert.equal(longo.searchParams.get("X-Amz-Expires"), "604800");
  const curto = new URL(enderecoAssinado({ metodo: "GET", host: "h", caminho: "/a", expiraEmSegundos: 0 }, cred));
  assert.equal(curto.searchParams.get("X-Amz-Expires"), "1");
});

test("texto da consulta vazio quando não há nada", () => {
  assert.equal(textoDaConsulta(undefined), "");
  assert.equal(textoDaConsulta({ uploads: "" }), "?uploads=");
});
