/**
 * <AppShell> — sidebar + header, à volta de cada ecrã.
 *
 * A estrutura é a do bloco `app-shell-11` do Efferd (a "seller shell"), que é
 * o caso deste produto: uma loja escolhida em cima, a navegação em grupos, e o
 * plano no rodapé. Reescrita em JSX sem build — ver components/painel.jsx.
 *
 * Do Efferd vêm três coisas que mudam a leitura do ecrã:
 *
 *   - **A navegação em grupos com rótulo.** Oito links seguidos eram uma
 *     lista; em três grupos são um mapa do que a conta faz
 *   - **O cabeçalho fino com o rasto** em vez do título. O título grande
 *     repetia o que o ecrã já diz no seu próprio cabeçalho
 *   - **O conteúdo com largura máxima e as bordas a cruzar** (`--extended-border`)
 *
 * O que fica nosso: o filete laranja à esquerda marca a página activa em vez
 * de um pill arredondado — cantos rectos em toda a plataforma.
 */

/*
 * O menu do cliente, de 7 para 5 entradas (28 Set, avaliação de UX, mudança 5).
 * Sem grupos: com cinco entradas, os rótulos CAMPANHAS / REDE / MARCA eram mais
 * texto a ler do que mapa. Os Painéis ficam a um clique das Lojas (separadores
 * no topo dos dois ecrãs), e a Conta junta os utilizadores, o plano e o consumo.
 * `seccoes` diz em que rotas a entrada fica acesa; as rotas antigas continuam.
 *
 * Os Resultados só entram no menu quando há uma campanha publicada ou
 * terminada: antes disso eram uma página vazia (ver `mostrarResultados`).
 */
const NAV_CONTA = [
  { chave: 'dashboard',    rotulo: 'Início',          Icone: IconDashboard,    seccoes: ['dashboard'] },
  // O Criar no menu (4 Nov): era a acção principal e só se chegava lá por um botão dentro das Campanhas,
  // com «Campanhas» assinalado (utilizadores virgens). Só aparece com o Criar activo.
  { chave: 'criar',        rotulo: 'Criar vídeo',     Icone: IconMais,         seccoes: ['criar'], soComCriar: true },
  { chave: 'campanhas',    rotulo: 'Campanhas',       Icone: IconCampanhas,    seccoes: ['campanhas'] },
  { chave: 'resultados',   rotulo: 'Resultados',      Icone: IconResultados,   seccoes: ['resultados'], soComPublicadas: true },
  { chave: 'marca',        rotulo: 'A minha marca',   Icone: IconConhecimento, seccoes: ['marca', 'conhecimento', 'biblioteca'] },
  { chave: 'lojas',        rotulo: 'Lojas e painéis', Icone: IconLojas,        seccoes: ['lojas', 'paineis-pdv'] },
  { chave: 'configuracao', rotulo: 'Conta',           Icone: IconConfiguracao, seccoes: ['configuracao'] },
];

// O título do rasto por secção, quando não é o rótulo do menu.
// O Criar é uma forma de começar uma campanha: marca Campanhas no menu, e o rasto diz «Criar» e não
// «NetScreen AI» (agentes, 20 Out).
const TITULO_SECCAO = { 'paineis-pdv': 'Painéis e assistente', lojas: 'Lojas', criar: 'Criar vídeo' };

/**
 * O perfil por baixo do nome, com os nomes da lista de utilizadores e do convite. A barra dizia
 * «Dono da conta» e «Consulta» para o Responsável e a Leitura (agentes, 20 Out).
 */
const PERFIL_NA_BARRA = { owner: 'Responsável', editor: 'Editor', viewer: 'Leitura' };

/**
 * Se a conta já tem alguma campanha publicada ou terminada. Lê a lista uma vez
 * por conta; `null` enquanto não se sabe (e aí não se mostra).
 */
function useTemPublicadas(slug) {
  const [tem, setTem] = React.useState(null);
  React.useEffect(() => {
    if (!slug) { setTem(null); return undefined; }
    let vivo = true;
    setTem(null);
    api.get(`/api/${slug}/campanhas`)
      .then((r) => { if (vivo) setTem((r.campanhas || []).some((c) => ['ativa', 'encerrada'].includes(c.estado))); })
      .catch(() => { if (vivo) setTem(null); });
    return () => { vivo = false; };
  }, [slug]);
  return tem;
}

/** O quadrado com as iniciais da marca — o avatar da loja no `stores` do Efferd. */
function MarcaQuadrado({ nome }) {
  return (
    <span className="flex h-8 w-8 shrink-0 items-center justify-center border border-border bg-secondary font-display text-base leading-none tracking-wide text-foreground">
      {iniciais(nome || '?')}
    </span>
  );
}

/**
 * Back office. Só aparece a quem é da equipa NetScreen.
 *
 * O `id` é o segundo segmento de /admin/:id — a rota do admin não tem slug de
 * conta, por isso é o id que distingue os ecrãs.
 */
const NAV_NETSCREEN = [
  // O que está à espera da equipa, em todas as contas (lib/fila.js, 24 Set).
  { id: 'fila',      rotulo: 'A tratar', Icone: IconAlerta,    href: '/admin/fila' },
  { id: null,        rotulo: 'Rede',     Icone: IconAdmin,     href: '/admin' },
  { id: 'clientes',  rotulo: 'Clientes', Icone: IconLojas,     href: '/admin/clientes' },
  { id: 'entregas',  rotulo: 'Entregas', Icone: IconCheck,     href: '/admin/entregas' },
  { id: 'hardware',  rotulo: 'Painéis',  Icone: IconPaineis,   href: '/admin/hardware' },
  { id: 'planos',    rotulo: 'Planos',   Icone: IconConfiguracao, href: '/admin/planos' },
  { id: 'equipa',    rotulo: 'Equipa',   Icone: IconSocial,    href: '/admin/equipa' },
  { id: 'auditoria', rotulo: 'Auditoria', Icone: IconRelogio,  href: '/admin/auditoria' },
];

function NavLink({ href, activo, Icone, children, onNavigate }) {
  return (
    <a
      href={href}
      aria-current={activo ? 'page' : undefined}
      onClick={e => {
        e.preventDefault();
        onNavigate(href);
      }}
      className="nav-item flex items-center gap-3 px-4 py-2 text-sm text-muted-foreground transition-colors hover:bg-muted hover:text-foreground"
    >
      <Icone size={16} />
      <span>{children}</span>
    </a>
  );
}

/**
 * Entre que contas é que esta pessoa pode trocar.
 *
 * Só existe para as agências (L14 do spec): a agência trabalha nas contas dos
 * seus anunciantes, cada um com o seu brand kit e as suas campanhas. Para
 * todos os outros há uma conta só, e um selector com uma opção é ruído — nesse
 * caso mostra-se o nome e pronto.
 *
 * A lista vem do `/api/me` e não daqui. É o mesmo cálculo que o `requireSlug`
 * faz do lado do servidor, por isso o que está no selector é exactamente o que
 * a API aceita.
 *
 * Não é o "ver como" da equipa NetScreen, que é outra coisa: esse atravessa
 * clientes diferentes, pede confirmação e fica na auditoria. Aqui a pessoa
 * está sempre dentro do que já é seu.
 */
function SelectorConta({ contas, activo, onEscolher }) {
  const escolhida = contas.find((c) => c.slug === activo) || contas[0];
  const anunciantes = contas.filter((c) => !c.propria);

  if (contas.length <= 1) {
    return (
      <div className="flex items-center gap-3 border-b border-border px-4 py-3">
        <MarcaQuadrado nome={escolhida.nome} />
        <div className="min-w-0">
          <p className="truncate text-sm text-foreground">{escolhida.nome}</p>
          <p className="text-xs text-muted-foreground">{formatPlano(escolhida.plano)}</p>
        </div>
      </div>
    );
  }

  return (
    <div className="flex items-center gap-3 border-b border-border px-4 py-3">
      <MarcaQuadrado nome={escolhida.nome} />
      <div className="min-w-0 flex-1">
      {/* Quem troca de conta aqui é uma agência, e para ela são clientes (agentes, 20 Out). */}
      <label htmlFor="selector-conta" className="label-eyebrow block">
        {anunciantes.length > 0 ? 'Cliente' : 'Conta'}
      </label>
      {/* Um <select> nativo e não um menu próprio: é uma lista curta, e o
          nativo já traz teclado, leitor de ecrã e o comportamento certo no
          telemóvel. A seta é a do sistema. */}
      <select
        id="selector-conta"
        value={escolhida.slug}
        onChange={(e) => onEscolher(e.target.value)}
        className="mt-1 w-full cursor-pointer border-0 bg-transparent p-0 text-sm text-foreground outline-none focus-visible:underline"
      >
        {/* Em grupos e não com um sufixo no nome: um "— anunciante" colado ao
            nome ficava também no estado fechado, onde não diz nada de novo. */}
        {contas.filter((c) => c.propria).map((c) => (
          <option key={c.slug} value={c.slug} className="bg-background">{c.nome}</option>
        ))}
        {anunciantes.length > 0 && (
          <optgroup label="Clientes">
            {anunciantes.map((c) => (
              <option key={c.slug} value={c.slug} className="bg-background">{c.nome}</option>
            ))}
          </optgroup>
        )}
      </select>
      </div>
    </div>
  );
}

/**
 * De quem é a vez, no cabeçalho de todos os ecrãs da conta (lib/fila.js).
 *
 * O ensaio de ponta a ponta de 24 Set: com o briefing aberto, a resposta da
 * NetScreen não chegava a lado nenhum, porque os ecrãs não relêem enquanto se
 * escreve (para não apagar o que não se guardou). Isto só lê a fila e não mexe
 * em formulário nenhum, por isso lê também enquanto se escreve: de 30 em 30
 * segundos, com a janela à vista.
 */
const chaveDaNovidade = (n) => `${n.chave}|${n.desde}`;
function novidadesVistas(slug) {
  try { return JSON.parse(localStorage.getItem(`novidades-vistas:${slug}`) || '[]'); } catch { return []; }
}
function marcarNovidadeVista(slug, n) {
  try {
    const vistas = novidadesVistas(slug).concat(chaveDaNovidade(n)).slice(-50);
    localStorage.setItem(`novidades-vistas:${slug}`, JSON.stringify(vistas));
  } catch { /* sem armazenamento: volta a aparecer, e não faz mal */ }
}

function AvisoDaVez({ slug, onNavigate }) {
  // A fila guarda-se com o slug a que pertence: ao trocar de conta, o primeiro
  // desenho mostrava a fila da conta anterior (revisor, 24 Set).
  const [lido, setLido] = React.useState(null);
  const [vistas, setVistas] = React.useState(() => novidadesVistas(slug));
  React.useEffect(() => {
    let vivo = true;
    setVistas(novidadesVistas(slug));
    const ler = () => {
      if (document.visibilityState !== 'visible') return;
      api.get(`/api/${slug}/fila`).then((r) => { if (vivo) setLido({ slug, fila: r }); }).catch(() => {});
    };
    ler();
    const t = setInterval(ler, 30000);
    document.addEventListener('visibilitychange', ler);
    window.addEventListener('fila-mudou', ler);
    return () => { vivo = false; clearInterval(t); document.removeEventListener('visibilitychange', ler); window.removeEventListener('fila-mudou', ler); };
  }, [slug]);
  if (!lido || lido.slug !== slug) return null;
  const { fila } = lido;
  // A mais recente que ainda não se abriu: uma vista sai do cabeçalho (ronda 6:
  // ficava a antiga, e a nova nunca aparecia).
  const novidade = (fila.novidades || []).find((n) => !vistas.includes(chaveDaNovidade(n)));
  const tua = fila.cliente.length;
  if (!novidade && !tua) return null;
  return (
    <div className="ml-auto flex shrink-0 items-center gap-2 text-xs">
      {novidade && (
        <button
          type="button"
          onClick={() => {
            onNavigate(novidade.rota);
            if (window.location.pathname === novidade.rota) { marcarNovidadeVista(slug, novidade); setVistas(novidadesVistas(slug)); }
          }}
          className="border-l-2 border-primary px-2 py-1 text-foreground hover:bg-secondary"
          title={novidade.texto}
        >
          <span className="hidden sm:inline">{novidade.texto}</span>
          <span className="sm:hidden">A NetScreen respondeu</span>
        </button>
      )}
      {tua > 0 && (
        <button
          type="button"
          onClick={() => onNavigate(`/${slug}/dashboard`)}
          className="border border-border px-2 py-1 text-muted-foreground hover:text-foreground"
        >
          {tua} à tua espera
        </button>
      )}
    </div>
  );
}

function AppShell({ me, rota, onNavigate, children, verComo, onSairDeVerComo }) {
  const [menuAberto, setMenuAberto] = React.useState(false);
  // A ver como cliente: a navegação da conta passa a ser a do cliente, senão
  // os links levavam de volta à conta interna da NetScreen.
  const ehEquipa = me.user.tipo === 'netscreen';
  // As contas onde esta pessoa pode trabalhar, do `/api/me`. Vazio para a
  // equipa NetScreen: a conta que vê é a de quem está a impersonar.
  const contas = me.contas || [];

  /**
   * Qual é a conta que está à vista.
   *
   * A rota manda, e não a conta a que a pessoa pertence: uma agência que
   * escolha um anunciante fica em `/an-cliente-alfa/campanhas`, e os links da
   * sidebar têm de ir para lá e não de volta para a agência. O router já
   * garantiu que o slug é um dos acessíveis antes de chegar aqui.
   *
   * Quem é da equipa não pertence a conta nenhuma (`me.account` é null) e só
   * tem conta à vista quando está a ver como um cliente.
   */
  const slugDaRota = rota.tipo === 'conta' ? rota.slug : null;
  const slug = (verComo && verComo.slug) || slugDaRota || (me.account && me.account.slug) || null;
  const contaActiva = contas.find((c) => c.slug === slug);
  const nomeConta =
    (verComo && verComo.nome) || (contaActiva && contaActiva.nome) || (me.account && me.account.nome) || null;

  const seccaoActual = rota.seccao;
  // No back office o título vem do ecrã, não da secção: os três ecrãs vivem
  // todos em /admin e diziam "Admin NetScreen" indistintamente.
  const itemNetscreen =
    seccaoActual === 'admin'
      ? NAV_NETSCREEN.find(i => i.id === (rota.id || null))
      : null;
  const tituloPagina =
    TITULO_SECCAO[seccaoActual] ||
    (NAV_CONTA.find(i => i.chave === seccaoActual) || {}).rotulo ||
    (itemNetscreen ? `NetScreen · ${itemNetscreen.rotulo}` : null) ||
    // Os factos de uma conta não estão no menu: sem isto dizia "Admin NetScreen" (ronda 9).
    (seccaoActual === 'admin' && rota.id === 'dossie' ? 'Factos da marca' : null) ||
    (seccaoActual === 'admin' ? 'Admin NetScreen' : 'NetScreen AI');

  const navegar = href => {
    setMenuAberto(false);
    onNavigate(href);
  };

  const temPublicadas = useTemPublicadas(slug);
  const navConta = NAV_CONTA.filter((i) => !i.soComCriar || (me && me.criar_activo))
    .filter((i) => !i.soComPublicadas || temPublicadas || seccaoActual === i.chave)
    // Numa conta de cliente da agência, a marca não é «minha» (agentes, 20 Out).
    .map((i) => (i.chave === 'marca' && contaActiva && !contaActiva.propria ? { ...i, rotulo: 'A marca' } : i));

  const sidebar = (
    <nav className="flex h-full w-60 shrink-0 flex-col border-r border-border bg-background">
      <div className="flex h-16 items-center gap-3 border-b border-border px-4">
        <Logo size="sm" />
        <button
          type="button"
          className="-mr-2 ml-auto flex h-10 w-10 items-center justify-center text-muted-foreground hover:text-foreground lg:hidden"
          onClick={() => setMenuAberto(false)}
          aria-label="Fechar menu"
        >
          <IconFechar size={18} />
        </button>
      </div>

      {contas.length > 0 && !verComo && (
        <SelectorConta
          contas={contas}
          activo={slug}
          onEscolher={(novo) => navegar(`/${novo}/${seccaoActual}`)}
        />
      )}

      {/* A ver como cliente: não há selector nenhum, porque a conta não é
          escolha de quem está a ver — é onde a equipa entrou. */}
      {verComo && nomeConta && (
        <div className="flex items-center gap-3 border-b border-border px-4 py-3">
          <MarcaQuadrado nome={nomeConta} />
          <div className="min-w-0">
            <p className="label-eyebrow">A ver como</p>
            <p className="truncate text-sm text-foreground">{nomeConta}</p>
          </div>
        </div>
      )}

      <div className="flex-1 overflow-y-auto py-3">
        {slug && (
          <div className="mb-3 space-y-0.5">
            {navConta.map(item => (
              <NavLink
                key={item.chave}
                href={`/${slug}/${item.chave}`}
                activo={item.seccoes.includes(seccaoActual)}
                Icone={item.Icone}
                onNavigate={navegar}
              >
                {item.rotulo}
              </NavLink>
            ))}
          </div>
        )}

        {ehEquipa && (
          <div className={`${slug ? 'mt-1 border-t border-border pt-3' : ''}`}>
            <p className="label-eyebrow px-4 pb-1.5 pt-1">NetScreen</p>
            {NAV_NETSCREEN.map(item => (
              <NavLink
                key={item.href}
                href={item.href}
                activo={seccaoActual === 'admin' && (rota.id || null) === item.id}
                Icone={item.Icone}
                onNavigate={navegar}
              >
                {item.rotulo}
              </NavLink>
            ))}
          </div>
        )}
      </div>

      {/* O plano saiu do rodapé do menu para a Conta (28 Set), com o consumo
          de IA e o espaço: estava em três sítios e em nenhum era o seu. */}

      {/* Quem está, em baixo — o `app-user` do Efferd. Estava no cabeçalho,
          que passa a ser só o rasto. */}
      <div className="flex h-14 items-center gap-3 border-t border-border px-4">
        <span
          className="flex h-8 w-8 shrink-0 items-center justify-center bg-secondary text-xs tracking-wider text-foreground"
          title={me.user.email}
        >
          {iniciais(me.user.nome || me.user.email)}
        </span>
        <div className="min-w-0 flex-1">
          <p className="truncate text-sm leading-tight">{me.user.nome || me.user.email}</p>
          <p className="truncate text-xs leading-tight text-muted-foreground">{(!ehEquipa && PERFIL_NA_BARRA[me.user.role]) || formatRole(me.user.role)}</p>
        </div>
        <button
          type="button"
          onClick={() => api.post('/api/logout').then(() => window.location.assign('/'))}
          className="-mr-2 flex h-10 w-10 shrink-0 items-center justify-center text-muted-foreground hover:text-foreground"
          aria-label="Terminar sessão"
          title="Terminar sessão"
        >
          <IconSair size={16} />
        </button>
      </div>
    </nav>
  );

  return (
    <div className="flex h-screen overflow-hidden">
      {/* Sidebar fixa em ecrãs grandes */}
      <div className="hidden lg:block">{sidebar}</div>

      {/* Sidebar em overlay no mobile */}
      {menuAberto && (
        <div className="fixed inset-0 z-40 flex lg:hidden">
          <div
            className="absolute inset-0 bg-black/70"
            onClick={() => setMenuAberto(false)}
            aria-hidden="true"
          />
          <div className="relative z-10">{sidebar}</div>
        </div>
      )}

      <div className="flex min-w-0 flex-1 flex-col">
        <header className="flex h-16 shrink-0 items-center gap-3 border-b border-border px-4 lg:px-6">
          {/* 40 px de alvo e não os 20 do ícone: no telemóvel falhava-se o toque (agentes, 20 Out). */}
          <button
            type="button"
            className="-ml-2 flex h-10 w-10 shrink-0 items-center justify-center text-muted-foreground hover:text-foreground lg:hidden"
            onClick={() => setMenuAberto(true)}
            aria-label="Abrir menu"
          >
            <IconMenu />
          </button>

          {/* O rasto e não o título: cada ecrã já tem o seu cabeçalho, e o
              título grande aqui em cima dizia duas vezes a mesma coisa. */}
          <nav aria-label="Onde estás" className="flex min-w-0 items-center gap-2 text-sm">
            {(seccaoActual === 'admin' ? 'Back office' : nomeConta) && (
              <>
                <span className="truncate text-muted-foreground">
                  {seccaoActual === 'admin' ? 'Back office' : nomeConta}
                </span>
                <span className="text-muted-foreground" aria-hidden="true">/</span>
              </>
            )}
            <span className="truncate text-foreground" aria-current="page">
              {itemNetscreen ? itemNetscreen.rotulo : tituloPagina}
            </span>
          </nav>
          {/* O sino (9 Out): no lugar do «N à tua espera», com a lista do que espera e das novidades. */}
          {slug && seccaoActual !== 'admin' && <Sino slug={slug} onNavigate={navegar} />}
          {seccaoActual === 'admin' && <Sino equipa onNavigate={navegar} />}
        </header>

        {/* Barra de impersonação. Fica sempre visível enquanto durar: o risco
            desta funcionalidade não é entrar, é esquecer que se entrou. */}
        {verComo && (
          <div className="flex flex-wrap items-center gap-3 border-b-2 border-accent bg-[hsl(var(--accent)/0.12)] px-4 py-2.5 lg:px-6">
            <p className="min-w-0 flex-1 text-sm text-foreground">
              {/* No back office a faixa dizia «Estás na área do cliente» (agentes, 20 Out). */}
              {seccaoActual === 'admin' ? (
                <span className="text-muted-foreground">Ainda tens aberta a área de {verComo.nome}. Fecha-a quando acabares.</span>
              ) : (
                <>
                  <span className="font-medium">A ver como {verComo.nome}.</span>{' '}
                  <span className="text-muted-foreground">
                    Estás na área do cliente, não na tua. A entrada e o que mudares ficam registados.
                  </span>
                </>
              )}
            </p>
            <button
              type="button"
              onClick={onSairDeVerComo}
              className="shrink-0 border border-accent px-3 py-1.5 text-xs uppercase tracking-wider text-accent outline-none hover:bg-accent hover:text-[hsl(var(--accent-foreground))] focus-visible:ring-1 focus-visible:ring-accent"
            >
              {seccaoActual === 'admin' ? 'Fechar a área do cliente' : 'Voltar ao back office'}
            </button>
          </div>
        )}

        {/* `--extended-border` é o quanto as bordas dos painéis passam dos
            cantos (components/painel.jsx). O overflow-x fica escondido para
            essas pontas nunca abrirem uma barra horizontal no telemóvel. */}
        <main className="flex-1 overflow-y-auto overflow-x-hidden p-4 [--extended-border:1.5rem] lg:p-8">
          <div className="mx-auto w-full max-w-[72rem]">{children}</div>
        </main>
      </div>
    </div>
  );
}
