import type { Role } from "@/lib/auth/types";
import { PAPEL_MINIMO_DA_CENTRAL } from "@/lib/ai/inbox-destino";
import type { ModuloOpcional } from "@/lib/instalacao/modulos";
import type { CapacidadeDaOrganizacao } from "@/lib/organizacao/capacidades";

/**
 * Registro de navegação — a ÚNICA lista de destinos do app do tenant.
 *
 * Antes disto, três listas descreviam o mesmo conjunto e divergiam: `NAV_ITEMS`
 * no Sidebar, `LINKS` no hub de Configurações e `TABS` na área de IA. Sete telas
 * só eram alcançáveis por dentro da própria seção e uma não tinha link nenhum.
 *
 * Sidebar, hubs e a paleta ⌘K são PROJEÇÕES puras deste array — nenhum deles
 * decide o que existe, só desenha o que sai daqui. Tela nova aparece nos três
 * sem editar três arquivos, e `tests/unit/navegacao-completude.test.ts` reprova
 * o CI se uma rota nascer fora daqui.
 *
 * Doutrina: docs/doctrine/sistema-vivo.md — "por qual porta se chega até mim?"
 */

export type NavGroupId = "atendimento" | "crm" | "ia" | "canais" | "analise" | "organizacao";

export interface NavGroup {
  id: NavGroupId;
  label: string;
  /**
   * Hub do grupo, quando ele tem telas demais para caber no sidebar.
   * O rótulo é declarado junto do href porque não é derivável: "Ver tudo em IA"
   * é útil, "Ver tudo em Organização" seria gratuito quando a tela já se chama
   * Configurações e o usuário a conhece por esse nome.
   */
  hub?: { href: string; label: string };
}

export interface NavMetadata {
  href: string;
  label: string;
  /** Aparece no card do hub e é texto buscável no ⌘K. Nunca vazio. */
  description: string;
  icon: string;
  group: NavGroupId;
  /** Obrigatória em grupo com hub — é o agrupamento por jornada dentro dele. */
  section?: string;
  /** Ausente = viewer. Ver a regra de escolha abaixo. */
  minRole?: Role;
  /** Ausente = só no hub. `true` = uso diário, sobe para o sidebar. */
  sidebar?: boolean;
  healthDot?: boolean;
  /**
   * Contador de pendências ao lado do rótulo. `casos`: casos em que a IA espera
   * uma pessoa (`awaiting_human`) — quem desenha é `ContadorDeCasos`. `fila`:
   * conversas que esperam uma pessoa (a aba Fila do Inbox) — `ContadorDaFila`.
   * `tarefas`: tarefas em aberto do PRÓPRIO usuário (`pending` + `in_progress`,
   * responsável = ele) — `ContadorDeTarefas`.
   * Zero não desenha nada: número que nunca some ensina a ignorar o número.
   */
  contador?: "casos" | "fila" | "tarefas";
  /**
   * A porta de um MÓDULO OPCIONAL da instalação (`lib/instalacao/modulos.ts`).
   * Com o módulo desligado ela some do menu, do hub e do ⌘K — para todo papel.
   * É apresentação, como o resto deste arquivo: quem recusa é a tela e a rota.
   */
  modulo?: ModuloOpcional;
  /**
   * A porta de uma CAPACIDADE que a organização liga para si
   * (`lib/organizacao/capacidades.ts`). Desligada, some do menu, do hub e do
   * ⌘K. Apresentação, como `modulo`: quem recusa é a tela e a rota.
   */
  capacidade?: CapacidadeDaOrganizacao;
}

/**
 * Grupos por OBJETIVO, na ordem de uso: o que se abre toda hora primeiro, o que
 * se ajusta uma vez por mês por último.
 *
 * "Análise" e não "Observabilidade": quem instala isto numa VPS é dono de PME,
 * não engenheiro. E configurar o sistema (grupo IA) é atividade diferente de
 * observar o sistema funcionando (grupo Análise) — por isso Evolução da IA mora
 * aqui, e não junto dos agentes.
 *
 * Hub só onde o grupo passa de 4 telas. Abaixo disso ele cabe inteiro no
 * sidebar, e um hub de 3 itens seria só um clique a mais para chegar onde já
 * dava para chegar.
 *
 * O CRM cruzou essa linha com a tela de Tarefas (PR #546), e o hub dele é a
 * cobrança de uma promessa escrita: o comentário de densidade do `Sidebar.tsx`
 * dizia, desde a vez em que Produtos estourou a dobra por uma linha, que
 * "quando o quinto destino de CRM aparecer, é hub que se cria, não mais 4px que
 * se raspa". Tarefas foi o quinto. Raspar de novo devolveria 13px e adiaria a
 * mesma conversa para a sexta tela.
 */
export const NAV_GROUPS: NavGroup[] = [
  { id: "atendimento", label: "Atendimento" },
  { id: "crm", label: "CRM", hub: { href: "/app/crm", label: "Ver tudo em CRM" } },
  { id: "ia", label: "Agente de IA", hub: { href: "/app/ai", label: "Ver tudo em IA" } },
  { id: "canais", label: "Canais" },
  { id: "analise", label: "Análise", hub: { href: "/app/analise", label: "Ver tudo em Análise" } },
  {
    id: "organizacao",
    label: "Organização",
    hub: { href: "/app/settings", label: "Configurações" },
  },
];

/**
 * Grupo cujo hub vive no RODAPÉ fixo do sidebar, fora da área que rola.
 *
 * Medido em tela (1280×768, o notebook comum): com todos os grupos na área
 * rolável, o conteúdo dava 1019px contra 663px visíveis — Configurações ficava
 * fora da dobra em TODAS as alturas testadas, inclusive 1080px. É o item que
 * mais se procura quando não se acha algo; deixá-lo dependendo de scroll
 * recriaria, em outra forma, o problema que esta reorganização veio resolver.
 */
export const GRUPO_NO_RODAPE: NavGroupId = "organizacao";

/**
 * A PORTA SOBE AO MENU LATERAL? — a regra, num lugar só.
 *
 * `sidebarGroups` (`lib/navigation/registry.ts`) e o texto de "onde o módulo aparece"
 * (`lib/navigation/onde-o-modulo-aparece.ts`) precisam concordar: se o primeiro desenha a porta no
 * menu e o segundo anuncia o passo do hub, o produto mente para quem liga o módulo. A condição
 * estava ESCRITA NOS DOIS — copiada —, e cópia de regra diverge sem avisar.
 *
 * `temEscolhaExplicita` é "a empresa escreveu lista de áreas à mão". Ele só importa para grupo SEM
 * hub: ali não há "Ver tudo" para hospedar a porta, então ela sobe ao menu quando a empresa
 * personalizou. Com hub, quem decide é só o `sidebar` do destino.
 */
export function sobeAoMenuLateral(
  d: Pick<NavMetadata, "sidebar" | "group">,
  temEscolhaExplicita: boolean,
): boolean {
  const grupo = NAV_GROUPS.find((g) => g.id === d.group);
  return !!d.sidebar || (!grupo?.hub && temEscolhaExplicita);
}

/**
 * Como `minRole` foi escolhido — medido tela a tela, não estimado:
 *
 *   1. A página redireciona por papel?  → usa esse papel. Assim a navegação
 *      nunca mostra um link que morre em /403.
 *   2. Não redireciona, mas a navegação antiga já filtrava? → mantém o filtro
 *      antigo, para esta mudança reorganizar sem alterar quem vê o quê.
 *   3. Nenhum dos dois → viewer.
 *
 * `ROLE_RANK` só distingue papel dentro do tenant; capacidade interna da tela
 * (`canShare` em Respostas rápidas, `canCompare` em Desempenho) NÃO é porta
 * fechada e por isso não vira `minRole`.
 */
export const NAV_CATALOG = [
  {
    // SEM `sidebar: true`, e a razão não tem nada a ver com a qualidade desta
    // tela: o menu lateral está no limite medido. Com ela, seriam 20 portas, e
    // `tests/e2e/navegacao.spec.ts` reprova ("em 900px o menu inteiro tem de
    // caber sem scroll" · Received: true). O comentário daquela spec já
    // antecipava o número: "Trocar '17 itens sem hierarquia' por '20 itens que
    // não cabem' seria recriar o problema em outra forma."
    //
    // A porta NÃO sumiu: ela vive no hub do grupo CRM ("Ver tudo em CRM") e no
    // ⌘K — o mesmo caminho das outras entradas do grupo.
    // CONDIÇÃO QUE ENCERRA ESTA EXCEÇÃO: quando o menu couber mais uma porta
    // (ver doc 47), este item volta ao sidebar — é o primeiro da fila, porque
    // saiu por falta de espaço e não por decisão de produto.
    href: "/app/prospecting",
    label: "Prospecção",
    description: "Busque empresas e conduza abordagens graduais com IA.",
    icon: "Funnel",
    group: "crm",
    minRole: "admin",
    section: "O dia a dia da venda",
  },
  // ---- Atendimento — onde o operador passa o dia ----
  {
    href: "/app/inbox",
    label: "Inbox",
    description: "As conversas de WhatsApp, com você e a IA atendendo lado a lado.",
    icon: "Inbox",
    group: "atendimento",
    sidebar: true,
    // Quantas conversas a IA passou para a equipe e esperam uma pessoa (aba Fila).
    contador: "fila",
  },
  {
    href: "/app/radar",
    label: "Radar",
    description: "Quem esfriou e ainda está aberto — o que corre risco de morrer sem resposta.",
    icon: "ClockCountdown",
    group: "atendimento",
    sidebar: true,
  },
  {
    // Entra em "atendimento", e não em "organizacao", porque a Agenda é onde o
    // dia acontece e não onde ele se configura: quem atende abre isto de manhã
    // junto com o Inbox. Os TIPOS de agendamento — que são configuração de
    // verdade — foram para Configurações, como este comentário previa: ver
    // `/app/settings/tenant/agenda` no grupo "organizacao".
    //
    // ⚠️ ESTA FRASE ESTAVA VENCIDA: dizia "a disponibilidade ainda não tem tela",
    // e tem — é a aba "Atendimento" de `/app/team`, com editor de fuso e janelas
    // (`app/app/team/_components/AttendantsClient.tsx`). Ela chegou a custar uma
    // investigação inteira: quem leu isto aqui concluiu que faltava construir a
    // tela, quando o que faltava era o CAMINHO até ela. O aviso da Agenda agora
    // aponta para `/app/team?aba=atendimento`.
    href: "/app/agenda",
    label: "Agenda",
    description: "O que está marcado, com quem, e quem atende — seu e da equipe.",
    icon: "CalendarBlank",
    group: "atendimento",
    sidebar: true,
  },
  {
    // Módulo opcional (ADR-0002, doc 208): a porta só existe depois que o
    // administrador da instalação instala `lista_de_espera` em `/admin/modulos`.
    href: "/app/agenda/lista-de-espera",
    label: "Lista de espera",
    description: "Quem aceitou ser avisado quando abrir um horário, na ordem da fila.",
    icon: "CalendarBlank",
    group: "crm",
    section: "O dia a dia da venda",
    minRole: "viewer",
    modulo: "lista_de_espera",
  },
  {
    // Renomeado de "Templates": estes são scripts do atendente, consumidos pelo
    // Composer do inbox. O nome "Templates" fica livre para os da Meta (HSM),
    // onde é o termo técnico correto.
    href: "/app/templates",
    label: "Respostas rápidas",
    description: "Scripts salvos para responder mais rápido, seus ou da equipe.",
    icon: "FileText",
    group: "atendimento",
    sidebar: true,
  },

  // ---- CRM — o funil ----
  {
    // ⚠️ ERA "Kanban", e a URL continua sendo. O nome saiu da interface porque o
    // produto tinha CINCO vocabulários para a mesma coisa — "Kanban" no menu,
    // "Pipelines" no título desta tela, "Funis" no menu ao lado, "funil" em todo
    // o corpo dela e "quadro" no onboarding inteiro. Três deles no mesmo
    // viewport: o <h1> dizia "Pipelines", o estado vazio dizia "Sem pipelines
    // configurados" e o botão embaixo dizia "Criar meu primeiro funil".
    //
    // Ficou "Funis" porque é o que esta tela É: a lista dos funis, de onde se
    // abre o quadro de cada um. "Pipeline" é palavra de quem construiu o
    // sistema; "funil de vendas" é palavra de quem vende.
    href: "/app/kanban",
    label: "Funis",
    description: "Seus funis de venda — clique em um para abrir o quadro de clientes.",
    icon: "Kanban",
    group: "crm",
    section: "O dia a dia da venda",
    sidebar: true,
  },
  {
    // A campanha vive no CRM e não em Conexões: quem a usa está pensando em
    // QUEM vai falar, não no número que fala. O ritmo (que é de Conexões) ela
    // herda, e só sabe deixar mais devagar.
    href: "/app/campaigns",
    label: "Campanhas",
    description: "Fale com uma lista de contatos que você escolhe, no ritmo do número.",
    icon: "Megaphone",
    group: "crm",
    section: "O dia a dia da venda",
    // SÓ NO HUB, como as demais telas de preparação: o quinto item do sidebar do
    // CRM já fez o menu rolar 13px em 900px (e2e `navegacao.spec.ts`), e a
    // campanha é montada de vez em quando, não aberta todo dia.
  },
  {
    href: "/app/contacts",
    label: "Contatos",
    description: "As pessoas do outro lado da conversa e seu histórico.",
    icon: "Users",
    group: "crm",
    section: "O dia a dia da venda",
    sidebar: true,
  },
  {
    href: "/app/companies",
    label: "Empresas",
    description: "Cadastro B2B — razão social, CNPJ e decisores.",
    icon: "Buildings",
    group: "crm",
    section: "O dia a dia da venda",
    // SEM sidebar: o CRM já tem Funis/Contatos/Tarefas no menu diário; este trio
    // mora no hub "Ver tudo em CRM" para não reabrir a corrida por pixel.
    minRole: "viewer",
    // Módulo opcional da instalação, desligado por padrão (doc 68, #1621).
    modulo: "crm_b2b",
  },
  {
    href: "/app/people",
    label: "Pessoas",
    description: "Decisores e contatos ligados a empresas, com vários telefones.",
    icon: "UserCircle",
    group: "crm",
    section: "O dia a dia da venda",
    minRole: "viewer",
    // Módulo opcional da instalação, desligado por padrão (doc 68, #1621).
    modulo: "crm_b2b",
  },
  {
    // Extraída do PR #418 (@clinicacentrodosorrisosc-code). Fica no CRM e no
    // sidebar porque é tela de USO DIÁRIO — quem atende abre para ver o que
    // vence hoje, do mesmo jeito que abre o Inbox. Sem `minRole`: `viewer` VÊ
    // o que o time combinou (é informação de operação), e a criação é cobrada
    // pela rota, com `requireRole("agent")`.
    href: "/app/tasks",
    label: "Tarefas",
    description: "O que ficou combinado, com prazo — e o que já venceu sem ninguém fazer.",
    icon: "ListChecks",
    group: "crm",
    section: "O dia a dia da venda",
    sidebar: true,
    // As tarefas em aberto DE QUEM ESTÁ LOGADO. Automação, follow-up e aviso de
    // carteira criam tarefa para cada atendente; sem número no menu, a tarefa
    // atrasada só existia para quem abrisse a tela por conta própria (#3938).
    // Gerente e admin contam só as suas — a visão da equipe é a tela.
    contador: "tarefas",
  },
  {
    // A porta dos planos de tarefa (#1752): montar a sequência UMA vez e o
    // `apply_task_plan` aplicá-la a cada negócio. Fica no MESMO hub de Tarefas,
    // mas SEM `sidebar` pelo critério já usado em Empresas/Pessoas: é
    // CADASTRO, não uso diário — quem monta um plano abre esta tela poucas
    // vezes, e o menu de 900px (`tests/e2e/navegacao.spec.ts`) não ganha mais
    // um pixel por isso.
    href: "/app/tasks/planos",
    label: "Planos de tarefa",
    description: "Sequências reutilizáveis de tarefas — montar uma vez e aplicar a cada negócio.",
    icon: "FlowArrow",
    group: "crm",
    section: "O dia a dia da venda",
  },
  {
    // Módulo VoIP (migration 0347). No grupo do CRM pelo mesmo critério de
    // Tarefas: quem atende confere ligações perdidas e transcrições no dia a
    // dia, não como revisão deliberada.
    //
    // ─── MÓDULO OPCIONAL: só aparece com a telefonia ligada PARA A ORGANIZAÇÃO ─
    //
    // O dono decidiu (doc 27) que a telefonia por SIP entra desligada por
    // padrão — quem não liga não tem os contêineres, não tem tronco e não tem
    // ligação nenhuma para ver.
    //
    // Até a #1290 este item aparecia para todo mundo e levava a uma tela vazia:
    // o menu oferecia uma porta que não funcionava. Hoje ele declara
    // `capacidade: "telefonia"` e o filtro de `permitidos()` o esconde de quem
    // não tem o módulo em uso. A decisão sai de `capacidades_ligadas`, a MESMA
    // lista que já governa Propostas — não há uma segunda régua para a
    // telefonia: `lib/telefonia/em-uso.ts` responde "esta organização salvou o
    // tronco?", e `capacidadesLigadas()` soma essa resposta à lista.
    //
    // Por que o sinal é o tronco, e não o profile do compose: "telefonia
    // ligada" na INSTALAÇÃO é `COMPOSE_PROFILES` no servidor, que o aplicativo
    // não lê (`lib/recursos-opcionais/catalogo.ts`, `telefonia_sip`:
    // `ler: () => "nao_verificado"`). O tronco é o primeiro estado que a
    // organização produz e que o app enxerga; sem ele não há chamada possível.
    // Efeito colateral aceito e declarado: instalação com o profile de pé e
    // nenhum tronco salvo não mostra o item — a tela, vazia, não faria falta, e
    // o tronco se cadastra em `/app/settings/voip-trunk`, não aqui.
    //
    // SEGUE FORA DO SIDEBAR, e isso é independente do filtro: o menu de 900px
    // (`tests/e2e/navegacao.spec.ts:222`) cabe com 19 itens e rola com o
    // vigésimo. A porta vive no hub do grupo CRM ("Ver tudo em CRM") e no ⌘K —
    // os dois passam pela mesma projeção, então o item some dos dois quando a
    // telefonia está desligada, e a navegação direta por URL continua
    // respondendo como sempre (esconder do menu nunca foi autorização).
    //
    // ADMINISTRADOR DA INSTALAÇÃO não tem exceção aqui, e isso também é medição,
    // não escolha nova: `permitidos()` aplica `modulo`/`capacidade` DEPOIS de
    // `canSee()`, então platform admin já não vê porta de capacidade desligada
    // hoje (ver `tests/unit/navegacao-registry.test.ts`). Bypass só existe para
    // PAPEL (`canSee`), que é outra pergunta.
    href: "/app/calls",
    label: "Chamadas",
    description: "Histórico de ligações (voz por IA) com transcrição.",
    icon: "Phone",
    group: "crm",
    section: "O dia a dia da venda",
    minRole: "manager",
    capacidade: "telefonia",
  },
  {
    // Fila de ligações sobre um funil próprio. A régua está em lib/cold-call/regras.ts.
    href: "/app/cold-call",
    label: "Cold Call",
    description: "Ligue para uma lista de leads, um por vez, com roteiro e resultado de cada ligação.",
    icon: "Phone",
    // No CRM, e não em Atendimento: porta de módulo mora em grupo com hub ("Ver tudo em CRM"),
    // que é o que dá a ela um caminho sem depender de quem personalizou o menu.
    group: "crm",
    section: "O dia a dia da venda",
    minRole: "agent",
    // Módulo opcional da instalação, desligado por padrão (#3933).
    modulo: "cold_call",
  },
  {
    // ⚠️ Esta tela nasceu porque a FERRAMENTA já existia sem ela. O agente de IA
    // vinha com "procurar produto na loja" ligada por padrão, lendo uma tabela
    // que ninguém nunca preencheu — e o efeito não era silêncio: era o agente
    // respondendo "não tenho nada com esse nome" para uma loja de estoque cheio.
    //
    // Fica no grupo do CRM, e não em Configurações, porque o catálogo é insumo
    // de VENDA: ele existe para o agente responder preço na conversa.
    //
    // ⚠️ ESTA FRASE DIZIA "consultar preço é trabalho de quem ATENDE, todo dia",
    // e era o argumento para o `sidebar: true`. Ela se contradizia com a própria
    // descrição do destino, uma linha abaixo: quem responde o preço é o
    // atendente de IA, dentro do Inbox. O humano não abre esta tela para
    // vender — abre para cadastrar o que vende.
    href: "/app/products",
    label: "Produtos",
    description: "O catálogo da loja, com o preço que o atendente de IA responde.",
    icon: "Storefront",
    group: "crm",
    section: "Preparar a venda",
    // SEM `sidebar`: mora atrás de "Ver tudo em CRM".
    //
    // O critério é QUEM CONSOME a tela, e a descrição acima já o entrega: o
    // preço quem responde é o atendente de IA, dentro da conversa. Esta tela é
    // onde o catálogo se CADASTRA — trabalho de quando entra produto novo ou
    // muda preço, não de toda manhã. Quem atende não a abre para vender; abre o
    // Inbox e o funil, que continuam no menu.
  },
  {
    href: "/app/imports",
    label: "Importações",
    description: "Lotes CSV/XLSX de empresas, pessoas e telefones.",
    icon: "FileText",
    group: "crm",
    section: "Preparar a venda",
    minRole: "manager",
    // Módulo opcional da instalação, desligado por padrão (doc 68, #1621).
    modulo: "crm_b2b",
  },
  {
    href: "/app/proposals",
    label: "Propostas",
    description: "Rascunhe, revise e envie propostas comerciais — do orçamento ao aceite.",
    icon: "FileText",
    group: "crm",
    section: "Fechar a venda",
    capacidade: "propostas",
  },
  {
    // A promessa que o comentário da Agenda fazia desde que ela nasceu. Aqui se
    // decide O QUE se pode marcar, quanto dura e quem atende — e é isto que a
    // tela de marcar e o agente de IA oferecem ao cliente.
    //
    // Nasceu porque a `calendar_event_types` tinha dez categorias no CHECK,
    // duração, buffers e antecedência mínima, e NÃO havia como criar ou editar
    // um tipo por lugar nenhum: a organização recebia três semeados e ficava com
    // eles para sempre.
    href: "/app/settings/tenant/agenda",
    label: "Tipos de agendamento",
    description: "O que se pode marcar, quanto dura, onde acontece e quem atende.",
    icon: "CalendarBlank",
    group: "organizacao",
    // "Sua empresa", junto de Atendimento e Empresa: é configuração do NEGÓCIO,
    // não da conta de quem está logado. O gate `navegacao-registry` cobra a
    // seção em todo grupo que tem hub, e sem ela o destino não aparece no hub.
    section: "Sua empresa",
    // SEM `sidebar`, como as outras DEZ entradas de "organizacao": este grupo
    // tem hub, e se chega às telas dele por "Configurações". Eu tinha posto
    // `sidebar: true` e a cerca reprovou dizendo "a tela existe e não tem porta
    // na navegação" — a porta existia, era outra.
  },
  {
    // O aviso de agendamento no WhatsApp do dono (migration 0753). Fica junto
    // dos tipos de agendamento porque é configuração da agenda do NEGÓCIO, não
    // do dia de quem atende. "Aviso de agendamento", nunca "Aviso no WhatsApp":
    // esse já é o nome do aviso de CASO, e dois destinos com o mesmo rótulo na
    // busca não dizem qual é qual.
    href: "/app/agenda/avisos",
    label: "Aviso de agendamento",
    description: "Receber no WhatsApp cada horário marcado, remarcado ou cancelado, e o resumo das 7h.",
    icon: "PaperPlaneTilt",
    group: "organizacao",
    section: "Sua empresa",
    // `admin`: escolhe um número conectado e manda nome de cliente para um
    // celular — o mesmo gate da rota e da RLS de `config_aviso_de_agendamento`.
    minRole: "admin",
  },
  {
    href: "/app/settings/tenant/proposals",
    label: "Propostas",
    description: "Configure a validade padrão e condições para propostas comerciais.",
    icon: "FileText",
    group: "organizacao",
    section: "Sua empresa",
    minRole: "manager",
    // Doc 79: é aqui que a EMPRESA liga — então a porta depende só da chave da
    // INSTALAÇÃO (a capacidade da empresa ainda está desligada quando se chega).
    modulo: "propostas",
    // SEM `sidebar`: mora atrás de "Configurações", como as demais telas de
    // settings/tenant que não são de acompanhamento diário.
  },
  {
    href: "/app/settings/tenant/proposals/modelos",
    label: "Modelos de proposta",
    description: "Personalize os modelos da plataforma ou crie os da sua empresa, inclusive a partir de uma proposta que você já usa.",
    icon: "FileText",
    group: "organizacao",
    section: "Sua empresa",
    minRole: "manager",
    capacidade: "propostas",
    // SEM `sidebar`, como a tela-mãe de Propostas: chega-se por Configurações.
  },
  {
    // O BALCÃO. Fica em CRM, e não em Configurações, porque é uso diário de quem
    // está com a cliente na frente — a tela irmã, em Configurações › Financeiro,
    // é onde o negócio se descreve uma vez.
    //
    // `viewer` porque conferir o que foi lançado no dia não é privilégio de
    // quem lança; o que a RLS impede é ele escrever.
    href: "/app/comandas",
    label: "Comandas",
    description: "O que foi feito, por quem, e quanto o cliente paga.",
    icon: "Receipt",
    group: "crm",
    section: "O dia a dia da venda",
    // ⚠️ FORA do sidebar, e isto foi MEDIDO, não escolhido por gosto. Com
    // `sidebar: true` o menu passou a rolar em 1280×900 e o e2e
    // `navegacao.spec.ts` ("nenhum grupo fica fora da dobra") reprovou — grupo
    // abaixo da dobra é indistinguível de grupo que não existe. A regra escrita
    // no comentário de densidade do `Sidebar.tsx` é esta: grupo COM hub não
    // ganha linha nova no menu, a tela mora dentro do hub. O CRM tem hub desde
    // Tarefas (PR #546), e raspar 4px de densidade de novo só adiaria a mesma
    // conversa para a próxima tela.
    //
    // O balcão continua a um clique: CRM › Ver tudo em CRM › "O dia a dia da
    // venda", e pelo ⌘K digitando "comanda".
    minRole: "viewer",
    // Módulo de tabela (ADR-0002, #1907): as cinco tabelas da comanda nascem na
    // instalação do `financeiro` em `/admin/modulos`; sem ele a porta some, como
    // `/app/honorarios`. O caixa (Configurações › Financeiro) é núcleo e fica.
    modulo: "financeiro",
  },
  {
    // O catálogo financeiro: contas, formas de pagamento e plano de contas.
    //
    // Fica em "Sua empresa" pelo mesmo motivo dos tipos de agendamento — é onde
    // o negócio se DESCREVE, não onde o dia acontece. E vem ANTES de qualquer
    // tela de venda porque a forma de pagamento é quem decide em que conta a
    // entrada cai quando uma comanda é fechada: sem esta camada, a comanda não
    // tem onde depositar.
    href: "/app/settings/tenant/financeiro",
    label: "Financeiro",
    description: "Contas, formas de pagamento e como cada lançamento é classificado.",
    icon: "ChartBar",
    group: "organizacao",
    section: "Sua empresa",
    // Leitura para a organização, escrita para manager+ (é a RLS que decide).
    // `viewer` aqui e não `manager`: quem só olha precisa conferir para onde o
    // dinheiro vai, e esconder a tela não esconde o dado — só torna a
    // conferência impossível.
    minRole: "viewer",
  },
  {
    // Estava enterrado em Configurações e ninguém sabia que existia — o achado
    // que originou esta reorganização. A URL não muda; só o lugar na navegação.
    //
    // ⚠️ ERA "Funis", nome que ele DISPUTAVA com o destino acima: os dois
    // listavam as mesmas linhas de `crm_pipelines`, lado a lado no mesmo grupo,
    // com nomes que não diziam qual servia para quê. A diferença real é o VERBO,
    // e é ela que o nome carrega agora: lá se ABRE o funil, aqui se CONFIGURA o
    // que ele significa.
    href: "/app/settings/tenant/pipelines",
    label: "Etapas do funil",
    description: "As colunas de cada funil, o vocabulário do negócio e os motivos de perda.",
    icon: "Funnel",
    group: "crm",
    section: "Preparar a venda",
    minRole: "manager",
    // SEM `sidebar`: mora atrás de "Ver tudo em CRM".
    //
    // ⚠️ O ACHADO ORIGINAL NÃO FOI DESFEITO. Ele era "esta tela está enterrada
    // em CONFIGURAÇÕES e ninguém sabe que existe" — o problema era o GRUPO
    // errado, não a profundidade. Ela continua sendo CRM: aparece no hub do
    // CRM, no ⌘K, e o caminho é "CRM › Ver tudo em CRM", nunca mais
    // "Configurações". O que muda é a frequência: desenhar as colunas do funil
    // e escrever os motivos de perda é trabalho de montagem, feito uma vez e
    // revisitado por `manager` de vez em quando — enquanto Funis, Contatos e
    // Tarefas se abrem todo dia. É esse o corte que decide quem fica no menu.
  },

  // ---- Agente de IA — montar, ensinar, acompanhar ----
  {
    href: "/app/ai/agents",
    label: "Agentes",
    description: "Quem atende por você: instruções, modelo, ferramentas e publicação.",
    icon: "Robot",
    group: "ia",
    section: "Montar o agente",
    minRole: "manager",
    sidebar: true,
  },
  {
    href: "/app/ai/followups",
    label: "Follow-ups",
    description: "Como o agente retoma uma conversa que esfriou, para nenhuma morrer no silêncio.",
    icon: "FlowArrow",
    group: "ia",
    section: "Montar o agente",
    minRole: "manager",
    sidebar: true,
  },
  {
    // Os roteiros de atendimento (#1130, de @vgamkt): perguntas que a IA conduz
    // durante a conversa. MÓDULO OPCIONAL da instalação, desligado por padrão
    // (doc 64): a porta só existe onde quem administra o servidor o ligou.
    //
    // SEM `sidebar`, pela decisão (d) do doc 48: o menu lateral encheu e ficou
    // configurável por empresa — o padrão não cresce; a porta mora no hub de IA
    // e na busca, e quem usa pode pô-la no menu dela.
    href: "/app/ai/atendimento",
    label: "Fluxos de atendimento",
    description: "Perguntas que a IA conduz durante a conversa, com as respostas guardadas na ficha do cliente.",
    icon: "ListChecks",
    group: "ia",
    section: "Montar o agente",
    minRole: "manager",
    modulo: "fluxos_atendimento",
  },
  {
    href: "/app/ai/routers",
    label: "Roteadores",
    description: "Qual agente pega qual conversa, e quando o humano assume.",
    icon: "Signpost",
    group: "ia",
    section: "Montar o agente",
    minRole: "manager",
    // Saiu do sidebar para dar lugar a Casos: roteador se configura poucas
    // vezes, e o caso pede resposta no mesmo dia. A troca é um-por-um porque o
    // menu inteiro tem de caber em 900px (e2e `navegacao.spec.ts`).
  },
  {
    href: "/app/ai/credentials",
    label: "Credenciais",
    description: "A chave do provedor de IA que os agentes usam para pensar.",
    icon: "Key",
    // VOLTOU para "ia"/"Montar o agente" na triagem, e a razão de quem tinha
    // movido VENCEU em vez de estar errada: quando este PR nasceu, a tela não
    // aparecia em hub nenhum e só se chegava nela digitando a URL. Depois
    // disso a main ganhou o hub de IA, que a lista — e `tests/unit/nav-hub`
    // cobra a entrada ali, inclusive em espanhol. Movê-la para "Sua empresa"
    // agora tiraria a tela do lugar onde o hub promete que ela está.
    group: "ia",
    section: "Montar o agente",
    minRole: "manager",
  },
  {
    // O sistema chama modelo em 23 lugares e, até esta tela, a escolha vivia
    // espalhada por três pilhas de código e sete variáveis de ambiente — não
    // havia onde responder "quem usa IA aqui, e com qual chave?".
    href: "/app/ai/providers",
    label: "Provedores",
    // O "Jev" vem cedo: o ⌘K mostra só o começo da descrição, e a versão
    // longa cortava antes do nome — quem procurava "jev" achava, mas não via por quê.
    description: "Ligue o Jev para decisões rápidas e escolha qual inteligência atende cada parte do sistema.",
    icon: "Plugs",
    group: "ia",
    section: "Montar o agente",
    minRole: "manager",
    // SEM `sidebar: true`, como as outras nove telas deste grupo. Adicionar as
    // duas telas novas à sidebar estourou a dobra em 900px — medido pelo e2e
    // `navegacao.spec.ts`, que existe justamente porque agrupar o menu o faz
    // crescer. Configurar provedor é tarefa de poucas vezes; o caminho é o hub
    // "Ver tudo em IA", igual a Credenciais, Conhecimento, Memória e Skills.
  },
  {
    href: "/app/ai/knowledge/sources",
    label: "Conhecimento",
    description: "Os materiais que o agente consulta antes de responder sobre o seu negócio.",
    icon: "BookOpen",
    group: "ia",
    section: "Ensinar o agente",
    minRole: "manager",
  },
  {
    href: "/app/ai/memory",
    label: "Memória",
    description: "O que o agente já aprendeu sobre a sua operação e reaproveita.",
    icon: "Brain",
    group: "ia",
    section: "Ensinar o agente",
    minRole: "manager",
  },
  {
    href: "/app/ai/skills",
    label: "Skills",
    description: "As ações que o agente pode executar sozinho durante o atendimento.",
    icon: "PuzzlePiece",
    group: "ia",
    section: "Ensinar o agente",
    minRole: "manager",
  },
  {
    href: "/app/ai/cases",
    label: "Casos",
    description: "Os atendimentos que o agente conduziu, do início ao desfecho.",
    icon: "ClipboardText",
    group: "ia",
    section: "Acompanhar o agente",
    minRole: "agent",
    // Uso diário: a IA abre caso quando precisa de uma pessoa e segue
    // atendendo enquanto espera. Escondido no hub, o caso só era visto por quem
    // fosse procurar — medido numa loja que vende pelo WhatsApp.
    sidebar: true,
    contador: "casos",
  },
  {
    href: "/app/ai/inbox",
    label: "Alertas",
    description: "O que a IA encontrou e precisa de uma decisão sua.",
    icon: "Flag",
    group: "ia",
    section: "Acompanhar o agente",
    // ⚠️ ESTA ENTRADA NÃO TINHA PISO, E POR ISSO OFERECIA UMA PORTA FECHADA.
    //
    // Ela não decide só o item do menu: é DELA que
    // `components/shell/AlertsBell.tsx` e `AvisoDePropostaEmDestaque.tsx` leem
    // para se mostrar — os dois perguntam se `/app/ai/inbox` está entre os
    // destinos da interface. Sem `minRole`, a resposta era "sim" para o
    // `viewer`, e o sino aparecia para quem não alcança a Central. O contador,
    // então, ficava em ZERO PARA SEMPRE: `useAgentInbox` só consulta quando o
    // papel alcança a tela (`ai.inbox.view` = `agent`, em
    // `hooks/auth/AuthProvider.tsx`), e as rotas de `/api/v1/ai/inbox` exigem
    // `PAPEL_MINIMO_DA_CENTRAL` — recusam o `viewer`. Nada quebrava e nada
    // avisava: a pessoa via um sino que nunca ia contar nada.
    //
    // A régua é a CONSTANTE, nunca o literal "agent": ela é o mesmo piso que
    // `requireRole(PAPEL_MINIMO_DA_CENTRAL, …)` aplica nas três rotas da
    // Central e o mesmo que `lib/notifications/push-dos-avisos.ts` usa para o
    // push. Com o literal aqui, subir (ou descer) o piso da Central reabriria a
    // porta em silêncio, para todo mundo, sem nenhum teste ficando vermelho.
    // Quem guarda isso é `components/shell/sino-so-para-quem-alcanca-a-central.test.tsx`.
    minRole: PAPEL_MINIMO_DA_CENTRAL,
  },
  {
    // "Aviso no WhatsApp", NUNCA "Avisos": a vizinha de cima chama-se "Alertas"
    // e É a central de avisos — todo o vocabulário interno dela é "aviso"
    // (POLITICAS_DE_AVISO, REFERENCIAS_DE_AVISO). Duas entradas com a mesma
    // palavra, na mesma seção, é a tela ficando ilegível para quem não
    // programa. O rótulo nomeia o CANAL e o destinatário.
    href: "/app/ai/cases/avisos",
    label: "Aviso no WhatsApp",
    description: "Receber no WhatsApp quando o assistente abrir um caso.",
    icon: "PaperPlaneTilt",
    group: "ia",
    section: "Acompanhar o agente",
    // `admin` porque escolhe um número conectado e manda dado de cliente para um
    // celular — o mesmo gate da rota e da RLS de `config_aviso_de_caso`.
    minRole: "admin",
    // SEM `sidebar`: o grupo IA já tem treze telas e o e2e de navegação exige
    // que o menu inteiro caiba em 900px de altura. Configurar isto é tarefa de
    // poucas vezes; o caminho é o hub "Ver tudo em IA".
  },
  {
    // Órfã: nenhum lugar do app linkava para cá. O flywheel gerava propostas de
    // melhoria do agente e a fila só era vista por quem soubesse a URL.
    href: "/app/ai/proposals",
    label: "Propostas",
    description: "Melhorias que a IA sugere para si mesma, esperando sua decisão.",
    icon: "Lightbulb",
    group: "ia",
    section: "Acompanhar o agente",
  },
  {
    // A tela de Uso responde "quanto gastei". Esta responde a pergunta que não
    // tinha lugar nenhum: "o agente parou de responder, o que aconteceu?".
    // Antes da migration 0128 ela seria impossível de construir com honestidade
    // — llm_calls só registrava sucesso.
    href: "/app/ai/runs",
    label: "Execuções",
    description: "O que a IA fez — e, quando falhou, o que aconteceu e o que fazer.",
    icon: "ListChecks",
    group: "ia",
    section: "Acompanhar o agente",
    minRole: "manager",
    // Idem: fora da sidebar para o menu não passar da dobra. Quem vem para cá
    // está diagnosticando, e chega pelo hub ou pelo link do aviso na Central.
  },
  {
    href: "/app/ai/usage",
    label: "Uso e orçamento",
    description: "Quanto a IA consumiu e qual é o teto de gasto do mês.",
    icon: "Gauge",
    group: "ia",
    section: "Acompanhar o agente",
    minRole: "manager",
  },

  // ---- Canais — por onde as mensagens entram e saem ----
  {
    href: "/app/connections",
    label: "Conexões",
    // Cobre os DOIS caminhos desde o PR #105: número por QR e canal oficial da
    // Meta (com os templates dele), cada um numa aba. A descrição cita "oficial"
    // e "Meta" de propósito — é por esses nomes que se procura no ⌘K, e a busca
    // varre a descrição além do rótulo.
    description:
      "Seus números de WhatsApp: por QR ou canal oficial da Meta, com saúde, reconexão e templates.",
    icon: "PlugsConnected",
    group: "canais",
    minRole: "admin",
    sidebar: true,
    healthDot: true,
  },
  {
    // Não tinha link nenhum no app inteiro: só se chegava digitando a URL.
    href: "/app/integrations/nuvemshop",
    label: "Nuvemshop",
    description: "Conecte a loja para trazer pedidos e clientes para dentro do CRM.",
    icon: "Storefront",
    group: "canais",
    // A página não filtra por papel, mas as Server Actions de conectar e
    // desconectar exigem admin — mostrar a um viewer seria oferecer botão morto.
    minRole: "admin",
    // SEM `sidebar`: fora do menu lateral por decisão do dono do produto — a
    // integração não é usada nesta instalação e ocupava uma linha de "Canais"
    // toda vez que alguém abria o app.
    //
    // Continua sendo DESTINO, e é por isso que a linha some em vez do bloco
    // inteiro: `searchable()` (abaixo) filtra só por papel, então a tela segue
    // no ⌘K; a rota, a página e as Server Actions ficam intactas; e
    // `tests/unit/navegacao-completude.test.ts` continua vendo uma porta para
    // `/app/integrations/nuvemshop` — apagar a entrada exigiria justificá-la na
    // allowlist de "rota sem porta", que é coisa de rota morta, e esta não está.
    //
    // ⚠️ O grupo "canais" não tem hub, então o ⌘K passa a ser a ÚNICA porta
    // navegável. Para voltar a mostrá-la, basta devolver `sidebar: true`.
  },
  {
    href: "/app/webhooks",
    label: "Webhooks",
    description: "Avise outros sistemas quando algo acontecer aqui dentro.",
    icon: "WebhooksLogo",
    group: "canais",
    minRole: "manager",
    sidebar: true,
  },

  // ---- Análise — olhar o sistema funcionando ----
  //
  // ── QUEM FICA NO MENU, E POR QUÊ ─────────────────────────────────────────
  //
  // A régua é a FREQUÊNCIA de quem opera vendas por WhatsApp, não a importância
  // da tela. As três de cima entram na rotina — o dono abre Desempenho para
  // saber como vai o mês, Meta Ads para saber quanto custou trazer quem chegou,
  // e Atividades para saber se a equipe (e a IA) trabalhou no período. São
  // perguntas que se refazem toda semana, e um menu é para o que se refaz.
  //
  // As duas de baixo são visita DELIBERADA: "Evolução da IA" é revisão do
  // agente, coisa de quando se senta para ensiná-lo — e quem senta para isso já
  // vai ao grupo de IA; "Audit Log" é forense, aberto quando algo deu errado e
  // se precisa saber quem mexeu. Nenhuma das duas se abre de passagem, e é
  // justamente disso que o hub é feito: quem vai lá vai de propósito.
  //
  // Sair do menu não é sair do produto — o hub `/app/analise` é INVENTÁRIO e
  // lista as cinco (`hubSections`), então as duas continuam a um clique, com a
  // frase que explica para que servem. O ⌘K também as acha por nome.
  {
    // A terceira ponta do módulo financeiro: Configurações › Financeiro descreve
    // para onde o dinheiro vai, CRM › Comandas é onde o dia acontece, e aqui se
    // responde a pergunta do fim do mês.
    //
    // Fora do sidebar de propósito: é consulta periódica, não uso diário, e o
    // hub de Análise é onde ela se encontra sem disputar pixel com o que se abre
    // toda hora.
    href: "/app/faturamento",
    label: "Faturamento",
    description: "Quanto entrou, de que forma, e quanto cada pessoa tem a receber.",
    icon: "ChartBar",
    group: "analise",
    section: "Dinheiro",
    minRole: "viewer",
  },
  {
    // Módulo opcional (ADR-0002), fonte `modulos_instalados` — a porta só existe
    // depois que o administrador da instalação instala `honorarios` em
    // `/admin/modulos`. Mesmo mecanismo genérico de `banco_externo` acima, só
    // que a fonte é a TABELA, não a flag de `platform_config`; `deModuloDesligado`
    // e este filtro não sabem a diferença, e não precisam saber.
    href: "/app/cadencias",
    label: "Cadências de e-mail",
    description: "Sequências de e-mail por negócio, com descadastro.",
    icon: "PaperPlaneTilt",
    group: "crm",
    section: "O dia a dia da venda",
    minRole: "manager",
    modulo: "cadencias",
    capacidade: "cadencias",
  },
  {
    href: "/app/settings/cadencias",
    label: "Configurar cadências",
    description: "Ativar as cadências de e-mail nesta empresa.",
    icon: "PaperPlaneTilt",
    group: "organizacao",
    section: "Sua empresa",
    minRole: "admin",
    modulo: "cadencias",
  },
  {
    href: "/app/honorarios",
    label: "Honorários",
    description: "O modelo de cobrança de cada caso e o calendário de parcelas.",
    icon: "ChartBar",
    group: "analise",
    section: "Dinheiro",
    minRole: "viewer",
    modulo: "honorarios",
  },
  {
    href: "/app/metrics",
    label: "Desempenho",
    description: "Funil e performance por atendente nos últimos 30 dias.",
    icon: "ChartBar",
    group: "analise",
    section: "Os números do período",
    sidebar: true,
  },
  {
    // ── A porta do ALVO (#3025, fatia 1) ────────────────────────────────────
    //
    // Mora aqui, ao lado de Desempenho, porque é a metade que faltava da MESMA
    // tela: lá se lê o progresso contra a meta, e sem esta porta o alvo não
    // teria onde ser cadastrado — o painel novo diria "nenhuma meta" para
    // sempre. O `group` é `organizacao` (é Configurações, como as vizinhas de
    // /app/settings), e é ele — não a posição nesta lista — que a coloca no
    // hub certo.
    //
    // SEM `sidebar`: definir o alvo é decisão de uma vez por mês, não uso
    // diário. O painel de Desempenho é a porta que se usa todo dia, e é ele
    // que linka para cá quando não há meta.
    href: "/app/settings/metas",
    label: "Meta de vendas",
    description: "Quanto a empresa quer vender no mês, para o Desempenho medir o progresso.",
    // `Gauge` (já exportado por lib/ui/icons.ts) e não um gráfico novo: aqui se
    // AJUSTA o medidor, não se lê o resultado — os gráficos ficam para as telas
    // de leitura, ao lado.
    icon: "Gauge",
    group: "organizacao",
    section: "Sua empresa",
    // `manager`, a MESMA régua da action: o alvo é o número pelo qual a
    // empresa cobra o próprio time, e quem o define não é quem o persegue.
    minRole: "manager",
  },
  {
    // Logo abaixo de Desempenho porque responde a metade da MESMA pergunta: lá
    // está o que aconteceu depois que a pessoa chegou; aqui, quanto custou
    // trazê-la. Ler as duas juntas é o que fecha a conta do custo por cliente.
    href: "/app/ads/meta",
    label: "Meta Ads",
    description: "Quanto custou cada resultado das campanhas que trazem gente para cá.",
    icon: "Megaphone",
    group: "analise",
    section: "Os números do período",
    // `manager`, e não o `viewer` de Desempenho: aqui não há recorte por
    // pessoa — orçamento e criativo são da empresa inteira. Mesmo grau dos
    // outros dois vizinhos do grupo.
    minRole: "manager",
    sidebar: true,
  },
  {
    // Irmã de "Desempenho", não a mesma coisa: lá é DESFECHO (funil agora,
    // ganho/perdido por atendente); aqui é o TRABALHO que aconteceu no
    // período, com quem fez cada coisa. Um mês inteiro atendido pela IA e um
    // mês inteiro atendido pela equipe têm o mesmo desfecho e histórias
    // opostas — só esta tela distingue as duas.
    href: "/app/activities",
    label: "Atividades",
    description:
      "Relatório do que a equipe e os agentes fizeram no período: quanto, quem e de que tipo.",
    icon: "ClockCounterClockwise",
    group: "analise",
    section: "Os números do período",
    sidebar: true,
  },
  {
    // Em Análise, e não em Atendimento: porta de módulo mora em grupo com hub
    // ("Ver tudo em Análise"), como a do Cold Call.
    href: "/app/painel",
    label: "Painel de atendimento",
    description: "Quem espera resposta, quem está em atendimento e quem foi atendido hoje, por número.",
    icon: "ListChecks",
    group: "analise",
    section: "Os números do período",
    // Módulo opcional da instalação, desligado por padrão (#3403).
    modulo: "painel_atendimento",
  },
  {
    // Observabilidade, não configuração: por isso não fica junto dos agentes.
    href: "/app/ai/evolution",
    label: "Evolução da IA",
    description: "Se o agente está melhorando, onde ele erra e o que falta ensinar.",
    icon: "ChartLineUp",
    group: "analise",
    section: "O histórico que se consulta",
    minRole: "manager",
  },
  {
    href: "/app/audit",
    label: "Audit Log",
    description: "Quem fez o quê, quando — o histórico que não se apaga.",
    icon: "ClockCounterClockwise",
    group: "analise",
    section: "O histórico que se consulta",
    minRole: "manager",
  },

  // ---- Organização — conta, empresa, acesso ----
  {
    href: "/app/settings/profile",
    label: "Perfil",
    description: "Seu nome, idioma, fuso horário e avatar.",
    icon: "UserCircle",
    group: "organizacao",
    section: "Sua conta",
  },
  {
    href: "/app/settings/security",
    label: "Segurança",
    description: "Verificação em duas etapas, códigos de recuperação e sessões.",
    icon: "ShieldCheck",
    group: "organizacao",
    section: "Sua conta",
  },
  {
    href: "/app/settings/notifications",
    label: "Notificações",
    description: "Por onde e sobre o quê você quer ser avisado.",
    icon: "Bell",
    group: "organizacao",
    section: "Sua conta",
  },
  {
    href: "/app/settings/aplicativo",
    label: "App no celular",
    description: "Instale o sistema na tela de início do celular e receba os avisos.",
    icon: "DeviceMobile",
    group: "organizacao",
    section: "Sua conta",
  },
  {
    href: "/app/team",
    label: "Equipe",
    description: "Quem trabalha aqui, com qual papel e quanta conversa cada um aguenta.",
    icon: "UsersThree",
    group: "organizacao",
    section: "Sua empresa",
  },
  {
    // A porta que faltava (issue #144): rodízio de atendimento e restrição de
    // visibilidade existiam inteiros no backend e não tinham NENHUMA tela — só
    // dava para ligar com UPDATE à mão no banco.
    href: "/app/settings/atendimento",
    label: "Distribuição de atendimento",
    description: "Quem recebe cada cliente novo, e o que cada atendente enxerga.",
    icon: "UsersThree",
    group: "organizacao",
    section: "Sua empresa",
    minRole: "manager",
  },
  {
    // A porta do freio POR EMPRESA do passo `ai_decide` (issue #2367). O #2228
    // criou o passo e o deixou opcional POR REGRA; sem esta tela, desligar de
    // uma vez todos os `ai_decide` de uma organização só existiria como
    // `UPDATE` à mão no `organizations.settings` — o anti-exemplo de "toda
    // configuração tem superfície" (docs/doctrine/restricao-de-canal.md).
    href: "/app/settings/automacoes",
    label: "Automações",
    description: "O freio único do passo em que a IA escolhe entre as opções de uma regra, para a empresa inteira.",
    icon: "FlowArrow",
    group: "organizacao",
    section: "Sua empresa",
    minRole: "manager",
  },
  {
    // A porta que faltava para o vocabulário de etiquetas (issue #852). Até
    // aqui a etiqueta só ENTRAva no vocabulário — cada agente escrevia a que
    // quisesse em `add_tag` — e não havia por onde corrigir, juntar as duas
    // grafias que a operação criou, nem tirar a que ninguém mais usa. O
    // vocabulário dava para LER (`/api/v1/conversation-tags`) e não para
    // AJUSTAR, então a única saída era digitar errado para sempre.
    //
    // `manager` e não `admin`, pelo mesmo critério da vizinha acima: quem
    // escreve a etiqueta é quem monta a regra do agente, e a tela existe para
    // quem monta a regra. Nada aqui apaga conversa ou muda dinheiro — o
    // alcance da operação é ao lado do de "Distribuição de atendimento".
    href: "/app/settings/tags",
    label: "Tags",
    description:
      "O vocabulário de etiquetas da empresa: onde cada uma é usada e como renomear, juntar ou excluir.",
    icon: "Tag",
    group: "organizacao",
    section: "Sua empresa",
    minRole: "manager",
  },
  {
    // A área única dos recursos opcionais (pedido do mantenedor, doc 73/80):
    // tudo o que a empresa pode ligar, com o estado e o caminho até a tela onde
    // se liga. Só leitura — quem liga continua sendo a tela do assunto.
    href: "/app/settings/recursos",
    label: "Recursos opcionais",
    description: "Tudo o que se liga e desliga, se está ligado e onde se ajusta.",
    icon: "ListChecks",
    group: "organizacao",
    section: "Sua empresa",
    // `manager`: o gerente decide metade das chaves da lista. Atendente e
    // leitor não ajustam nenhuma, e a tela redireciona os dois para /403.
    minRole: "manager",
  },
  {
    href: "/app/settings/tenant",
    label: "Organização",
    description: "Dados da empresa, retenção de dados e encarregado de LGPD.",
    icon: "Buildings",
    group: "organizacao",
    section: "Sua empresa",
    minRole: "admin",
  },
  {
    // Mora em Organização e não em Canais de propósito: o que se configura aqui
    // é a CONTA DE ANÚNCIOS da empresa — dinheiro e identidade comercial, ao lado
    // de billing e API tokens. Canais é por onde se FALA com o cliente, e os dois
    // eixos são independentes (dá para receber lead de anúncio num número servido
    // por qualquer transporte). Ver `lib/plataformas-de-anuncio/types.ts`.
    href: "/app/settings/conversoes",
    label: "Conversões",
    description:
      "Devolver ao anúncio as vendas que ele trouxe, e marcar a origem de quem chega pelo site.",
    icon: "ChartLineUp",
    group: "organizacao",
    section: "Sua empresa",
    // `admin` pelo mesmo critério das vizinhas: o token grava na conta de
    // anúncios da empresa, e quem o troca decide para onde vai o dinheiro de
    // mídia. Um `manager` ficaria acima de billing na mesma prancheta.
    minRole: "admin",
  },
  {
    // Vizinha de Conversões, e SEPARADA dela de propósito. As duas conectam "a
    // Meta" e a tentação de fundi-las é real — mas são credenciais de escopos
    // diferentes, em tabelas diferentes (0214), com consequências opostas
    // quando vencem: o token de leitura vencido deixa uma tela vazia, o de
    // conversões vencido faz a empresa parar de reportar vendas sem sintoma.
    // Uma tela só, com dois campos de token parecidos, é como se cola o token
    // errado no campo errado e se perde uma semana achando que quebrou.
    href: "/app/settings/meta-ads",
    label: "Meta Ads",
    description: "Conectar a conta de anúncios para ler o desempenho das campanhas.",
    icon: "Megaphone",
    group: "organizacao",
    section: "Sua empresa",
    // `admin` pelo mesmo critério da vizinha, mesmo o token sendo só de
    // leitura: ele expõe orçamento e performance da conta inteira, e quem
    // apenas LÊ a tela (`manager`) não precisa poder trocar a credencial.
    minRole: "admin",
  },
  {
    href: "/app/settings/marca",
    label: "Marca",
    description: "O nome e a cor que sua empresa mostra dentro do sistema.",
    icon: "Palette",
    group: "organizacao",
    section: "Sua empresa",
    // `admin` pelo mesmo motivo da linha de cima: o que se edita ali é
    // identidade da empresa, e dá-lo a `manager` o colocaria abaixo de billing e
    // de API tokens na mesma prancheta.
    minRole: "admin",
    // SEM `sidebar`: fica só no hub. Trocar a marca é tarefa de uma vez, e
    // agrupar o menu já o fez crescer — duas telas a mais estouraram a dobra em
    // 900px, medido pelo e2e `navegacao.spec.ts`.
  },
  {
    href: "/app/settings/billing",
    // Spec da cobrança §9: as mensagens de limite e a Central mandam para
    // "Configurações › Plano e cobrança" — o item precisa ter esse nome.
    label: "Plano e cobrança",
    description: "Pagamento, troca de plano e faturas da sua empresa.",
    icon: "Receipt",
    group: "organizacao",
    section: "Sua empresa",
    minRole: "admin",
  },
  {
    href: "/app/lgpd/requests",
    label: "LGPD",
    description: "Pedidos de exportação e exclusão de dados feitos por clientes.",
    icon: "ScalesSimple",
    group: "organizacao",
    section: "Dados e acesso",
    minRole: "admin",
  },
  {
    // A tela de gestão da Ouvidoria (#3611, fatia 3). `admin` porque é a régua
    // da leitura: a RLS da 9040 exige `fn_role_at_least(…, 'admin')` e a tela
    // barra em `lib/ouvidoria/acesso.ts`. Oferecer a porta a `manager` criaria
    // um item de menu que responde "não pode" ao clicar. Módulo de tabela
    // (ADR-0002): a porta só existe depois que quem administra a instalação
    // instala `ouvidoria` em `/admin/modulos`.
    href: "/app/ouvidoria",
    label: "Ouvidoria",
    description: "Reclamações, denúncias e elogios recebidos pela empresa, com protocolo.",
    icon: "Flag",
    group: "organizacao",
    section: "Dados e acesso",
    minRole: "admin",
    modulo: "ouvidoria",
  },
  {
    href: "/app/settings/api-tokens",
    label: "API Tokens",
    description: "Chaves para outro sistema conversar com o seu CRM.",
    icon: "Lock",
    group: "organizacao",
    section: "Dados e acesso",
    minRole: "admin",
  },
  {
    href: "/app/settings/voip-trunk",
    label: "Trunk SIP",
    description: "Credenciais do provedor SIP para chamadas de voz por IA.",
    icon: "Phone",
    group: "organizacao",
    section: "Dados e acesso",
    minRole: "admin",
  },
  {
    // A porta do cadastro do servidor MCP externo (#2948). Em "Dados e acesso"
    // pela MESMA razão de API Tokens e Dados externos: é o eixo "por onde dado
    // entra e sai do CRM" — o endereço e a chave são de um sistema de fora que
    // a IA da empresa consulta. Antes desta linha, a action que grava
    // (`definirServidorMcpExterno`) existia e não era chamada de lugar nenhum:
    // registrar o servidor só era possível por fora da interface.
    //
    // SEM `sidebar`, como as duas vizinhas: trocar a credencial de um ERP é
    // tarefa de uma vez, e o hub + o ⌘K já são porta suficiente.
    //
    // ⚠️ `minRole: "admin"` é o mais perto que este registro chega do que a
    // tela cobra: quem ESCREVE é o dono da INSTALAÇÃO (`is_platform_admin`, a
    // `regraPlatformAdmin` da action), e o catálogo não modela esse papel — a
    // mesma limitação que a allowlist de `/app/settings/atualizacao` já
    // registra em `tests/unit/navegacao-completude.test.ts`. Quem clicar sem a
    // permissão recebe `/403` da própria tela; a porta existe porque a tela
    // nova precisa de por onde ser encontrada (item 14 da Definition of Done).
    href: "/app/settings/servidor-mcp-externo",
    label: "Servidor MCP externo",
    description: "O endereço e a chave do sistema de fora que a IA da empresa pode consultar.",
    icon: "Plugs",
    group: "organizacao",
    section: "Dados e acesso",
    minRole: "admin",
  },
  {
    href: "/app/extensions",
    label: "Extensões",
    description:
      "Guias instalados para orientar o trabalho no CRM, com permissões e estado visíveis.",
    icon: "PuzzlePiece",
    group: "organizacao",
    section: "Sua empresa",
  },
  {
    // A porta da fonte de dados externa (migration 0372). Fica em "Dados e
    // acesso" porque é o MESMO eixo de API Tokens: por onde dado entra e sai do
    // CRM. NÃO é `admin` como as vizinhas de propósito — a decisão do dono (D2)
    // é que QUALQUER autenticado vê a lista e consulta os dados; só CRIAR e
    // editar conexão é `admin`. Gatear a leitura em `manager` esconderia do
    // atendente exatamente a fonte que responde o que o cliente pergunta.
    href: "/app/integracao-dados",
    label: "Dados externos",
    description:
      "Conecte um banco de dados de outro sistema para o agente consultar em tempo real.",
    icon: "PlugsConnected",
    group: "organizacao",
    section: "Dados e acesso",
    // SEM `sidebar`: o menu de Organização já estourou a dobra uma vez e hub é
    // onde se agrupa por uso. Configurar fonte de dados é tarefa de uma vez.
    //
    // Módulo opcional da instalação, desligado por padrão (doc 37): a porta só
    // existe onde quem administra o servidor o ligou, em `/admin/sistema`.
    modulo: "banco_externo",
  },
  {
    // A porta da ajuda dentro do produto (issue #3567, fatia F1). Fica por
    // ÚLTIMO, e isso é a decisão e não a sobra: é o que se procura quando o
    // resto não respondeu.
    //
    // `sidebar: true` porque o grupo `organizacao` é o RODAPÉ FIXO do menu: o
    // `Sidebar` não desenha os itens deste grupo como lista (ele os hospeda no
    // hub de Configurações) — o rodapé mostra o hub e o `VersionFooter`. É o
    // `Sidebar` que lê este item pelo `href` para pôr "Ajuda" embaixo de
    // Configurações, sem uma segunda lista para manter em sincronia.
    //
    // `section` própria porque as três vizinhas do grupo são "Sua conta",
    // "Dados e acesso" e "Sua empresa" — nenhuma delas é ajuda, e chamar de
    // configuração o que é socorro seria mentir na única tela em que a pessoa
    // já está perdida.
    href: "/app/ajuda",
    label: "Ajuda",
    description: "O caminho para cada tarefa, e onde ir quando algo não sai como esperado.",
    icon: "BookOpen",
    group: "organizacao",
    section: "Ajuda",
    sidebar: true,
  },
] as const satisfies readonly NavMetadata[];

export type NavDestinationId = (typeof NAV_CATALOG)[number]["href"];
