/* Extensao explicita: e o que o runner de teste do Node exige para resolver o
   modulo. O bundler do Next aceita as duas formas. */
import { canCompanyRole, type CompanyPermission, type CompanyRole } from "./company-context.ts";

/**
 * Decide se a página pode aparecer.
 *
 * Mora fora do layout por dois motivos: e a regra que decide se alguem ve uma
 * tela inteira, e regra assim precisa ser lida sem caçar dentro de um
 * componente de 2500 linhas; e porque testar isso pelo navegador exigiria uma
 * segunda conta so para conferir cada caso.
 *
 * Os quatro estados sao diferentes de proposito. "Ainda nao sei" nao pode
 * abrir a pagina, e "nao consegui descobrir" nao pode fechar: uma oscilacao de
 * rede prendendo quem tem acesso e pior que o problema que a checagem resolve —
 * e a API continua barrando o que precisa ser barrado.
 */

const semAncora = (href: string) => href.split("#")[0].split("?")[0];

/**
 * Qual permissão cada tela exige.
 *
 * A ordem é a regra e não pode ser reordenada por estética: ganha o primeiro
 * `startsWith` que casar, então a rota mais específica precisa vir antes da que
 * a contém. `/dashboard/catalog/marketing` depois de `/dashboard/catalog` nunca
 * seria alcançada, e o módulo inteiro passaria a responder pela chave errada —
 * em silêncio, porque a tela continuaria abrindo para quase todo mundo. O teste
 * ao lado existe para essa linha específica.
 *
 * `null` = tela sem exigência declarada, que segue aberta.
 */
export function permissaoDaRota(path: string): CompanyPermission | null {
  const rota = semAncora(path);

  if (rota === "/dashboard" || rota.startsWith("/dashboard/overview")) return "dashboard.read";
  /* A senha e da pessoa, nao da empresa. Exigir `company.settings` para
     trocar a propria senha trancava o funcionario fora da conta dele. */
  if (rota === "/dashboard/settings/password") return null;
  if (rota.startsWith("/dashboard/company/users")) return "users.manage";
  if (rota.startsWith("/dashboard/assinatura")) return "subscription.manage";
  /* Mora sob /settings por historia, mas o assunto e financeiro: imposto, taxa
     de recebimento, perda. Quem cuida do financeiro precisa entrar sem depender
     de configurar a empresa inteira. Antes da regra generica de /settings. */
  /* Esta rota nao e mais tela: so redireciona para a aba de custos fixos,
     dentro de Contas a pagar. Entao quem manda e a chave de la — pedir a da
     Precificacao levava a pessoa ate uma porta que se fecha na cara dela. */
  if (rota.startsWith("/dashboard/precificacao/custos-fixos")) return "financial.fixedcosts.read";
  /* As telas de pricing, da mais especifica para a raiz. */
  if (rota === "/dashboard/precificacao/custos-da-venda" || rota.startsWith("/dashboard/precificacao/custos-da-venda/")) return "pricing.taxes.read";
  if (rota === "/dashboard/precificacao/saude" || rota.startsWith("/dashboard/precificacao/saude/")) return "pricing.health.read";
  if (rota === "/dashboard/precificacao/nova" || rota.startsWith("/dashboard/precificacao/nova/")) return "pricing.wizard.read";
  if (rota === "/dashboard/precificacao" || rota.startsWith("/dashboard/precificacao/")) return "pricing.config.read";
  if (rota.startsWith("/dashboard/precificacao")) return "pricing.read";

  /* As telas de company, da mais especifica para a raiz. */
  if (rota === "/dashboard/settings/shipping" || rota.startsWith("/dashboard/settings/shipping/")) return "company.shipping.read";
  /* As regras automáticas só quem configura a empresa vê (a API também exige). */
  if (rota === "/dashboard/settings/regras" || rota.startsWith("/dashboard/settings/regras/")) return "company.settings";
  if (rota === "/dashboard/settings" || rota.startsWith("/dashboard/settings/")) return "company.data.read";
  if (rota.startsWith("/dashboard/settings")) return "company.settings";
  if (rota.startsWith("/dashboard/whatsapp")) return "whatsapp.manage";
  /* As telas de suppliers, da mais especifica para a raiz. */
  if (rota === "/dashboard/fornecedores/meus-fornecedores" || rota.startsWith("/dashboard/fornecedores/meus-fornecedores/")) return "suppliers.list.read";
  if (rota === "/dashboard/fornecedores/xbz" || rota.startsWith("/dashboard/fornecedores/xbz/")) return "suppliers.xbz.read";
  /* A raiz de Fornecedores e a lista: as duas rotas renderizam o mesmo
     componente. Uma tela, uma chave. */
  if (rota === "/dashboard/fornecedores" || rota.startsWith("/dashboard/fornecedores/")) return "suppliers.list.read";
  if (rota.startsWith("/dashboard/fornecedores")) return "suppliers.read";
  if (rota.startsWith("/dashboard/my-commissions")) return "commissions.self";
  if (rota.startsWith("/dashboard/financial/commissions")) return "commissions.read";
  /* UMA CHAVE POR TELA DO FINANCEIRO.
     As onze telas respondiam por `financial.read`: abrir o modulo abria todas,
     e fechar fechava todas. Agora cada uma tem a propria chave, e toda chave
     dessas HERDA de `financial.read` — quem tem o modulo hoje continua com as
     onze, e so muda o de quem for configurado tela a tela.
     A ordem aqui e a regra: mais especifica primeiro, senao a linha generica
     do fim engole todas. */
  if (rota.startsWith("/dashboard/financial/payables")) return "financial.payables.read";
  if (rota.startsWith("/dashboard/financial/receivables")) return "financial.receivables.read";
  if (rota.startsWith("/dashboard/financial/payment-links")) return "financial.links.read";
  if (rota.startsWith("/dashboard/financial/payments")) return "financial.payments.read";
  if (rota.startsWith("/dashboard/financial/cash-flow")) return "financial.cashflow.read";
  if (rota.startsWith("/dashboard/financial/bank-accounts")) return "financial.bank.read";
  if (rota === "/dashboard/design" || rota.startsWith("/dashboard/design/")) return "catalog.banners.read";
  if (rota.startsWith("/dashboard/financial/dre")) return "financial.dre.read";
  if (rota.startsWith("/dashboard/financial/overview")) return "financial.overview.read";
  if (rota === "/dashboard/financial") return "financial.overview.read";
  if (rota.startsWith("/dashboard/financial")) return "financial.read";
  if (rota.startsWith("/dashboard/reports")) return "reports.read";
  if (rota === "/dashboard/paineis" || rota.startsWith("/dashboard/paineis/")) return "reports.read";
  if (rota.startsWith("/dashboard/catalog/marketing")) return "marketing.read";
  /* As telas de catalog, da mais especifica para a raiz. */
  if (rota === "/dashboard/catalog/dashboard" || rota.startsWith("/dashboard/catalog/dashboard/")) return "catalog.dashboard.read";
  if (rota === "/dashboard/catalog/products" || rota.startsWith("/dashboard/catalog/products/")) return "catalog.products.read";
  /* O catálogo em PDF é a vitrine impressa: quem vê os produtos da loja gera. */
  if (rota === "/dashboard/catalog/pdf" || rota.startsWith("/dashboard/catalog/pdf/")) return "catalog.products.read";
  if (rota === "/dashboard/catalog/banners" || rota.startsWith("/dashboard/catalog/banners/")) return "catalog.banners.read";
  /* KtaloguDesign (o editor de artes): quem vê os banners da loja cria e baixa artes. A rota antiga dentro do catálogo só leva para lá. */
  if (rota === "/dashboard/catalog/artes" || rota.startsWith("/dashboard/catalog/artes/")) return "catalog.banners.read";
  if (rota === "/dashboard/catalog/stories" || rota.startsWith("/dashboard/catalog/stories/")) return "catalog.stories.read";
  if (rota === "/dashboard/catalog/reviews" || rota.startsWith("/dashboard/catalog/reviews/")) return "catalog.reviews.read";
  if (rota === "/dashboard/catalog/orders" || rota.startsWith("/dashboard/catalog/orders/")) return "catalog.orders.read";
  if (rota === "/dashboard/catalog" || rota.startsWith("/dashboard/catalog/")) return "catalog.store.read";
  if (rota.startsWith("/dashboard/catalog")) return "catalog.read";
  if (rota.startsWith("/dashboard/categories")) return "categories.read";
  /* As telas de products, da mais especifica para a raiz. */
  if (rota === "/dashboard/products/estoque" || rota.startsWith("/dashboard/products/estoque/")) return "products.stock.read";
  if (rota === "/dashboard/products/new" || rota.startsWith("/dashboard/products/new/")) return "products.catalog.write";
  if (rota === "/dashboard/products" || rota.startsWith("/dashboard/products/")) return "products.catalog.read";
  if (rota.startsWith("/dashboard/products")) return "products.read";
  /* As telas de clients, da mais especifica para a raiz. */
  if (rota === "/dashboard/clients/ranking" || rota.startsWith("/dashboard/clients/ranking/")) return "clients.ranking.read";
  if (rota === "/dashboard/clients" || rota.startsWith("/dashboard/clients/")) return "clients.list.read";
  if (rota.startsWith("/dashboard/clients")) return "clients.read";
  /* Abrir o formulario de um orcamento novo e comecar a escrever um: quem so
     pode ler chegava ate o fim e levava 403 no salvar. Mesma regra de
     /dashboard/products/new. */
  if (rota === "/dashboard/quotes/new" || rota.startsWith("/dashboard/quotes/new/")) return "quotes.write";
  if (rota.startsWith("/dashboard/quotes")) return "quotes.read";
  if (rota.startsWith("/dashboard/orders")) return "orders.read";
  /* Notas fiscais: configurar é de dono e admin; a lista, de quem vê notas. */
  if (rota === "/dashboard/notas-fiscais/configuracao" || rota.startsWith("/dashboard/notas-fiscais/configuracao/")) return "fiscal.configurar";
  if (rota === "/dashboard/notas-fiscais/emitir" || rota.startsWith("/dashboard/notas-fiscais/emitir/")) return "fiscal.emitir";
  if (rota === "/dashboard/notas-fiscais/recebidas" || rota.startsWith("/dashboard/notas-fiscais/recebidas/")) return "fiscal.recebidas";
  if (rota === "/dashboard/notas-fiscais" || rota.startsWith("/dashboard/notas-fiscais/")) return "fiscal.ver";
  /* Produtos digitais, da mais especifica para a raiz. Cursos sao produtos:
     a mesma chave abre os dois. */
  if (rota === "/dashboard/digitais/produtos" || rota.startsWith("/dashboard/digitais/produtos/")) return "digital.products.read";
  if (rota === "/dashboard/digitais/cursos" || rota.startsWith("/dashboard/digitais/cursos/")) return "digital.products.read";
  /* A live também é um produto: a mesma chave. */
  if (rota === "/dashboard/digitais/lives" || rota.startsWith("/dashboard/digitais/lives/")) return "digital.products.read";
  if (rota === "/dashboard/digitais/alunos" || rota.startsWith("/dashboard/digitais/alunos/")) return "digital.students.read";
  if (rota === "/dashboard/digitais/comentarios" || rota.startsWith("/dashboard/digitais/comentarios/")) return "digital.comments.read";
  if (rota === "/dashboard/digitais/entregas" || rota.startsWith("/dashboard/digitais/entregas/")) return "digital.deliveries.read";
  if (rota === "/dashboard/digitais/arquivos" || rota.startsWith("/dashboard/digitais/arquivos/")) return "digital.files.read";
  if (rota === "/dashboard/digitais/configuracoes" || rota.startsWith("/dashboard/digitais/configuracoes/")) return "digital.settings.read";
  if (rota === "/dashboard/digitais") return "digital.overview.read";
  if (rota.startsWith("/dashboard/digitais/")) return "digital.read";
  if (rota.startsWith("/dashboard/pdv")) return "pdv.read";
  if (rota.startsWith("/dashboard/production/materials")) return "materials.read";
  /* As telas de production, da mais especifica para a raiz. */
  if (rota === "/dashboard/production/operations" || rota.startsWith("/dashboard/production/operations/")) return "production.costs.read";
  if (rota === "/dashboard/production" || rota.startsWith("/dashboard/production/")) return "production.board.read";
  if (rota.startsWith("/dashboard/production")) return "production.read";
  /* As telas de agenda, da mais especifica para a raiz. */
  if (rota === "/dashboard/agenda/relatorio" || rota.startsWith("/dashboard/agenda/relatorio/")) return "agenda.report.read";
  if (rota === "/dashboard/agenda" || rota.startsWith("/dashboard/agenda/")) return "agenda.board.read";
  if (rota.startsWith("/dashboard/agenda")) return "agenda.read";
  /* Mora fora de /dashboard porque abre em aba propria, mas e tela como as
     outras: quem nao pode ver conversa nao ve o item no menu. */
  if (rota === "/chats" || rota.startsWith("/chats/")) return "chats.read";
  return null;
}

/**
 * Telas que sao a casa de dois assuntos: basta UMA das chaves para entrar.
 *
 * Nao e o mesmo que heranca. Heranca e vertical — a chave da tela herda a do
 * modulo, e por isso quem tinha o modulo continua entrando. Isto aqui e
 * horizontal: duas abas irmas, cada uma com o seu dono, dentro da mesma tela.
 *
 * Fica curto de proposito. Toda entrada nova aqui e uma tela que faz duas
 * coisas, e quase sempre a resposta certa e separar a tela, nao a chave.
 */
const TAMBEM_ABREM: Partial<Record<CompanyPermission, CompanyPermission[]>> = {
  /* Estoque: a aba "Produtos prontos" e a aba "Insumos". */
  "products.stock.read": ["materials.read"],
  /* Contas a pagar: a aba "Lançamentos" e a aba "Custos fixos". */
  "financial.payables.read": ["financial.fixedcosts.read"],
  /* Integrações: as abas de frete e pagamento, e a aba "Assistentes e Apps".
     Quem recebeu só a permissão dos assistentes precisa chegar na aba dele. */
  "company.shipping.read": ["company.assistants.manage"],
};

/**
 * Todas as chaves que abrem a tela. Lista vazia = tela sem exigencia.
 *
 * `permissaoDaRota` continua respondendo a chave principal, que e o que o
 * catalogo e os testes usam para dizer "esta tela e desta chave".
 */
export function permissoesDaRota(path: string): CompanyPermission[] {
  const principal = permissaoDaRota(path);
  if (!principal) return [];
  return [principal, ...(TAMBEM_ABREM[principal] ?? [])];
}

export type EstadoChecagem = "carregando" | "pronto" | "indisponivel";

export type DecisaoAcesso = "liberado" | "aguardando" | "negado";

export function decidirAcessoDaRota(params: {
  checagem: EstadoChecagem;
  /* `null` ou lista vazia = rota sem exigencia declarada. Segue liberada, como
     sempre foi. Lista com mais de uma chave = basta uma delas. */
  permissaoExigida: CompanyPermission | CompanyPermission[] | null;
  papel: CompanyRole | null;
  ajustes: unknown;
}): DecisaoAcesso {
  const { checagem, permissaoExigida, papel, ajustes } = params;

  const exigidas =
    permissaoExigida === null ? [] : Array.isArray(permissaoExigida) ? permissaoExigida : [permissaoExigida];

  if (exigidas.length === 0) return "liberado";
  if (checagem === "indisponivel") return "liberado";
  if (checagem === "carregando" || papel === null) return "aguardando";

  return exigidas.some((chave) => canCompanyRole(papel, ajustes, chave)) ? "liberado" : "negado";
}
