import { describe, expect, it } from "vitest";

import { safeNext } from "@/lib/auth/safe-next";

/**
 * Open redirect no `next` do login/MFA (relatório de segurança da comunidade).
 *
 * A tabela abaixo é allowlist: o que não for caminho relativo à raiz vira o
 * fallback. Os casos "parece interno mas não é" são o miolo — quem escreve uma
 * denylist acerta `https://` e erra `//`, que é a forma que de fato aparece em
 * exploração.
 */
describe("safeNext — só caminho interno passa", () => {
  const FALLBACK = "/app/inbox";

  it.each([
    ["undefined", undefined],
    ["vazio", ""],
    ["absoluta https", "https://evil.example/phish"],
    ["absoluta http", "http://evil.example"],
    ["protocol-relative", "//evil.example"],
    ["protocol-relative com barra invertida", "/\\evil.example"],
    ["barra invertida no meio", "/app\\..\\evil"],
    ["javascript:", "javascript:alert(1)"],
    ["data:", "data:text/html,<script>alert(1)</script>"],
    ["relativa sem raiz", "app/inbox"],
    ["com espaço", "/app /inbox"],
    ["com quebra de linha (header smuggling)", "/app\ninbox"],
    ["com CR", "/app\rinbox"],
    ["com NUL", "/app inbox"],
  ])("recusa %s", (_rotulo, entrada) => {
    expect(safeNext(entrada, FALLBACK)).toBe(FALLBACK);
  });

  it.each([
    ["raiz", "/"],
    ["caminho simples", "/app/inbox"],
    ["caminho com hífen", "/app/meus-leads"],
    ["com querystring", "/app/leads?stage=novo"],
    ["com fragmento", "/app/leads#topo"],
    ["com percent-encoding", "/app/leads%2Fdetalhe"],
  ])("aceita %s", (_rotulo, entrada) => {
    expect(safeNext(entrada, FALLBACK)).toBe(entrada);
  });

  it("o fallback é devolvido tal como veio (o chamador escolhe o destino)", () => {
    expect(safeNext("https://evil.example", "/onboarding/mfa")).toBe("/onboarding/mfa");
  });
});
