import { test } from "node:test";
import assert from "node:assert/strict";
import { decidirAcessoDaRota, permissaoDaRota, permissoesDaRota } from "./acesso-de-rota.ts";
import { canCompanyRole, type CompanyRole } from "./company-context.ts";

/**
 * Quem entra em cada tela.
 *
 * Testar isso pelo navegador exigiria uma conta de funcionario por cenario, e
 * ainda assim so provaria o caso que fosse aberto na mao. Aqui os quatro
 * estados sao verificados de uma vez — inclusive os dois que so aparecem em
 * condicao ruim de rede, que sao justamente os que ninguem lembra de testar.
 */

test("rota sem exigência continua liberada", () => {
  /* Fechar essas e assunto de outra etapa; hoje elas passam, e mudar isso
     agora tiraria acesso de gente sem ninguem ter pedido. */
  assert.equal(
    decidirAcessoDaRota({
      checagem: "pronto",
      permissaoExigida: null,
      papel: "viewer",
      ajustes: {},
    }),
    "liberado"
  );
});

test("não decide antes de saber — a página não aparece", () => {
  /* O bug que esta etapa corrige: antes, a tela renderizava e so depois vinha
     o redirecionamento, entao o conteudo piscava e as consultas ja tinham
     saido. */
  assert.equal(
    decidirAcessoDaRota({
      checagem: "carregando",
      permissaoExigida: "financial.read",
      papel: null,
      ajustes: {},
    }),
    "aguardando"
  );
});

test("falha ao consultar não tranca quem tem acesso", () => {
  assert.equal(
    decidirAcessoDaRota({
      checagem: "indisponivel",
      permissaoExigida: "financial.read",
      papel: null,
      ajustes: {},
    }),
    "liberado"
  );
});

test("vendedor não entra no financeiro", () => {
  assert.equal(
    decidirAcessoDaRota({
      checagem: "pronto",
      permissaoExigida: "financial.read",
      papel: "sales",
      ajustes: {},
    }),
    "negado"
  );
});

test("financeiro entra no financeiro", () => {
  assert.equal(
    decidirAcessoDaRota({
      checagem: "pronto",
      permissaoExigida: "financial.read",
      papel: "financial",
      ajustes: {},
    }),
    "liberado"
  );
});

test("o ajuste do admin vence o padrão do papel, nos dois sentidos", () => {
  /* E o que preserva as 25 configuracoes manuais que ja existem em producao:
     o que esta gravado sempre ganha. */
  assert.equal(
    decidirAcessoDaRota({
      checagem: "pronto",
      permissaoExigida: "financial.read",
      papel: "sales",
      ajustes: { "financial.read": true },
    }),
    "liberado"
  );
  assert.equal(
    decidirAcessoDaRota({
      checagem: "pronto",
      permissaoExigida: "financial.read",
      papel: "financial",
      ajustes: { "financial.read": false },
    }),
    "negado"
  );
});

test("dono e admin entram em tudo", () => {
  for (const papel of ["owner", "admin"] as const) {
    assert.equal(
      decidirAcessoDaRota({
        checagem: "pronto",
        permissaoExigida: "users.manage",
        papel,
        ajustes: {},
      }),
      "liberado",
      `${papel} deveria entrar`
    );
  }
});

test("gerente não gerencia funcionários sem liberação explícita", () => {
  assert.equal(
    decidirAcessoDaRota({
      checagem: "pronto",
      permissaoExigida: "users.manage",
      papel: "manager",
      ajustes: {},
    }),
    "negado"
  );
});

test("ajuste malformado não vira liberação", () => {
  /* A API grava `permissions` sem validar, entao lixo chega ate aqui. Valor
     que nao seja booleano precisa ser ignorado, e nao interpretado como sim. */
  for (const ajustes of [null, "sim", 42, ["financial.read"], { "financial.read": "true" }]) {
    assert.equal(
      decidirAcessoDaRota({
        checagem: "pronto",
        permissaoExigida: "financial.read",
        papel: "sales",
        ajustes,
      }),
      "negado",
      `ajuste ${JSON.stringify(ajustes)} nao podia liberar`
    );
  }
});

test("rota específica ganha da rota que a contém", () => {
  /* Estes quatro pares são o motivo do teste existir: reordenar as linhas por
     estética faria a rota de dentro nunca ser alcançada, e o módulo passaria a
     responder pela chave do módulo de fora — sem erro, sem aviso. */
  assert.equal(permissaoDaRota("/dashboard/catalog/marketing"), "marketing.read");
  /* MUDANÇA DELIBERADA: a raiz do Catálogo é a tela da loja. Herda de
     `catalog.read`, então ninguém perdeu acesso. */
  assert.equal(permissaoDaRota("/dashboard/catalog"), "catalog.store.read");

  assert.equal(permissaoDaRota("/dashboard/production/materials"), "materials.read");
  assert.equal(permissaoDaRota("/dashboard/production"), "production.board.read");

  assert.equal(permissaoDaRota("/dashboard/financial/commissions"), "commissions.read");
  /* MUDANÇA DELIBERADA: a raiz do Financeiro é a Visão geral, e passou a
     perguntar pela chave dela. Ninguém perdeu acesso — `financial.overview.read`
     herda de `financial.read`, que era quem respondia antes. */
  assert.equal(permissaoDaRota("/dashboard/financial"), "financial.overview.read");

  assert.equal(permissaoDaRota("/dashboard/company/users"), "users.manage");
});

test("cada tela do Financeiro pergunta pela própria chave", () => {
  /* O motivo desta separação: o dono precisa deixar o vendedor conferir se o
     cliente pagou sem abrir para ele as contas da empresa. Antes era tudo ou
     nada, porque as onze telas respondiam pela mesma chave. */
  assert.equal(permissaoDaRota("/dashboard/financial/payables"), "financial.payables.read");
  assert.equal(permissaoDaRota("/dashboard/financial/payables?tab=custos-fixos"), "financial.payables.read");
  assert.equal(permissaoDaRota("/dashboard/financial/receivables"), "financial.receivables.read");
  assert.equal(permissaoDaRota("/dashboard/financial/payments"), "financial.payments.read");
  assert.equal(permissaoDaRota("/dashboard/financial/payment-links"), "financial.links.read");
  assert.equal(permissaoDaRota("/dashboard/financial/cash-flow"), "financial.cashflow.read");
  assert.equal(permissaoDaRota("/dashboard/financial/bank-accounts"), "financial.bank.read");
  assert.equal(permissaoDaRota("/dashboard/financial/dre"), "financial.dre.read");
  assert.equal(permissaoDaRota("/dashboard/financial/dre-analytic"), "financial.dre.read");

  /* E Comissões continua sendo módulo próprio, antes de todas elas. */
  assert.equal(permissaoDaRota("/dashboard/financial/commissions"), "commissions.read");
});

test("quem tinha o Financeiro continua entrando em todas as telas", () => {
  /* A prova de que a separação não tirou acesso de ninguém: o papel financeiro,
     sem nenhum ajuste configurado, continua entrando nas onze telas. */
  const telas = [
    "/dashboard/financial",
    "/dashboard/financial/payables",
    "/dashboard/financial/receivables",
    "/dashboard/financial/payments",
    "/dashboard/financial/payment-links",
    "/dashboard/financial/cash-flow",
    "/dashboard/financial/bank-accounts",
    "/dashboard/financial/dre",
    "/dashboard/financial/dre-analytic",
    "/dashboard/financial/reports",
  ];
  for (const tela of telas) {
    assert.equal(
      decidirAcessoDaRota({
        checagem: "pronto",
        permissaoExigida: permissaoDaRota(tela),
        papel: "financial",
        ajustes: {},
      }),
      "liberado",
      `${tela} fechou para quem sempre pôde entrar`
    );
  }
});

test("fechar uma tela do Financeiro não fecha as outras", () => {
  /* O caso do vendedor: entra em contas a receber, não entra em contas a pagar. */
  const ajustes = { "financial.payables.read": false };
  assert.equal(
    decidirAcessoDaRota({
      checagem: "pronto",
      permissaoExigida: permissaoDaRota("/dashboard/financial/payables"),
      papel: "financial",
      ajustes,
    }),
    "negado"
  );
  assert.equal(
    decidirAcessoDaRota({
      checagem: "pronto",
      permissaoExigida: permissaoDaRota("/dashboard/financial/receivables"),
      papel: "financial",
      ajustes,
    }),
    "liberado"
  );
});

test("uma tela sozinha pode ser aberta para quem não tem o módulo", () => {
  /* O outro lado: o vendedor não tem Financeiro nenhum, e ganha só a tela de
     recebimentos. O ajuste da ação vence, como sempre venceu. */
  const ajustes = { "financial.receivables.read": true };
  assert.equal(
    decidirAcessoDaRota({
      checagem: "pronto",
      permissaoExigida: permissaoDaRota("/dashboard/financial/receivables"),
      papel: "sales",
      ajustes,
    }),
    "liberado"
  );
  assert.equal(
    decidirAcessoDaRota({
      checagem: "pronto",
      permissaoExigida: permissaoDaRota("/dashboard/financial/payables"),
      papel: "sales",
      ajustes,
    }),
    "negado"
  );
});

test("cada módulo do menu pergunta pela própria chave", () => {
  /* Antes destes mapeamentos, Fornecedores perguntava "pode configurar a
     empresa?" e Relatórios perguntava "pode ver o financeiro?". */
  /* A raiz e a lista: as duas rotas renderizam o mesmo componente. */
  assert.equal(permissaoDaRota("/dashboard/fornecedores"), "suppliers.list.read");
  assert.equal(permissaoDaRota("/dashboard/fornecedores/meus-fornecedores"), "suppliers.list.read");
  assert.equal(permissaoDaRota("/dashboard/reports"), "reports.read");
  assert.equal(permissaoDaRota("/dashboard/whatsapp"), "whatsapp.manage");
  assert.equal(permissaoDaRota("/dashboard/agenda"), "agenda.board.read");
  /* O Chats abre em aba propria, fora de /dashboard, e mesmo assim responde
     pela propria chave — o menu e o painel do atendimento dependem disto. */
  assert.equal(permissaoDaRota("/chats"), "chats.read");
  assert.equal(permissaoDaRota("/chats?conversa=abc"), "chats.read");
});

test("âncora e query não mudam a permissão da rota", () => {
  assert.equal(permissaoDaRota("/dashboard/reports?de=2026-01-01"), "reports.read");
  assert.equal(permissaoDaRota("/dashboard/catalog/marketing#campanhas"), "marketing.read");
});

test("rota sem exigência declarada segue aberta", () => {
  assert.equal(permissaoDaRota("/dashboard/qualquer-coisa-nova"), null);
});

test("configuração de custos da venda é financeira, não da empresa", () => {
  /* Vive sob /settings por historia, mas quem cuida do financeiro precisa
     entrar sem depender de configurar a empresa inteira. A ordem das linhas e
     o que faz isso valer. */
  assert.equal(permissaoDaRota("/dashboard/precificacao/custos-da-venda"), "pricing.taxes.read");
  assert.equal(permissaoDaRota("/dashboard/settings"), "company.data.read");
  assert.equal(permissaoDaRota("/dashboard/settings/shipping"), "company.shipping.read");
});


test("cada tela dos demais módulos pergunta pela própria chave", () => {
  /* O mesmo que o Financeiro ganhou, agora nos outros oito módulos. A ordem
     importa: a tela mais específica tem de vir antes da raiz do módulo. */
  assert.equal(permissaoDaRota("/dashboard/catalog/marketing"), "marketing.read");
  assert.equal(permissaoDaRota("/dashboard/catalog/dashboard"), "catalog.dashboard.read");
  assert.equal(permissaoDaRota("/dashboard/catalog/products"), "catalog.products.read");
  /* O catálogo em PDF responde pela chave dos produtos da loja, não pela da
     personalização (a regra genérica de /dashboard/catalog pegaria). */
  assert.equal(permissaoDaRota("/dashboard/catalog/pdf"), "catalog.products.read");
  assert.equal(permissaoDaRota("/dashboard/catalog/orders"), "catalog.orders.read");
  assert.equal(permissaoDaRota("/dashboard/catalog/banners"), "catalog.banners.read");
  assert.equal(permissaoDaRota("/dashboard/catalog/stories"), "catalog.stories.read");
  assert.equal(permissaoDaRota("/dashboard/catalog/reviews"), "catalog.reviews.read");

  assert.equal(permissaoDaRota("/dashboard/precificacao"), "pricing.config.read");
  assert.equal(permissaoDaRota("/dashboard/precificacao/nova"), "pricing.wizard.read");
  assert.equal(permissaoDaRota("/dashboard/precificacao/saude"), "pricing.health.read");

  assert.equal(permissaoDaRota("/dashboard/products"), "products.catalog.read");
  assert.equal(permissaoDaRota("/dashboard/products/estoque"), "products.stock.read");
  assert.equal(permissaoDaRota("/dashboard/products/new"), "products.catalog.write");

  assert.equal(permissaoDaRota("/dashboard/production"), "production.board.read");
  assert.equal(permissaoDaRota("/dashboard/production/operations"), "production.costs.read");
  /* Insumos continua respondendo por materials, antes da raiz de produção. */
  assert.equal(permissaoDaRota("/dashboard/production/materials"), "materials.read");

  assert.equal(permissaoDaRota("/dashboard/clients"), "clients.list.read");
  assert.equal(permissaoDaRota("/dashboard/clients/ranking"), "clients.ranking.read");

  assert.equal(permissaoDaRota("/dashboard/agenda"), "agenda.board.read");
  assert.equal(permissaoDaRota("/dashboard/agenda/relatorio"), "agenda.report.read");

  assert.equal(permissaoDaRota("/dashboard/settings"), "company.data.read");
  assert.equal(permissaoDaRota("/dashboard/settings/shipping"), "company.shipping.read");
  /* E os funcionários continuam antes de qualquer coisa de empresa. */
  assert.equal(permissaoDaRota("/dashboard/company/users"), "users.manage");
});

test("a própria senha não é permissão da empresa", () => {
  /* Exigir `company.settings` para trocar a própria senha trancava o
     funcionário fora da conta dele — e nem o item aparecia no menu. */
  assert.equal(permissaoDaRota("/dashboard/settings/password"), null);
  assert.equal(
    decidirAcessoDaRota({
      checagem: "pronto",
      permissaoExigida: permissaoDaRota("/dashboard/settings/password"),
      papel: "viewer",
      ajustes: { company: "nenhum" },
    }),
    "liberado"
  );
});

test("ninguém perdeu acesso quando as telas ganharam chave própria", () => {
  /* A garantia, módulo a módulo: cada papel que entrava numa tela continua
     entrando, sem nenhum ajuste configurado. */
  const casos: Array<[CompanyRole, string]> = [
    ["sales", "/dashboard/clients"],
    ["sales", "/dashboard/clients/ranking"],
    ["sales", "/dashboard/products"],
    ["sales", "/dashboard/products/estoque"],
    ["production", "/dashboard/production"],
    ["production", "/dashboard/production/operations"],
    ["manager", "/dashboard/catalog"],
    ["manager", "/dashboard/catalog/products"],
    ["manager", "/dashboard/settings"],
    ["manager", "/dashboard/settings/shipping"],
    ["viewer", "/dashboard/agenda"],
  ];
  for (const [papel, rota] of casos) {
    const antes = canCompanyRole(papel, {}, permissaoDaRotaAntiga(rota));
    const agora = canCompanyRole(papel, {}, permissaoDaRota(rota)!);
    assert.equal(agora, antes, `${rota} mudou para ${papel}`);
  }
});

/* A chave que respondia por cada tela antes da separação. */
function permissaoDaRotaAntiga(rota: string) {
  if (rota.startsWith("/dashboard/catalog")) return "catalog.read" as const;
  if (rota.startsWith("/dashboard/precificacao")) return "pricing.read" as const;
  if (rota.startsWith("/dashboard/products")) return "products.read" as const;
  if (rota.startsWith("/dashboard/fornecedores")) return "suppliers.read" as const;
  if (rota.startsWith("/dashboard/production")) return "production.read" as const;
  if (rota.startsWith("/dashboard/clients")) return "clients.read" as const;
  if (rota.startsWith("/dashboard/agenda")) return "agenda.read" as const;
  if (rota.startsWith("/dashboard/settings")) return "company.settings" as const;
  throw new Error(`sem regra antiga para ${rota}`);
}

test("tela de dois assuntos abre com qualquer uma das duas chaves", () => {
  /* O Estoque tem a aba de produtos prontos e a de insumos, cada uma com dono
     próprio. Irmãs não herdam entre si, então quem recebeu só insumos batia
     numa porta fechada — e batia justamente porque a tela velha de insumos
     hoje só redireciona para lá. */
  assert.deepEqual(permissoesDaRota("/dashboard/products/estoque"), [
    "products.stock.read",
    "materials.read",
  ]);

  const soInsumos = { "materials.read": true };
  assert.equal(
    decidirAcessoDaRota({
      checagem: "pronto",
      permissaoExigida: permissoesDaRota("/dashboard/products/estoque"),
      papel: "viewer",
      ajustes: soInsumos,
    }),
    "liberado"
  );

  /* E continua fechada para quem não tem nenhuma das duas. */
  assert.equal(
    decidirAcessoDaRota({
      checagem: "pronto",
      permissaoExigida: permissoesDaRota("/dashboard/products/estoque"),
      papel: "viewer",
      ajustes: { "products.read": false, "materials.read": false },
    }),
    "negado"
  );
});

test("toda tela continua com uma chave principal, e a lista começa por ela", () => {
  /* `permissoesDaRota` não pode virar a porta dos fundos: ela é a chave da
     tela mais as irmãs declaradas, nessa ordem. */
  for (const rota of [
    "/dashboard/financial/cash-flow",
    "/dashboard/clients/ranking",
    "/dashboard/catalog/banners",
    "/dashboard/products/estoque",
  ]) {
    const lista = permissoesDaRota(rota);
    assert.equal(lista[0], permissaoDaRota(rota), `${rota} mudou de chave principal`);
  }
  assert.deepEqual(permissoesDaRota("/dashboard/settings/password"), []);
});

test("rota que só redireciona pede a chave do destino", () => {
  /* Custos fixos virou uma aba de Contas a pagar. A rota antiga sobreviveu
     como redirecionamento, e por um tempo pediu a chave da Precificação:
     a pessoa passava pela porta e batia na segunda. */
  assert.equal(permissaoDaRota("/dashboard/precificacao/custos-fixos"), "financial.fixedcosts.read");
  assert.deepEqual(permissoesDaRota("/dashboard/financial/payables"), [
    "financial.payables.read",
    "financial.fixedcosts.read",
  ]);
  /* E a Precificação em si não mudou de dono. */
  assert.equal(permissaoDaRota("/dashboard/precificacao"), "pricing.config.read");
});

test("começar um orçamento é escrever um orçamento", () => {
  assert.equal(permissaoDaRota("/dashboard/quotes/new"), "quotes.write");
  assert.equal(permissaoDaRota("/dashboard/quotes"), "quotes.read");
  assert.equal(permissaoDaRota("/dashboard/quotes/abc"), "quotes.read");
});

test("produtos digitais: cada tela pergunta pela própria chave, e ninguém entra sem receber", () => {
  assert.equal(permissaoDaRota("/dashboard/digitais"), "digital.overview.read");
  assert.equal(permissaoDaRota("/dashboard/digitais/produtos"), "digital.products.read");
  assert.equal(permissaoDaRota("/dashboard/digitais/produtos/novo"), "digital.products.read");
  assert.equal(permissaoDaRota("/dashboard/digitais/cursos/abc"), "digital.products.read");
  assert.equal(permissaoDaRota("/dashboard/digitais/lives"), "digital.products.read");
  assert.equal(permissaoDaRota("/dashboard/digitais/lives/nova"), "digital.products.read");
  assert.equal(permissaoDaRota("/dashboard/digitais/alunos"), "digital.students.read");
  assert.equal(permissaoDaRota("/dashboard/digitais/comentarios"), "digital.comments.read");
  assert.equal(permissaoDaRota("/dashboard/digitais/entregas"), "digital.deliveries.read");
  assert.equal(permissaoDaRota("/dashboard/digitais/arquivos"), "digital.files.read");
  assert.equal(permissaoDaRota("/dashboard/digitais/configuracoes"), "digital.settings.read");
  assert.equal(permissaoDaRota("/dashboard/digitais/qualquer"), "digital.read");
  assert.equal(permissaoDaRota("/dashboard/digitaisx"), null, "prefixo parecido não é a área");
  const papeis: CompanyRole[] = ["manager", "financial", "production", "sales", "viewer"];
  for (const papel of papeis) {
    assert.equal(canCompanyRole(papel, {}, "digital.overview.read"), false, `${papel} não entra sem liberação`);
    assert.equal(canCompanyRole(papel, { digital: "ver" }, "digital.overview.read"), true, `${papel} entra com o nível do módulo`);
    assert.equal(canCompanyRole(papel, { digital: "ver" }, "digital.products.write"), false, `${papel} só vê com nível ver`);
  }
  assert.equal(canCompanyRole("admin", {}, "digital.settings.write"), true);
});
