import { test } from "node:test";
import assert from "node:assert/strict";

import { gerarChave, hashDaChave, pareceChave, temMarca } from "./chave.ts";
import { documentoConhecido, lerDocumento } from "./oauth/documento.ts";
import {
  acessoDaRenovacao,
  codigoComMarca,
  mesmoResumo,
  novoCodigo,
  pareceRenovacao,
  primeiraRenovacao,
  renovacaoSeguinte,
  resumoDaFicha,
} from "./oauth/fichas.ts";
import {
  caminhoDoConector,
  caminhoUsado,
  enderecoDosMetadadosDoRecurso,
  metadadosDoRecurso,
  metadadosDoServidor,
  montarEnderecos,
  origensDoConector,
} from "./oauth/metadados.ts";
import { conferirPedido, enderecoDeVolta, identidadeDoPrograma, recursoAceito, semAcentos } from "./oauth/pedido.ts";
import { desafioDoVerificador, pkceConfere } from "./oauth/pkce.ts";
import { lerDestinoDaVolta, voltaRegistrada } from "./oauth/redirecionamentos.ts";
import { lerRegistro } from "./oauth/registro.ts";

const SEGREDO = "segredo-do-servidor-nos-testes";
const OUTRO_SEGREDO = "outro-segredo";

/* ------------------------------------------------------------------- PKCE */

test("PKCE: o exemplo da norma confere, e só o S256 dele", () => {
  /* O par de exemplo do apêndice B da RFC 7636. */
  const verificador = "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk";
  const desafio = "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM";
  assert.equal(desafioDoVerificador(verificador), desafio);
  assert.equal(pkceConfere(verificador, desafio), true);

  assert.equal(pkceConfere(`${verificador}x`, desafio), false, "outro segredo não passa");
  assert.equal(pkceConfere(verificador, verificador), false, "o método plain (desafio igual ao segredo) não passa");
  assert.equal(pkceConfere("curto", desafio), false);
  assert.equal(pkceConfere(undefined, desafio), false);
  assert.equal(pkceConfere(verificador, "x"), false);
  assert.equal(pkceConfere("a".repeat(129), desafio), false, "segredo longo demais");
  assert.equal(pkceConfere(`${"a".repeat(42)}!`, desafio), false, "caractere fora do permitido");
});

/* -------------------------------------------------------- retornos aceitos */

test("retorno: os endereços dos assistentes conhecidos são aceitos, com o nome da nossa lista", () => {
  const conhecidos: Array<[string, string, string]> = [
    ["https://claude.ai/api/mcp/auth_callback", "Claude", "site"],
    ["https://claude.com/api/mcp/auth_callback", "Claude", "site"],
    ["https://chatgpt.com/connector_platform_oauth_redirect", "ChatGPT", "site"],
    ["https://chatgpt.com/connector/oauth/abc_DEF-123", "ChatGPT", "site"],
    ["https://www.cursor.com/agents/mcp/oauth/callback", "Cursor", "site"],
    ["https://vscode.dev/redirect", "VS Code", "site"],
    ["https://insiders.vscode.dev/redirect", "VS Code", "site"],
    ["cursor://anysphere.cursor-mcp/oauth/callback", "Cursor", "aplicativo"],
    ["claude://claude.ai/mcp-auth-callback/sdk", "Claude", "aplicativo"],
  ];
  for (const [endereco, assistente, tipo] of conhecidos) {
    const destino = lerDestinoDaVolta(endereco);
    assert.ok(destino, `${endereco} é aceito`);
    assert.equal(destino.assistente, assistente);
    assert.equal(destino.tipo, tipo);
    assert.equal(destino.endereco, endereco);
  }
});

test("retorno: o próprio computador é aceito em qualquer porta e caminho, sem nome de assistente", () => {
  for (const endereco of [
    "http://localhost:53124/callback",
    "http://127.0.0.1:33418/",
    "http://127.0.0.1:9000/callback/abc123",
    "http://localhost:6274/oauth/callback/debug",
    "http://[::1]:8787/callback",
    "http://localhost/callback",
  ]) {
    const destino = lerDestinoDaVolta(endereco);
    assert.ok(destino, `${endereco} é aceito`);
    assert.equal(destino.tipo, "computador");
    assert.equal(destino.assistente, null, "quem volta para o computador escolhe o próprio nome, então não ganha o da lista");
  }
});

test("retorno: tudo o que poderia levar o código para as mãos de outro é recusado", () => {
  const recusados: unknown[] = [
    "https://evil.example/callback",
    "http://evil.example/callback",
    /* Parecidos com os de verdade. */
    "https://claude.ai.evil.example/api/mcp/auth_callback",
    "https://evilclaude.ai/api/mcp/auth_callback",
    "https://claude.ai@evil.example/api/mcp/auth_callback",
    "https://evil.example/https://claude.ai/api/mcp/auth_callback",
    "http://localhost.evil.example/callback",
    "http://127.0.0.1.evil.example/callback",
    "http://localhost@evil.example/callback",
    /* O site certo, a página errada. */
    "https://claude.ai/",
    "https://claude.ai/api/mcp/auth_callback/extra",
    "https://claude.ai/api/mcp/auth_callback/../../outra",
    "https://chatgpt.com/connector/oauth/",
    "https://chatgpt.com/connector/oauth/a/b",
    /* O site certo, com desvio. */
    "https://claude.ai:8443/api/mcp/auth_callback",
    "https://claude.ai/api/mcp/auth_callback?next=https://evil.example",
    "https://claude.ai/api/mcp/auth_callback#fragmento",
    "http://claude.ai/api/mcp/auth_callback",
    "cursor://outro.aplicativo/oauth/callback",
    "claude://evil.example/mcp-auth-callback/sdk",
    /* Outros esquemas. */
    "javascript:alert(1)",
    "data:text/html,oi",
    "file:///etc/passwd",
    "vscode://qualquer/coisa",
    "meuapp://callback",
    "https://localhost/callback",
    /* Forma. */
    " https://claude.ai/api/mcp/auth_callback",
    "https://claude.ai/api/mcp/auth_callback ",
    "",
    "não é endereço",
    `http://localhost/${"a".repeat(400)}`,
    null,
    undefined,
    42,
    ["https://claude.ai/api/mcp/auth_callback"],
  ];
  for (const endereco of recusados) {
    assert.equal(lerDestinoDaVolta(endereco), null, `${String(endereco)} é recusado`);
  }
});

test("retorno registrado: exato, e no próprio computador a porta não conta", () => {
  const doClaude = ["https://claude.ai/api/mcp/auth_callback"];
  assert.equal(voltaRegistrada(doClaude, "https://claude.ai/api/mcp/auth_callback"), true);
  assert.equal(voltaRegistrada(doClaude, "https://claude.com/api/mcp/auth_callback"), false, "aceito na lista, mas não registrado por este programa");
  assert.equal(voltaRegistrada(doClaude, "http://localhost:1234/callback"), false);

  /* O Claude Code e o Codex declaram o endereço sem porta e voltam numa porta sorteada. */
  const doComputador = ["http://localhost/callback", "http://127.0.0.1/callback"];
  assert.equal(voltaRegistrada(doComputador, "http://localhost:53124/callback"), true);
  assert.equal(voltaRegistrada(doComputador, "http://127.0.0.1:41000/callback"), true);
  assert.equal(voltaRegistrada(doComputador, "http://localhost:53124/outro"), false, "o caminho conta");
  assert.equal(voltaRegistrada(doComputador, "http://localhost:53124/callback?x=1"), false, "o que vem depois do ? conta");
  assert.equal(voltaRegistrada(["http://localhost:8787/callback"], "http://127.0.0.1:8787/callback"), false, "localhost e 127.0.0.1 não são o mesmo endereço");
  assert.equal(voltaRegistrada(doComputador, "https://evil.example/callback"), false);
  assert.equal(voltaRegistrada([], "http://localhost/callback"), false);
});

/* --------------------------------------------------------------- registro */

test("registro: programa público que volta para o computador fica com o nome que deu", () => {
  const lido = lerRegistro({
    client_name: "Claude Code (ktalogu)",
    redirect_uris: ["http://localhost:53124/callback"],
    grant_types: ["authorization_code", "refresh_token"],
    response_types: ["code"],
    token_endpoint_auth_method: "none",
    application_type: "native",
  });
  assert.ok(lido.ok);
  assert.equal(lido.programa.nome, "Claude Code (ktalogu)");
  assert.equal(lido.programa.metodo, "none");
  assert.equal(lido.programa.conhecido, false);
  assert.deepEqual(lido.programa.redirectUris, ["http://localhost:53124/callback"]);
});

test("registro: para um assistente conhecido vale o nome da nossa lista, não o que ele escreveu", () => {
  const lido = lerRegistro({ client_name: "Banco do Brasil", redirect_uris: ["https://claude.ai/api/mcp/auth_callback", "https://claude.com/api/mcp/auth_callback"] });
  assert.ok(lido.ok);
  assert.equal(lido.programa.nome, "Claude");
  assert.equal(lido.programa.conhecido, true);
  assert.equal(lido.programa.metodo, "none", "quem não diz como se autentica é tratado como público");

  /* Misturou um endereço do Claude com um do próprio computador: não ganha o nome. */
  const misto = lerRegistro({ client_name: "Meu programa", redirect_uris: ["https://claude.ai/api/mcp/auth_callback", "http://localhost:3000/cb"] });
  assert.ok(misto.ok);
  assert.equal(misto.programa.nome, "Meu programa");
  assert.equal(misto.programa.conhecido, false);
});

test("registro: retorno fora da lista é descartado, e sem nenhum aceito o registro é recusado", () => {
  const parcial = lerRegistro({ client_name: "Cursor", redirect_uris: ["https://evil.example/cb", "http://localhost:8787/callback", "cursor://anysphere.cursor-mcp/oauth/callback"] });
  assert.ok(parcial.ok);
  assert.deepEqual(parcial.programa.redirectUris, ["http://localhost:8787/callback", "cursor://anysphere.cursor-mcp/oauth/callback"]);

  const nenhum = lerRegistro({ client_name: "Claude", redirect_uris: ["https://evil.example/cb"] });
  assert.equal(nenhum.ok, false);
  assert.ok(!nenhum.ok && nenhum.erro === "invalid_redirect_uri");

  assert.equal(lerRegistro({ client_name: "x" }).ok, false, "sem redirect_uris");
  assert.equal(lerRegistro({ redirect_uris: [] }).ok, false);
  assert.equal(lerRegistro({ redirect_uris: Array.from({ length: 11 }, (_, i) => `http://localhost:${3000 + i}/cb`) }).ok, false, "endereços demais");
  assert.equal(lerRegistro(null).ok, false);
  assert.equal(lerRegistro([]).ok, false);
  assert.equal(lerRegistro("texto").ok, false);
});

test("registro: método de autenticação, concessões e nome limpo", () => {
  const comSegredo = lerRegistro({ redirect_uris: ["https://claude.ai/api/mcp/auth_callback"], token_endpoint_auth_method: "client_secret_post" });
  assert.ok(comSegredo.ok && comSegredo.programa.metodo === "client_secret_post");

  const desconhecido = lerRegistro({ redirect_uris: ["http://localhost/cb"], token_endpoint_auth_method: "private_key_jwt" });
  assert.ok(!desconhecido.ok && desconhecido.erro === "invalid_client_metadata");

  assert.equal(lerRegistro({ redirect_uris: ["http://localhost/cb"], grant_types: ["client_credentials"] }).ok, false);
  assert.equal(lerRegistro({ redirect_uris: ["http://localhost/cb"], response_types: ["token"] }).ok, false);

  /* Caracteres que não se veem (e os que invertem a direção do texto) saem do nome. */
  const sujo = lerRegistro({ client_name: "Cla‮edu​  Code\u0007", redirect_uris: ["http://localhost/cb"] });
  assert.ok(sujo.ok);
  assert.equal(sujo.programa.nome, "Cla edu Code");

  const semNome = lerRegistro({ redirect_uris: ["http://localhost/cb"] });
  assert.ok(semNome.ok && semNome.programa.nome === "Programa sem nome");

  const longo = lerRegistro({ client_name: "x".repeat(300), redirect_uris: ["http://localhost/cb"] });
  assert.ok(longo.ok && longo.programa.nome.length === 80, "o nome cabe na lista do painel");
});

/* ---------------------------------------------------- identidade publicada */

test("identidade publicada: só os documentos da lista fechada", () => {
  assert.deepEqual(documentoConhecido("https://claude.ai/oauth/mcp-oauth-client-metadata"), { nome: "Claude" });
  assert.deepEqual(documentoConhecido("https://claude.ai/oauth/claude-code-client-metadata"), { nome: "Claude Code" });
  assert.deepEqual(documentoConhecido("https://chatgpt.com/oauth/client.json"), { nome: "ChatGPT" });
  assert.deepEqual(documentoConhecido("https://chatgpt.com/oauth/abc123/client.json"), { nome: "ChatGPT" });
  assert.deepEqual(documentoConhecido("https://chatgpt.com/oauth/codex/client.json"), { nome: "Codex" });
  assert.deepEqual(documentoConhecido("https://chatgpt.com/oauth/codex/abc123/client.json"), { nome: "Codex" });
  assert.deepEqual(documentoConhecido("https://vscode.dev/oauth/client-metadata.json"), { nome: "VS Code" });

  for (const fora of [
    "https://evil.example/oauth/client.json",
    "http://claude.ai/oauth/mcp-oauth-client-metadata",
    "https://claude.ai/oauth/mcp-oauth-client-metadata/",
    "https://claude.ai/oauth/mcp-oauth-client-metadata?x=1",
    "https://claude.ai/oauth/outro",
    "https://claude.ai.evil.example/oauth/mcp-oauth-client-metadata",
    "https://chatgpt.com/oauth/a/b/client.json",
    "https://169.254.169.254/latest/meta-data",
    "http://localhost:3002/oauth/client.json",
    "",
    null,
    undefined,
    7,
  ]) {
    assert.equal(documentoConhecido(fora), null, `${String(fora)} não é aceito`);
  }
});

test("identidade publicada: o documento tem de dizer que é ele mesmo, e os retornos passam pela mesma lista", () => {
  const endereco = "https://claude.ai/oauth/claude-code-client-metadata";
  const bom = lerDocumento(endereco, {
    client_id: endereco,
    client_name: "Qualquer nome",
    redirect_uris: ["http://localhost/callback", "http://127.0.0.1/callback", "https://evil.example/cb"],
    token_endpoint_auth_method: "none",
  });
  assert.ok(bom.ok);
  assert.equal(bom.programa.nome, "Claude Code", "o nome é o da nossa lista");
  assert.equal(bom.programa.metodo, "none");
  assert.deepEqual(bom.programa.redirectUris, ["http://localhost/callback", "http://127.0.0.1/callback"]);

  assert.equal(lerDocumento(endereco, { client_id: "https://claude.ai/oauth/mcp-oauth-client-metadata", redirect_uris: ["http://localhost/callback"] }).ok, false);
  assert.equal(lerDocumento(endereco, { client_id: endereco, redirect_uris: ["https://evil.example/cb"] }).ok, false);
  assert.equal(lerDocumento(endereco, { client_id: endereco }).ok, false);
  assert.equal(lerDocumento(endereco, "texto").ok, false);
  assert.equal(lerDocumento("https://evil.example/client.json", { client_id: "https://evil.example/client.json", redirect_uris: ["http://localhost/cb"] }).ok, false);
});

/* ------------------------------------------------------ pedido de permissão */

const PROGRAMA = { id: "0b0f6f5e-3c1b-4f0e-9a51-2f6a8c1d2e3f", redirectUris: ["https://claude.ai/api/mcp/auth_callback"] };
const ORIGENS = ["https://mcp.ktalogu.com.br", "https://admin.ktalogu.com.br"];
const DESAFIO = "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM";
const PEDIDO_BOM = {
  response_type: "code",
  client_id: PROGRAMA.id,
  redirect_uri: "https://claude.ai/api/mcp/auth_callback",
  code_challenge: DESAFIO,
  code_challenge_method: "S256",
  state: "estado-123",
  scope: "leitura escrita",
  resource: "https://mcp.ktalogu.com.br/mcp",
};

test("identidade do programa: o número do registro ou o endereço de um documento conhecido", () => {
  assert.deepEqual(identidadeDoPrograma(PROGRAMA.id.toUpperCase()), { tipo: "registro", id: PROGRAMA.id });
  assert.deepEqual(identidadeDoPrograma("https://claude.ai/oauth/mcp-oauth-client-metadata"), {
    tipo: "documento",
    endereco: "https://claude.ai/oauth/mcp-oauth-client-metadata",
  });
  for (const fora of ["", "abc", "https://evil.example/client.json", null, undefined, 3, `${PROGRAMA.id}x`]) {
    assert.equal(identidadeDoPrograma(fora), null);
  }
});

test("pedido: o que está certo passa, com o que a tela precisa", () => {
  const lido = conferirPedido(PEDIDO_BOM, PROGRAMA, ORIGENS);
  assert.ok(lido.ok);
  assert.deepEqual(lido.pedido, {
    clientId: PROGRAMA.id,
    redirectUri: "https://claude.ai/api/mcp/auth_callback",
    desafio: DESAFIO,
    estado: "estado-123",
    escoposPedidos: ["leitura", "escrita"],
    recurso: "https://mcp.ktalogu.com.br/mcp",
  });

  /* Parâmetro que não conhecemos não atrapalha. */
  const comExtras = conferirPedido({ ...PEDIDO_BOM, id_token_hint: "x", prompt: "consent", scope: "openid leitura offline_access" }, PROGRAMA, ORIGENS);
  assert.ok(comExtras.ok && comExtras.pedido.escoposPedidos.join(" ") === "leitura");

  const semEscopo = conferirPedido({ ...PEDIDO_BOM, scope: undefined, state: undefined, resource: undefined }, PROGRAMA, ORIGENS);
  assert.ok(semEscopo.ok);
  assert.deepEqual(semEscopo.pedido.escoposPedidos, []);
  assert.equal(semEscopo.pedido.estado, null);
  assert.equal(semEscopo.pedido.recurso, null);
});

test("pedido: retorno que não é do programa aparece na tela e NÃO manda a pessoa a lugar nenhum", () => {
  const trocado = conferirPedido({ ...PEDIDO_BOM, redirect_uri: "https://evil.example/cb" }, PROGRAMA, ORIGENS);
  assert.ok(!trocado.ok && "naTela" in trocado);

  /* Aceito na lista geral, mas não registrado por este programa. */
  const deOutro = conferirPedido({ ...PEDIDO_BOM, redirect_uri: "https://chatgpt.com/connector_platform_oauth_redirect" }, PROGRAMA, ORIGENS);
  assert.ok(!deOutro.ok && "naTela" in deOutro);

  const semRetorno = conferirPedido({ ...PEDIDO_BOM, redirect_uri: undefined }, PROGRAMA, ORIGENS);
  assert.ok(semRetorno.ok && semRetorno.pedido.redirectUri === PROGRAMA.redirectUris[0], "com um retorno só registrado, ele vale");

  const semRetornoEComDois = conferirPedido({ ...PEDIDO_BOM, redirect_uri: undefined }, { ...PROGRAMA, redirectUris: ["http://localhost/a", "http://localhost/b"] }, ORIGENS);
  assert.ok(!semRetornoEComDois.ok && "naTela" in semRetornoEComDois);

  const estadoEnorme = conferirPedido({ ...PEDIDO_BOM, state: "x".repeat(2001) }, PROGRAMA, ORIGENS);
  assert.ok(!estadoEnorme.ok && "naTela" in estadoEnorme);
});

test("pedido: com o retorno conferido, os outros erros voltam para o programa", () => {
  const casos: Array<[Record<string, string | undefined>, string]> = [
    [{ response_type: "token" }, "unsupported_response_type"],
    [{ response_type: undefined }, "unsupported_response_type"],
    [{ code_challenge: undefined }, "invalid_request"],
    [{ code_challenge_method: "plain" }, "invalid_request"],
    [{ code_challenge_method: undefined }, "invalid_request"],
    [{ code_challenge: "curto" }, "invalid_request"],
    [{ resource: "https://outro-servidor.example/mcp" }, "invalid_target"],
    [{ resource: "nao-e-endereco" }, "invalid_target"],
  ];
  for (const [mudanca, erro] of casos) {
    const lido = conferirPedido({ ...PEDIDO_BOM, ...mudanca }, PROGRAMA, ORIGENS);
    assert.ok(!lido.ok && "voltar" in lido, JSON.stringify(mudanca));
    assert.equal(lido.voltar.erro, erro);
    assert.equal(lido.voltar.redirectUri, PEDIDO_BOM.redirect_uri);
    assert.equal(lido.voltar.estado, "estado-123", "o estado volta junto, para o programa reconhecer o pedido");
  }
});

test("resource: só as origens do nosso conector, com ou sem caminho e barra", () => {
  for (const bom of ["https://mcp.ktalogu.com.br", "https://mcp.ktalogu.com.br/", "https://mcp.ktalogu.com.br/mcp", "https://admin.ktalogu.com.br/api/mcp", undefined, null, ""]) {
    assert.equal(recursoAceito(bom, ORIGENS), true, String(bom));
  }
  for (const ruim of ["https://mcp.ktalogu.com.br.evil.example/mcp", "http://mcp.ktalogu.com.br/mcp", "https://evil.example", "texto"]) {
    assert.equal(recursoAceito(ruim, ORIGENS), false, ruim);
  }
});

test("volta ao programa: os campos entram no endereço, e a explicação do erro vai sem acento", () => {
  const comCodigo = new URL(enderecoDeVolta("https://claude.ai/api/mcp/auth_callback", { code: "ktc_abc", state: "e 1&2", iss: "https://mcp.ktalogu.com.br" }));
  assert.equal(comCodigo.origin + comCodigo.pathname, "https://claude.ai/api/mcp/auth_callback");
  assert.equal(comCodigo.searchParams.get("code"), "ktc_abc");
  assert.equal(comCodigo.searchParams.get("state"), "e 1&2", "o estado volta inteiro, mesmo com caracteres especiais");
  assert.equal(comCodigo.searchParams.get("iss"), "https://mcp.ktalogu.com.br");
  assert.equal(comCodigo.searchParams.has("error"), false);

  const comErro = new URL(enderecoDeVolta("http://localhost:5000/callback", { error: "access_denied", error_description: "A pessoa não permitiu a conexão.", state: null }));
  assert.equal(comErro.searchParams.get("error"), "access_denied");
  assert.equal(comErro.searchParams.get("error_description"), "A pessoa nao permitiu a conexao.");
  assert.equal(comErro.searchParams.has("state"), false);

  assert.equal(semAcentos('Código "inválido" \\ ção'), "Codigo 'invalido' ' cao");
});

/* ------------------------------------------------------- cartões de visita */

test("endereços: em produção o conector tem caminho, e o painel é onde fica a tela de permitir", () => {
  const peloConector = montarEnderecos({ pedida: "https://mcp.ktalogu.com.br", painel: "https://mcp.ktalogu.com.br" });
  assert.deepEqual(peloConector, {
    conector: "https://mcp.ktalogu.com.br/mcp",
    emissor: "https://mcp.ktalogu.com.br",
    painel: "https://admin.ktalogu.com.br",
    pedida: "https://mcp.ktalogu.com.br",
  });

  const peloPainel = montarEnderecos({ pedida: "https://admin.ktalogu.com.br", painel: "https://admin.ktalogu.com.br/" });
  assert.equal(peloPainel.conector, "https://mcp.ktalogu.com.br/mcp");
  assert.equal(peloPainel.emissor, "https://mcp.ktalogu.com.br");

  /* A variável do servidor manda, e o endereço seco ganha o caminho. */
  assert.equal(montarEnderecos({ pedida: "https://x", painel: "https://admin.ktalogu.com.br", doAmbiente: "https://mcp.ktalogu.com.br/" }).conector, "https://mcp.ktalogu.com.br/mcp");
  assert.equal(montarEnderecos({ pedida: "https://x", painel: "https://admin.ktalogu.com.br", doAmbiente: "https://conector.exemplo.com/v1" }).conector, "https://conector.exemplo.com/v1");
  assert.equal(montarEnderecos({ pedida: "https://x", painel: "https://admin.ktalogu.com.br", doAmbiente: "isto não é endereço" }).conector, "https://mcp.ktalogu.com.br/mcp");

  /* Na máquina de quem desenvolve é tudo um endereço só. */
  const local = montarEnderecos({ pedida: "http://localhost:3002", painel: "http://localhost:3002" });
  assert.deepEqual(local, { conector: "http://localhost:3002/api/mcp", emissor: "http://localhost:3002", painel: "http://localhost:3002", pedida: "http://localhost:3002" });
  assert.deepEqual(origensDoConector(local), ["http://localhost:3002"]);
  assert.deepEqual(origensDoConector(peloConector), ["https://mcp.ktalogu.com.br", "https://admin.ktalogu.com.br"]);
});

test("cartão do conector: o campo resource é exatamente o endereço que o programa usou", () => {
  const enderecos = montarEnderecos({ pedida: "https://mcp.ktalogu.com.br", painel: "https://admin.ktalogu.com.br" });
  assert.equal(metadadosDoRecurso(enderecos, "/mcp").resource, "https://mcp.ktalogu.com.br/mcp");
  assert.equal(metadadosDoRecurso(enderecos, "").resource, "https://mcp.ktalogu.com.br");
  assert.equal(metadadosDoRecurso(enderecos, "/api/mcp").resource, "https://mcp.ktalogu.com.br/api/mcp");
  assert.deepEqual(metadadosDoRecurso(enderecos, "/mcp").authorization_servers, ["https://mcp.ktalogu.com.br"]);
  assert.equal(enderecoDosMetadadosDoRecurso(enderecos, "/mcp"), "https://mcp.ktalogu.com.br/.well-known/oauth-protected-resource/mcp");
  assert.equal(enderecoDosMetadadosDoRecurso(enderecos, ""), "https://mcp.ktalogu.com.br/.well-known/oauth-protected-resource");

  /* Quem chegou pelo painel recebe o cartão do painel. */
  const peloPainel = montarEnderecos({ pedida: "http://localhost:3002", painel: "http://localhost:3002" });
  assert.equal(metadadosDoRecurso(peloPainel, "/api/mcp").resource, "http://localhost:3002/api/mcp");
  assert.equal(enderecoDosMetadadosDoRecurso(peloPainel, "/api/mcp"), "http://localhost:3002/.well-known/oauth-protected-resource/api/mcp");

  assert.equal(caminhoDoConector(""), "");
  assert.equal(caminhoDoConector("/mcp"), "/mcp");
  assert.equal(caminhoDoConector("/mcp/"), "/mcp");
  assert.equal(caminhoDoConector("/api/mcp"), "/api/mcp");
  assert.equal(caminhoDoConector("/dashboard"), null);
  assert.equal(caminhoDoConector("/mcp/outra"), null);

  const cabecalho = (valor: string | null) => ({ get: () => valor });
  assert.equal(caminhoUsado(cabecalho("/")), "");
  assert.equal(caminhoUsado(cabecalho("/mcp")), "/mcp");
  assert.equal(caminhoUsado(cabecalho("/api/mcp")), "/api/mcp");
  assert.equal(caminhoUsado(cabecalho(null)), "/api/mcp");
  assert.equal(caminhoUsado(cabecalho("/qualquer/coisa")), "/api/mcp", "valor estranho no cabeçalho não vira caminho");
});

test("cartão do servidor de permissão: o mesmo emissor em todo lugar, PKCE S256 e a tela no painel", () => {
  const enderecos = montarEnderecos({ pedida: "https://mcp.ktalogu.com.br", painel: "https://admin.ktalogu.com.br" });
  const cartao = metadadosDoServidor(enderecos);
  assert.equal(cartao.issuer, "https://mcp.ktalogu.com.br");
  assert.equal(cartao.issuer, metadadosDoRecurso(enderecos, "/mcp").authorization_servers[0], "byte a byte igual ao do cartão do conector");
  assert.equal(cartao.authorization_endpoint, "https://admin.ktalogu.com.br/oauth/authorize");
  assert.equal(cartao.token_endpoint, "https://mcp.ktalogu.com.br/oauth/token");
  assert.equal(cartao.registration_endpoint, "https://mcp.ktalogu.com.br/oauth/register");
  assert.deepEqual(cartao.code_challenge_methods_supported, ["S256"]);
  assert.ok(cartao.token_endpoint_auth_methods_supported.includes("none"), "sem `none` o Claude não usa a identidade publicada");
  assert.equal(cartao.client_id_metadata_document_supported, true);
  assert.equal(cartao.authorization_response_iss_parameter_supported, true);
  assert.ok(!JSON.stringify(cartao).includes("null"), "nenhum campo nulo: há programa que tropeça neles");
});

/* ------------------------------------------- marca do servidor e a corrente */

test("marca do servidor: o que saiu daqui tem, o que alguém inventou não tem", () => {
  const comMarca = gerarChave(SEGREDO);
  assert.ok(pareceChave(comMarca.chave), "o formato visível é o mesmo da chave de sempre");
  assert.equal(comMarca.hash, hashDaChave(comMarca.chave));
  assert.equal(temMarca(SEGREDO, comMarca.chave), true);
  assert.equal(temMarca(OUTRO_SEGREDO, comMarca.chave), false, "a marca é do segredo do servidor");

  /* Chave antiga (só sorteio) continua com a forma certa, e sem marca. */
  let semMarca = 0;
  for (let i = 0; i < 50; i += 1) if (!temMarca(SEGREDO, gerarChave().chave)) semMarca += 1;
  assert.equal(semMarca, 50);

  /* Trocar um caractere, ou o prefixo, desfaz a marca. */
  const trocada = `${comMarca.chave.slice(0, 10)}${comMarca.chave[10] === "A" ? "B" : "A"}${comMarca.chave.slice(11)}`;
  assert.equal(temMarca(SEGREDO, trocada), false);
  assert.equal(temMarca(SEGREDO, `ktr_${comMarca.chave.slice(4)}`), false, "um acesso não vira renovação trocando o prefixo");
  for (const lixo of ["", "ktl_", "semprefixo", "ktl_curta", `ktl_${"a".repeat(43)}`, `ktl_${"!".repeat(43)}`]) {
    assert.equal(temMarca(SEGREDO, lixo), false, `"${lixo}"`);
  }
});

test("código: nasce com a marca, e só código com a marca vai ao banco", () => {
  const codigo = novoCodigo(SEGREDO);
  assert.ok(codigo.valor.startsWith("ktc_"));
  assert.equal(codigo.valor.length, 47);
  assert.equal(codigo.hash, resumoDaFicha(codigo.valor));
  assert.equal(codigoComMarca(SEGREDO, codigo.valor), true);
  assert.equal(codigoComMarca(OUTRO_SEGREDO, codigo.valor), false);
  assert.notEqual(novoCodigo(SEGREDO).valor, codigo.valor);

  for (const lixo of [undefined, null, 3, "", "ktc_", `ktc_${"a".repeat(43)}`, gerarChave(SEGREDO).chave, primeiraRenovacao(SEGREDO).valor, `${codigo.valor}x`]) {
    assert.equal(codigoComMarca(SEGREDO, lixo), false, String(lixo));
  }
});

test("corrente da renovação: quem pergunta igual recebe igual, e cada passo é um segredo novo", () => {
  const r0 = primeiraRenovacao(SEGREDO);
  assert.ok(pareceRenovacao(r0.valor));
  assert.equal(temMarca(SEGREDO, r0.valor), true);
  assert.notEqual(primeiraRenovacao(SEGREDO).valor, r0.valor, "a primeira é sorteada");

  /* O acesso que acompanha a renovação sai dela: repetir a conta repete o resultado. */
  const a0 = acessoDaRenovacao(SEGREDO, r0.valor);
  assert.deepEqual(acessoDaRenovacao(SEGREDO, r0.valor), a0);
  assert.ok(pareceChave(a0.valor), "o acesso passa pela mesma conferência da chave");
  assert.equal(temMarca(SEGREDO, a0.valor), true);
  assert.equal(a0.hash, hashDaChave(a0.valor));
  assert.equal(a0.prefixo, a0.valor.slice(0, 12));

  /* A renovação seguinte também sai da atual. */
  const r1 = renovacaoSeguinte(SEGREDO, r0.valor);
  assert.deepEqual(renovacaoSeguinte(SEGREDO, r0.valor), r1);
  assert.ok(pareceRenovacao(r1.valor));
  assert.equal(temMarca(SEGREDO, r1.valor), true);
  const a1 = acessoDaRenovacao(SEGREDO, r1.valor);
  const r2 = renovacaoSeguinte(SEGREDO, r1.valor);

  /* Nada se repete ao longo da corrente. */
  const todos = [r0.valor, r1.valor, r2.valor, a0.valor, a1.valor];
  assert.equal(new Set(todos).size, todos.length);
  assert.notEqual(a0.valor.slice(4), r1.valor.slice(4), "acesso e renovação saem da mesma origem por contas diferentes");

  /* Sem o segredo do servidor, quem tem a renovação não chega ao que vem depois. */
  assert.notEqual(acessoDaRenovacao(OUTRO_SEGREDO, r0.valor).valor, a0.valor);
  assert.notEqual(renovacaoSeguinte(OUTRO_SEGREDO, r0.valor).valor, r1.valor);

  assert.equal(pareceRenovacao(a0.valor), false);
  assert.equal(pareceRenovacao("ktr_curta"), false);
  assert.equal(pareceRenovacao(undefined), false);
});

test("comparação de resumos: igual é igual, e vazio nunca é", () => {
  assert.equal(mesmoResumo("abc", "abc"), true);
  assert.equal(mesmoResumo("abc", "abd"), false);
  assert.equal(mesmoResumo("abc", "abcd"), false);
  assert.equal(mesmoResumo("", ""), false);
  assert.equal(mesmoResumo(null, null), false);
  assert.equal(mesmoResumo(undefined, "abc"), false);
});
